1#[cfg(debug_assertions)]
3use std::collections::HashSet;
4use std::{
5 self, fmt,
6 future::Future,
7 marker::PhantomData,
8 pin::{pin, Pin},
9};
10
11use discard::DiscardOnDrop;
12use futures::StreamExt;
13use futures_signals::{
14 cancelable_future,
15 signal::{Signal, SignalExt},
16 signal_vec::{always, SignalVec, SignalVecExt},
17 CancelableFutureHandle,
18};
19use include_doc::function_body;
20use silkenweb_base::document;
21use silkenweb_signals_ext::value::{Executor, RefSignalOrValue, SignalOrValue, Value};
22use wasm_bindgen::{JsCast, JsValue};
23
24use self::child_vec::{ChildVec, ParentUnique};
25use super::{ChildNode, Node, Resource};
26use crate::{
27 attribute::Attribute,
28 clone,
29 dom::{
30 private::{DomElement, DomText, EventStore, InstantiableDomElement},
31 DefaultDom, Dom, Hydro, InDom, InstantiableDom, Template, Wet,
32 },
33 empty_str,
34 hydration::HydrationStats,
35 intern_str,
36 node::text,
37 task,
38};
39
40pub(crate) mod child_vec;
41
42pub struct GenericElement<D: Dom = DefaultDom, Mutability = Mut> {
49 static_child_count: usize,
50 child_vec: Option<Pin<Box<dyn SignalVec<Item = Node<D>>>>>,
51 resources: Vec<Resource>,
52 events: EventStore,
53 element: D::Element,
54 #[cfg(debug_assertions)]
55 attributes: HashSet<String>,
56 phantom: PhantomData<Mutability>,
57}
58
59impl<D: Dom> GenericElement<D> {
60 pub fn new(namespace: &Namespace, tag: &str) -> Self {
62 Self::from_dom(D::Element::new(namespace, tag), 0)
63 }
64
65 pub fn freeze(mut self) -> GenericElement<D, Const> {
67 self.build();
68 GenericElement {
69 static_child_count: self.static_child_count,
70 child_vec: self.child_vec,
71 resources: self.resources,
72 events: self.events,
73 element: self.element,
74 #[cfg(debug_assertions)]
75 attributes: self.attributes,
76 phantom: PhantomData,
77 }
78 }
79
80 pub fn observe_mutations<F>(mut self, f: F) -> Self
82 where
83 F: for<'a> FnOnce(GenericElementObserver<'a, D>) -> GenericElementObserver<'a, D>,
84 {
85 f(GenericElementObserver(&mut self));
86 self
87 }
88
89 pub(crate) fn from_dom(element: D::Element, static_child_count: usize) -> Self {
90 Self {
91 static_child_count,
92 child_vec: None,
93 resources: Vec::new(),
94 events: EventStore::default(),
95 element,
96 #[cfg(debug_assertions)]
97 attributes: HashSet::new(),
98 phantom: PhantomData,
99 }
100 }
101
102 pub(crate) fn store_child(&mut self, mut child: Self) {
103 child.build();
104 self.resources.append(&mut child.resources);
105 self.events.combine(child.events);
106 }
107
108 fn check_attribute_unique(&mut self, name: &str) {
109 #[cfg(debug_assertions)]
110 debug_assert!(self.attributes.insert(name.into()));
111 let _ = name;
112 }
113
114 async fn class_signal<T>(mut element: D::Element, class: impl Signal<Item = T>)
115 where
116 T: AsRef<str>,
117 {
118 let mut class = pin!(class.to_stream());
119
120 if let Some(new_value) = class.next().await {
121 Self::check_class(&new_value);
122 element.add_class(intern_str(new_value.as_ref()));
123 let mut previous_value = new_value;
124
125 while let Some(new_value) = class.next().await {
126 Self::check_class(&new_value);
127 element.remove_class(previous_value.as_ref());
128 element.add_class(intern_str(new_value.as_ref()));
129 previous_value = new_value;
130 }
131 }
132 }
133
134 async fn classes_signal<T>(
135 mut element: D::Element,
136 classes: impl Signal<Item = impl IntoIterator<Item = T>>,
137 ) where
138 T: AsRef<str>,
139 {
140 let mut classes = pin!(classes.to_stream());
141 let mut previous_classes: Vec<T> = Vec::new();
142
143 while let Some(new_classes) = classes.next().await {
144 for to_remove in previous_classes.drain(..) {
145 element.remove_class(to_remove.as_ref());
146 }
147
148 for to_add in new_classes {
149 Self::check_class(&to_add);
150 element.add_class(intern_str(to_add.as_ref()));
151 previous_classes.push(to_add);
152 }
153 }
154 }
155
156 fn check_class(name: &impl AsRef<str>) {
157 assert!(
158 !name.as_ref().is_empty(),
159 "Class names must not be empty. Use `.classes` with `Option::None` to unset an optional class."
160 );
161 debug_assert!(
162 !name.as_ref().contains(char::is_whitespace),
163 "Class names must not contain whitespace."
164 )
165 }
166}
167
168pub struct GenericElementObserver<'a, D: Dom>(&'a mut GenericElement<D>);
169
170impl<D: Dom> GenericElementObserver<'_, D> {
171 pub fn attribute(
172 self,
173 name: String,
174 mut f: impl FnMut(&web_sys::Element, Option<String>) + 'static,
175 ) -> Self {
176 let name = name.to_string();
177
178 self.0.element.observe_attributes(
179 move |changes, _observer| {
180 for change in changes
181 .to_vec()
182 .into_iter()
183 .map(|change| change.unchecked_into::<web_sys::MutationRecord>())
184 {
185 if change.type_() == "attributes"
186 && change.attribute_name().as_ref() == Some(&name)
187 {
188 if let Some(elem) =
189 change.target().and_then(|target| target.dyn_into().ok())
190 {
191 f(&elem, change.old_value())
192 }
193 }
194 }
195 },
196 &mut self.0.events,
197 );
198 self
199 }
200}
201
202impl<D: Dom, Mutability> GenericElement<D, Mutability> {
203 fn build(&mut self) {
204 if let Some(children) = self.child_vec.take() {
205 let child_vec =
206 ChildVec::<D, ParentUnique>::new(self.element.clone(), self.static_child_count);
207
208 let handle = child_vec.run(children);
209 self.resources.push(Resource::Any(Box::new(handle)));
210 }
211 }
212}
213
214impl<Param, D> GenericElement<Template<Param, D>>
215where
216 Param: 'static,
217 D: InstantiableDom,
218{
219 pub fn on_instantiate(
220 mut self,
221 f: impl 'static + Fn(GenericElement<D>, &Param) -> GenericElement<D>,
222 ) -> Self {
223 self.element.on_instantiate(f);
224 self
225 }
226}
227
228impl<D: Dom> TextParentElement<D> for GenericElement<D> {
229 fn text<'a, T>(mut self, child: impl RefSignalOrValue<'a, Item = T>) -> Self
230 where
231 T: 'a + AsRef<str> + Into<String>,
232 {
233 if self.child_vec.is_some() {
234 return self.child(child.map(|child| text(child.as_ref())));
235 }
236
237 self.static_child_count += 1;
238
239 child.select_spawn(
240 |parent, child| {
241 parent
242 .element
243 .append_child(&D::Text::new(child.as_ref()).into());
244 },
245 |parent, child_signal| {
246 let mut text_node = D::Text::new(empty_str());
247 parent.element.append_child(&text_node.clone().into());
248
249 child_signal.for_each(move |new_value| {
250 text_node.set_text(new_value.as_ref());
251 async {}
252 })
253 },
254 &mut self,
255 );
256
257 self
258 }
259}
260
261impl<D: Dom> ParentElement<D> for GenericElement<D> {
262 fn optional_child(self, child: impl SignalOrValue<Item = Option<impl ChildNode<D>>>) -> Self {
263 child.select(
264 |mut parent, child| {
265 if let Some(child) = child {
266 if parent.child_vec.is_some() {
267 return parent.children_signal(always(vec![child]));
268 }
269
270 parent.static_child_count += 1;
271 let child = child.into();
272
273 parent.element.append_child(&child.node);
274 parent.resources.append(&mut child.resources.into_vec());
275 parent.events.combine(child.events);
276 }
277
278 parent
279 },
280 |parent, child| {
281 let child_vec = child
282 .map(|child| child.into_iter().collect::<Vec<_>>())
283 .to_signal_vec();
284 parent.children_signal(child_vec)
285 },
286 self,
287 )
288 }
289
290 fn children<N>(mut self, children: impl IntoIterator<Item = N>) -> Self
291 where
292 N: Into<Node<D>>,
293 {
294 if self.child_vec.is_some() {
295 let children = children
296 .into_iter()
297 .map(|node| node.into())
298 .collect::<Vec<_>>();
299 return self.children_signal(always(children));
300 }
301
302 for child in children {
303 self = self.child(child.into());
304 }
305
306 self
307 }
308
309 fn children_signal<N>(mut self, children: impl SignalVec<Item = N> + 'static) -> Self
310 where
311 N: Into<Node<D>>,
312 {
313 let new_children = children.map(|child| child.into());
314
315 let boxed_children = if let Some(child_vec) = self.child_vec.take() {
316 child_vec.chain(new_children).boxed_local()
317 } else {
318 new_children.boxed_local()
319 };
320
321 self.child_vec = Some(boxed_children);
322
323 self
324 }
325}
326
327impl<Mutability> GenericElement<Wet, Mutability> {
328 pub(crate) fn dom_element(&self) -> web_sys::Element {
329 self.element.dom_element()
330 }
331}
332
333impl<Mutability> GenericElement<Hydro, Mutability> {
334 pub(crate) fn hydrate(
335 mut self,
336 element: &web_sys::Element,
337 tracker: &mut HydrationStats,
338 ) -> GenericElement<Wet, Const> {
339 self.build();
340
341 GenericElement {
342 static_child_count: self.static_child_count,
343 child_vec: None,
344 resources: self.resources,
345 events: self.events,
346 element: self.element.hydrate(element, tracker),
347 #[cfg(debug_assertions)]
348 attributes: self.attributes,
349 phantom: PhantomData,
350 }
351 }
352}
353
354impl<D: InstantiableDom> ShadowRootParent<D> for GenericElement<D> {
355 fn attach_shadow_children<N>(mut self, children: impl IntoIterator<Item = N> + 'static) -> Self
356 where
357 N: Into<Node<D>>,
358 {
359 let children: Vec<_> = children
360 .into_iter()
361 .map(|child| {
362 let child = child.into();
363 let child_node = child.node;
364 self.resources.append(&mut child.resources.into_vec());
365 self.events.combine(child.events);
366 child_node
367 })
368 .collect();
369
370 self.element.attach_shadow_children(children);
371 self
372 }
373}
374
375impl<D: Dom> Element for GenericElement<D> {
376 type Dom = D;
377 type DomElement = web_sys::Element;
378
379 fn class<'a, T>(mut self, class: impl RefSignalOrValue<'a, Item = T>) -> Self
380 where
381 T: 'a + AsRef<str>,
382 {
383 class.select_spawn(
384 |elem, class| {
385 Self::check_class(&class);
386 elem.element.add_class(intern_str(class.as_ref()))
387 },
388 |elem, class| Self::class_signal(elem.element.clone(), class),
389 &mut self,
390 );
391
392 self
393 }
394
395 fn classes<'a, T, Iter>(mut self, classes: impl RefSignalOrValue<'a, Item = Iter>) -> Self
396 where
397 T: 'a + AsRef<str>,
398 Iter: 'a + IntoIterator<Item = T>,
399 {
400 classes.select_spawn(
401 |elem, classes| {
402 for class in classes {
403 Self::check_class(&class);
404 elem.element.add_class(intern_str(class.as_ref()));
405 }
406 },
407 |elem, classes| Self::classes_signal(elem.element.clone(), classes),
408 &mut self,
409 );
410
411 self
412 }
413
414 fn attribute<'a>(
415 mut self,
416 name: &str,
417 value: impl RefSignalOrValue<'a, Item = impl Attribute>,
418 ) -> Self {
419 self.check_attribute_unique(name);
420
421 value.select_spawn(
422 |elem, value| elem.element.attribute(name, value),
423 |elem, value| {
424 let name = name.to_owned();
425 clone!(mut elem.element);
426
427 value.for_each(move |new_value| {
428 element.attribute(&name, new_value);
429
430 async {}
431 })
432 },
433 &mut self,
434 );
435
436 self
437 }
438
439 fn style_property<'a>(
440 mut self,
441 name: impl Into<String>,
442 value: impl RefSignalOrValue<'a, Item = impl AsRef<str> + 'a>,
443 ) -> Self {
444 #[cfg(debug_assertions)]
445 debug_assert!(!self.attributes.contains("style"));
446
447 let name = name.into();
448
449 value.select_spawn(
450 |elem, value| elem.element.style_property(&name, value.as_ref()),
451 |elem, value| {
452 clone!(name, mut elem.element);
453
454 value.for_each(move |new_value| {
455 element.style_property(&name, new_value.as_ref());
456
457 async {}
458 })
459 },
460 &mut self,
461 );
462
463 self
464 }
465
466 fn effect(mut self, f: impl FnOnce(&Self::DomElement) + 'static) -> Self {
467 self.element.effect(f);
468 self
469 }
470
471 fn effect_signal<T>(
472 self,
473 sig: impl Signal<Item = T> + 'static,
474 f: impl Clone + Fn(&Self::DomElement, T) + 'static,
475 ) -> Self
476 where
477 T: 'static,
478 {
479 clone!(mut self.element);
480
481 let future = sig.for_each(move |x| {
482 clone!(f);
483 element.effect(move |elem| f(elem, x));
484 async {}
485 });
486
487 self.spawn_future(future)
488 }
489
490 fn map_element(self, f: impl FnOnce(&Self::DomElement) + 'static) -> Self {
491 if let Some(element) = self.element.try_dom_element() {
492 f(&element);
493 }
494
495 self
496 }
497
498 fn map_element_signal<T>(
499 self,
500 sig: impl Signal<Item = T> + 'static,
501 f: impl Clone + Fn(&Self::DomElement, T) + 'static,
502 ) -> Self
503 where
504 T: 'static,
505 {
506 clone!(mut self.element);
507
508 let future = sig.for_each(move |x| {
509 if let Some(element) = element.try_dom_element() {
510 f(&element, x);
511 }
512
513 async {}
514 });
515
516 self.spawn_future(future)
517 }
518
519 fn handle(&self) -> ElementHandle<Self::Dom, Self::DomElement> {
520 ElementHandle(self.element.clone(), PhantomData)
521 }
522
523 fn spawn_future(mut self, future: impl Future<Output = ()> + 'static) -> Self {
524 self.spawn(future);
525 self
526 }
527
528 fn on(mut self, name: &'static str, f: impl FnMut(JsValue) + 'static) -> Self {
529 self.element.on(name, f, &mut self.events);
530 self
531 }
532}
533
534impl<D: Dom> Executor for GenericElement<D> {
535 fn spawn(&mut self, future: impl Future<Output = ()> + 'static) {
536 self.resources
537 .push(Resource::FutureHandle(spawn_cancelable_future(future)));
538 }
539}
540
541impl<D: Dom, Mutability> Value for GenericElement<D, Mutability> {}
542
543impl<D: Dom, Mutability> InDom for GenericElement<D, Mutability> {
544 type Dom = D;
545}
546
547impl<D: Dom, Mutability> From<GenericElement<D, Mutability>> for Node<D> {
548 fn from(mut elem: GenericElement<D, Mutability>) -> Self {
549 elem.build();
550
551 Self {
552 node: elem.element.into(),
553 resources: elem.resources.into_boxed_slice(),
554 events: elem.events,
555 }
556 }
557}
558
559pub trait ChildElement<D: Dom = DefaultDom>:
561 Into<GenericElement<D, Const>> + Into<Node<D>> + Value + 'static
562{
563}
564
565impl<D, T> ChildElement<D> for T
566where
567 D: Dom,
568 T: Into<GenericElement<D, Const>> + Into<Node<D>> + Value + 'static,
569{
570}
571
572pub trait Element: Sized {
574 type Dom: Dom;
575 type DomElement: JsCast + 'static;
576
577 #[doc = function_body!("tests/doc/node/element.rs", static_single_class_name, [])]
600 #[doc = function_body!("tests/doc/node/element.rs", dynamic_single_class_name, [])]
605 fn class<'a, T>(self, class: impl RefSignalOrValue<'a, Item = T>) -> Self
607 where
608 T: 'a + AsRef<str>;
609
610 #[doc = function_body!("tests/doc/node/element.rs", static_class_names, [])]
631 #[doc = function_body!("tests/doc/node/element.rs", dynamic_class_names, [])]
636 fn classes<'a, T, Iter>(self, classes: impl RefSignalOrValue<'a, Item = Iter>) -> Self
638 where
639 T: 'a + AsRef<str>,
640 Iter: 'a + IntoIterator<Item = T>;
641
642 fn attribute<'a>(
650 self,
651 name: &str,
652 value: impl RefSignalOrValue<'a, Item = impl Attribute>,
653 ) -> Self;
654
655 fn style_property<'a>(
662 self,
663 name: impl Into<String>,
664 value: impl RefSignalOrValue<'a, Item = impl AsRef<str> + 'a>,
665 ) -> Self;
666
667 #[doc = function_body!("tests/doc/node/element.rs", effect, [])]
677 fn effect(self, f: impl FnOnce(&Self::DomElement) + 'static) -> Self;
679
680 fn effect_signal<T: 'static>(
683 self,
684 sig: impl Signal<Item = T> + 'static,
685 f: impl Fn(&Self::DomElement, T) + Clone + 'static,
686 ) -> Self;
687
688 fn map_element(self, f: impl FnOnce(&Self::DomElement) + 'static) -> Self;
690
691 fn map_element_signal<T: 'static>(
693 self,
694 sig: impl Signal<Item = T> + 'static,
695 f: impl Fn(&Self::DomElement, T) + Clone + 'static,
696 ) -> Self;
697
698 #[doc = function_body!("tests/doc/node/element.rs", handle, [])]
706 fn handle(&self) -> ElementHandle<Self::Dom, Self::DomElement>;
708
709 fn spawn_future(self, future: impl Future<Output = ()> + 'static) -> Self;
713
714 fn on(self, name: &'static str, f: impl FnMut(JsValue) + 'static) -> Self;
723}
724
725pub trait TextParentElement<D: Dom = DefaultDom>: Element {
727 #[doc = function_body!("tests/doc/node/element.rs", static_text, [])]
735 #[doc = function_body!("tests/doc/node/element.rs", dynamic_text, [])]
740 fn text<'a, T>(self, child: impl RefSignalOrValue<'a, Item = T>) -> Self
742 where
743 T: 'a + AsRef<str> + Into<String>;
744}
745
746pub trait ParentElement<D: Dom = DefaultDom>: TextParentElement<D> {
748 #[doc = function_body!("tests/doc/node/element.rs", static_child, [])]
756 #[doc = function_body!("tests/doc/node/element.rs", dynamic_child, [])]
761 fn child(self, child: impl SignalOrValue<Item = impl ChildNode<D>>) -> Self {
763 self.optional_child(child.map(Some))
764 }
765
766 #[doc = function_body!("tests/doc/node/element.rs", static_optional_child, [])]
777 #[doc = function_body!("tests/doc/node/element.rs", dynamic_optional_child, [])]
782 fn optional_child(self, child: impl SignalOrValue<Item = Option<impl ChildNode<D>>>) -> Self;
784
785 #[doc = function_body!("tests/doc/node/element.rs", children, [])]
791 fn children<N>(self, children: impl IntoIterator<Item = N>) -> Self
793 where
794 N: Into<Node<D>>;
795
796 fn children_signal<N>(self, children: impl SignalVec<Item = N> + 'static) -> Self
801 where
802 N: Into<Node<D>>;
803}
804
805pub trait ShadowRootParent<D: InstantiableDom = DefaultDom>: Element {
807 fn attach_shadow_children<N>(self, children: impl IntoIterator<Item = N> + 'static) -> Self
813 where
814 N: Into<Node<D>>;
815}
816
817impl<D> fmt::Display for GenericElement<D, Const>
818where
819 D: Dom,
820{
821 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
822 self.element.fmt(f)
823 }
824}
825
826impl<Param, D> GenericElement<Template<Param, D>, Const>
827where
828 D: InstantiableDom,
829 Param: 'static,
830{
831 pub fn instantiate(&self, param: &Param) -> GenericElement<D> {
835 self.element.instantiate(param)
836 }
837}
838
839fn spawn_cancelable_future(
840 future: impl Future<Output = ()> + 'static,
841) -> DiscardOnDrop<CancelableFutureHandle> {
842 let (handle, cancelable_future) = cancelable_future(future, || ());
843
844 task::spawn_local(cancelable_future);
845
846 handle
847}
848
849pub struct ElementHandle<D: Dom, DomElement>(D::Element, PhantomData<DomElement>);
856
857impl<D: Dom, DomElement> Clone for ElementHandle<D, DomElement> {
858 fn clone(&self) -> Self {
859 Self(self.0.clone(), PhantomData)
860 }
861}
862
863impl<D: Dom, DomElement: JsCast + Clone> ElementHandle<D, DomElement> {
864 pub fn try_dom_element(&self) -> Option<DomElement> {
869 self.0
870 .try_dom_element()
871 .map(|elem| elem.dyn_into().unwrap())
872 }
873
874 pub fn dom_element(&self) -> DomElement {
881 self.0.dom_element().dyn_into().unwrap()
882 }
883}
884
885impl<D: Dom> ElementHandle<D, web_sys::Element> {
886 pub fn cast<T: JsCast>(self) -> ElementHandle<D, T> {
890 ElementHandle(self.0, PhantomData)
891 }
892}
893
894#[derive(Clone, Eq, PartialEq)]
896pub enum Namespace {
897 Html,
900 Svg,
901 MathML,
902 Other(String),
903}
904
905impl Namespace {
906 pub(crate) fn create_element(&self, tag: &str) -> web_sys::Element {
907 match self {
908 Namespace::Html => document::create_element(tag),
909 _ => document::create_element_ns(intern_str(self.as_str()), tag),
910 }
911 }
912
913 pub(crate) fn as_str(&self) -> &str {
914 match self {
915 Namespace::Html => "http://www.w3.org/1999/xhtml",
916 Namespace::Svg => "http://www.w3.org/2000/svg",
917 Namespace::MathML => "http://www.w3.org/1998/Math/MathML",
918 Namespace::Other(ns) => ns,
919 }
920 }
921}
922
923pub struct Mut;
925
926pub struct Const;