silex 0.1.0-alpha.2

Next Generation High-Performance Rust Web Framework based on fine-grained reactivity and no-virtual-DOM architecture.
Documentation
use silex_core::reactivity::SuspenseContext;
use silex_core::reactivity::{Effect, create_scope, provide_context};
use silex_dom::view::View;
use silex_html::div;
use web_sys::Node;

#[derive(Clone)]
pub struct Suspense<V, F> {
    children: V,
    fallback: F,
}

pub fn suspense() -> Suspense<(), ()> {
    Suspense {
        children: (),
        fallback: (),
    }
}

impl<V, F> Suspense<V, F> {
    pub fn children<NewV>(self, children: NewV) -> Suspense<NewV, F> {
        Suspense {
            children,
            fallback: self.fallback,
        }
    }

    pub fn fallback<NewF>(self, fallback: NewF) -> Suspense<V, NewF> {
        Suspense {
            children: self.children,
            fallback,
        }
    }
}

// 支持 children/fallback 作为返回 View 的闭包
impl<V, F, VRes, FRes> View for Suspense<V, F>
where
    V: Fn() -> VRes + 'static,
    VRes: View + 'static,
    F: Fn() -> FRes + 'static,
    FRes: View + 'static,
{
    fn mount(self, parent: &Node) {
        let children_fn = self.children;
        let fallback_fn = self.fallback;

        let parent_clone = parent.clone();

        // 包裹在作用域中以管理上下文和生命周期
        create_scope(move || {
            let ctx = SuspenseContext::new();
            provide_context(ctx);

            let count = ctx.count;

            // 1. 内容包装器(加载时隐藏)
            let content_wrapper = div(()).class("suspense-content");
            let _ = content_wrapper.clone().style(move || {
                if count.get() > 0 {
                    "display: none"
                } else {
                    "display: block"
                }
            });
            content_wrapper.clone().mount(&parent_clone);
            let content_root = content_wrapper.element;

            Effect::new(move |_| {
                let view = children_fn();
                content_root.set_inner_html("");
                view.mount(&content_root);
            });

            // 2. 后备包装器(加载时可见)
            let fallback_wrapper = div(()).class("suspense-fallback");
            let _ = fallback_wrapper.clone().style(move || {
                if count.get() > 0 {
                    "display: block"
                } else {
                    "display: none"
                }
            });
            fallback_wrapper.clone().mount(&parent_clone);
            let fallback_root = fallback_wrapper.element;

            Effect::new(move |_| {
                let view = fallback_fn();
                fallback_root.set_inner_html("");
                view.mount(&fallback_root);
            });
        });
    }
}