use silex_core::reactivity::{Effect, ReadSignal, Signal};
use silex_core::traits::Accessor;
use silex_dom::View;
use std::cell::RefCell;
use std::rc::Rc;
use web_sys::Node;
#[derive(Clone)]
pub struct Show<Cond, ViewFn, FalsyViewFn, V1, V2> {
condition: Cond,
view: ViewFn,
fallback: FalsyViewFn,
_marker: std::marker::PhantomData<(V1, V2)>,
}
impl<Cond, ViewFn, V1> Show<Cond, ViewFn, fn() -> (), V1, ()>
where
Cond: Accessor<bool> + 'static,
ViewFn: Fn() -> V1 + 'static,
V1: View,
{
pub fn new(condition: Cond, view: ViewFn) -> Self {
Self {
condition,
view,
fallback: || (),
_marker: std::marker::PhantomData,
}
}
}
impl<Cond, ViewFn, FalsyViewFn, V1, V2> Show<Cond, ViewFn, FalsyViewFn, V1, V2>
where
Cond: Accessor<bool> + 'static,
ViewFn: Fn() -> V1 + 'static,
FalsyViewFn: Fn() -> V2 + 'static,
V1: View,
V2: View,
{
pub fn fallback<NewFalsyFn, NewV2>(
self,
fallback: NewFalsyFn,
) -> Show<Cond, ViewFn, NewFalsyFn, V1, NewV2>
where
NewFalsyFn: Fn() -> NewV2 + 'static,
NewV2: View,
{
Show {
condition: self.condition,
view: self.view,
fallback,
_marker: std::marker::PhantomData,
}
}
}
impl<Cond, ViewFn, FalsyViewFn, V1, V2> View for Show<Cond, ViewFn, FalsyViewFn, V1, V2>
where
Cond: Accessor<bool> + 'static,
ViewFn: Fn() -> V1 + 'static,
FalsyViewFn: Fn() -> V2 + 'static,
V1: View,
V2: View,
{
fn mount(self, parent: &Node) {
let document = silex_dom::document();
let start_marker = document.create_comment("show-start");
let start_node: Node = start_marker.into();
if let Err(e) = parent
.append_child(&start_node)
.map_err(crate::SilexError::from)
{
silex_core::error::handle_error(e);
return;
}
let end_marker = document.create_comment("show-end");
let end_node: Node = end_marker.into();
if let Err(e) = parent
.append_child(&end_node)
.map_err(crate::SilexError::from)
{
silex_core::error::handle_error(e);
return;
}
let cond = self.condition;
let view_fn = self.view;
let fallback_fn = self.fallback;
let prev_state = Rc::new(RefCell::new(None::<bool>));
Effect::new(move |_| {
let val = cond.value();
let mut state = prev_state.borrow_mut();
if *state == Some(val) {
return;
}
if let Some(parent) = start_node.parent_node() {
while let Some(sibling) = start_node.next_sibling() {
if sibling == end_node {
break;
}
let _ = parent.remove_child(&sibling);
}
}
let fragment = document.create_document_fragment();
let fragment_node: Node = fragment.clone().into();
if val {
(view_fn)().mount(&fragment_node);
} else {
(fallback_fn)().mount(&fragment_node);
}
if let Some(parent) = end_node.parent_node() {
let _ = parent.insert_before(&fragment_node, Some(&end_node));
}
*state = Some(val);
});
}
}
pub trait SignalShowExt {
type Cond: Accessor<bool> + 'static;
fn when<V, F>(self, view: F) -> Show<Self::Cond, F, fn() -> (), V, ()>
where
V: View,
F: Fn() -> V + 'static;
}
impl SignalShowExt for ReadSignal<bool> {
type Cond = Self;
fn when<V, F>(self, view: F) -> Show<Self::Cond, F, fn() -> (), V, ()>
where
V: View,
F: Fn() -> V + 'static,
{
Show::new(self, view)
}
}
impl SignalShowExt for silex_core::reactivity::Memo<bool> {
type Cond = Self;
fn when<V, F>(self, view: F) -> Show<Self::Cond, F, fn() -> (), V, ()>
where
V: View,
F: Fn() -> V + 'static,
{
Show::new(self, view)
}
}
impl SignalShowExt for Signal<bool> {
type Cond = Self;
fn when<V, F>(self, view: F) -> Show<Self::Cond, F, fn() -> (), V, ()>
where
V: View,
F: Fn() -> V + 'static,
{
Show::new(self, view)
}
}