1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
//! # Maple API Documentation
//!
//! Maple is a VDOM-less web library with fine-grained reactivity.
//!
//! This is the API docs for maple. If you are looking for the usage docs, checkout the [README](https://github.com/lukechu10/maple).
//!
//! ## Supported Targets
//! - `wasm32-unknown-unknown`
//!
//! ## Features
//! - `serde` - Enables serializing and deserializing `Signal`s and other wrapper types using `serde`.

#![allow(non_snake_case)]
#![warn(clippy::clone_on_ref_ptr)]
#![warn(clippy::rc_buffer)]
#![deny(clippy::trait_duplication_in_bounds)]
#![deny(clippy::type_repetition_in_bounds)]

pub mod flow;
#[doc(hidden)]
pub mod internal;
#[doc(hidden)]
pub mod macros;
pub mod noderef;
pub mod reactive;
pub mod render;

use prelude::SignalVec;
use web_sys::Node;

use std::cell::RefCell;

pub use maple_core_macro::template;

/// The result of the [`template!`](template) macro. Should not be used directly.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TemplateResult {
    node: Node,
}

impl TemplateResult {
    /// Create a new [`TemplateResult`] from a [`Node`].
    pub fn new(node: Node) -> Self {
        Self { node }
    }

    /// Create a new [`TemplateResult`] with a blank comment node
    pub fn empty() -> Self {
        Self::new(
            web_sys::window()
                .unwrap()
                .document()
                .unwrap()
                .create_comment("")
                .into(),
        )
    }

    pub fn inner_element(&self) -> Node {
        self.node.clone()
    }
}

/// A [`SignalVec`](reactive::SignalVec) of [`TemplateResult`]s.
#[derive(Clone)]
pub struct TemplateList {
    templates: reactive::SignalVec<TemplateResult>,
}

impl From<SignalVec<TemplateResult>> for TemplateList {
    fn from(templates: SignalVec<TemplateResult>) -> Self {
        Self { templates }
    }
}

/// Render a [`TemplateResult`] into the DOM.
///
/// Alias for [`render_to`] with `parent` being the `<body>` tag.
pub fn render(template_result: impl FnOnce() -> TemplateResult + 'static) {
    let window = web_sys::window().unwrap();
    let document = window.document().unwrap();

    render_to(template_result, &document.body().unwrap());
}

/// Render a [`TemplateResult`] under a `parent` node. For rendering under the `<body>` tag, use [`render()`] instead.
pub fn render_to(template_result: impl FnOnce() -> TemplateResult + 'static, parent: &Node) {
    let owner = reactive::create_root(move || {
        parent.append_child(&template_result().node).unwrap();
    });

    thread_local! {
        static GLOBAL_OWNERS: RefCell<Vec<reactive::Owner>> = RefCell::new(Vec::new());
    }

    GLOBAL_OWNERS.with(|global_owners| global_owners.borrow_mut().push(owner));
}

/// The maple prelude.
pub mod prelude {
    pub use crate::cloned;
    pub use crate::flow::{Indexed, IndexedProps, Keyed, KeyedProps};
    pub use crate::noderef::NodeRef;
    pub use crate::reactive::{
        create_effect, create_effect_initial, create_memo, create_root, create_selector,
        create_selector_with, on_cleanup, untrack, Signal, SignalVec, StateHandle,
    };
    pub use crate::render::Render;
    pub use crate::{render, render_to, TemplateList, TemplateResult};

    pub use maple_core_macro::template;
}