1use super::{ElementTarget, JsValue, Scope, TemplateComponent, component::Retained};
3use crate::{OwnerHandle, untrack};
4use std::rc::Rc;
5
6type Factory = dyn Fn(&OwnerHandle) -> Result<Scope, JsValue>;
7
8#[derive(Clone)]
15#[must_use = "content is lazy; supply it to a slot to mount it"]
16pub struct Content(Rc<Factory>);
17
18impl Content {
19 pub fn new<C: TemplateComponent>(make: impl Fn(OwnerHandle) -> C + 'static) -> Self {
20 Self::try_new(move |owner| Ok(make(owner)))
21 }
22
23 pub fn try_new<C: TemplateComponent>(
25 make: impl Fn(OwnerHandle) -> Result<C, JsValue> + 'static,
26 ) -> Self {
27 Self(Rc::new(move |parent| C::prepare(parent, &make)))
28 }
29
30 #[doc(hidden)]
33 pub fn from_prepared(make: impl Fn(&OwnerHandle) -> Result<Scope, JsValue> + 'static) -> Self {
34 Self(Rc::new(make))
35 }
36
37 pub fn prepare(&self, parent: &OwnerHandle) -> Result<Scope, JsValue> {
40 let scope = (self.0)(parent)?;
41 let owner = scope.owner();
42 if owner.is_disposed()
43 || owner.is_active()
44 || !owner.is_child_of(parent)
45 || scope.root().parent_node().is_some()
46 {
47 return Err(JsValue::from_str(
48 "fusor: slot content must be a detached, prepared child of its receiving owner",
49 ));
50 }
51 Ok(scope)
52 }
53}
54
55impl PartialEq for Content {
56 fn eq(&self, other: &Self) -> bool {
57 Rc::ptr_eq(&self.0, &other.0)
58 }
59}
60
61impl Eq for Content {}
62
63impl Scope {
64 pub fn slot<R: Into<Option<Content>>>(
68 &mut self,
69 target: impl ElementTarget,
70 read: impl Fn() -> R + 'static,
71 ) -> Result<(), JsValue> {
72 self.slot_with(target, move || read().into().map(|content| ((), content)))
73 }
74
75 pub fn slot_with<K: PartialEq + 'static>(
78 &mut self,
79 target: impl ElementTarget,
80 read: impl Fn() -> Option<(K, Content)> + 'static,
81 ) -> Result<(), JsValue> {
82 let container = target.resolve(self)?;
83 let parent = self.owner();
84 let mut current: Retained<(K, Content)> = Retained::default();
85 self.bind(move || {
86 let next = read();
87 untrack(|| {
88 let Some(identity) = next else {
89 current.clear();
90 return Ok(());
91 };
92 if current.key() == Some(&identity) {
93 return Ok(());
94 }
95 let child = identity.1.prepare(&parent)?;
96 current.replace(identity, child, |child| child.attach(&container))
97 })
98 })
99 }
100}