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 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
//! An abstraction for the underlying DOM.
//!
//! An application, or parts of an application, can be parameterized on the
//! underlying DOM type so it can be rendered in different ways:
//!
//! - Server side rendering with [`Dry`]
//! - Client side rendering with [`Wet`]
//! - Hydrating a server side rendered initial application state with [`Hydro`]
//! - As a template, or part of a template, with [`Template`]
//!
//! See the concrete DOM types for some examples.
use std::marker::PhantomData;
use silkenweb_macros::cfg_browser;
use self::{
dry::{DryElement, DryNode, DryText},
hydro::{HydroElement, HydroNode, HydroText},
template::{TemplateElement, TemplateNode, TemplateText},
wet::{WetElement, WetNode, WetText},
};
pub(super) mod private;
mod dry;
mod hydro;
mod template;
mod wet;
/// The main DOM abstraction.
///
/// This is not user implementable.
pub trait Dom: private::Dom {}
/// A DOM that can be instantiated from a [`Template`] DOM.
pub trait InstantiableDom: Dom + private::InstantiableDom {}
/// The [`Dom`] type to which a node belongs.
pub trait InDom {
type Dom: Dom;
}
#[cfg_browser(true)]
/// The default DOM for the current platform.
pub type DefaultDom = Wet;
#[cfg_browser(false)]
/// The default DOM for the current platform.
pub type DefaultDom = Dry;
/// A DOM that can only be rendered on the server
///
/// # Example
///
/// Type annotations have been provided for clarity, but the types can be
/// inferred.
///
/// ```
/// # use silkenweb::{dom::Dry, elements::html::{p, P}, prelude::*};
/// let app: P<Dry> = p().text("Hello, world!").into();
///
/// assert_eq!(app.freeze().to_string(), "<p>Hello, world!</p>");
/// ```
pub struct Dry;
impl Dom for Dry {}
impl private::Dom for Dry {
type Element = DryElement;
type Node = DryNode;
type Text = DryText;
}
impl InstantiableDom for Dry {}
impl private::InstantiableDom for Dry {
type InstantiableElement = DryElement;
type InstantiableNode = DryNode;
}
/// A DOM that can be rendered on the client or hydrated onto an existing DOM
/// element.
///
/// # Example
///
/// Type annotations have been provided for clarity, but the types can be
/// inferred.
///
/// ```no_run
/// # use html::{p, P};
/// # use silkenweb::{dom::Hydro, hydration::hydrate, prelude::*};
/// let app: P<Hydro> = p().text("Hello, world!");
///
/// hydrate("app-id", app);
/// ```
pub struct Hydro;
impl Dom for Hydro {}
impl private::Dom for Hydro {
type Element = HydroElement;
type Node = HydroNode;
type Text = HydroText;
}
impl InstantiableDom for Hydro {}
impl private::InstantiableDom for Hydro {
type InstantiableElement = HydroElement;
type InstantiableNode = HydroNode;
}
/// A DOM that can only be rendered on the client.
///
/// # Example
///
/// Type annotations have been provided for clarity, but the types can be
/// inferred.
///
/// ```no_run
/// # use html::{p, P};
/// # use silkenweb::{dom::Wet, document::Document, prelude::*};
/// let app: P<Wet> = p().text("Hello, world!");
///
/// Wet::mount("app-id", app);
/// ```
pub struct Wet;
impl Dom for Wet {}
impl private::Dom for Wet {
type Element = WetElement;
type Node = WetNode;
type Text = WetText;
}
impl InstantiableDom for Wet {}
impl private::InstantiableDom for Wet {
type InstantiableElement = WetElement;
type InstantiableNode = WetNode;
}
/// A template DOM that can be used to instantiate other DOM types by cloning.
///
/// Cloning a template can be faster than creating each DOM node individually.
/// It's likely to get a maximum 10-20% increase, so should only be used in hot
/// code paths.
///
/// # Example
///
/// Type annotations have been provided for clarity, but the types can be
/// inferred.
///
/// ```
/// # use html::{p, P};
/// # use silkenweb::{
/// # dom::{Dry, Template},
/// # node::element::Const,
/// # prelude::*,
/// # };
/// let elem: P<Template<String, Dry>> = p().on_instantiate(|p, message| p.text(message));
/// let template: P<Template<String, Dry>, Const> = elem.freeze();
/// let hello = template.instantiate(&"Hello, world!".to_string());
/// let goodbye = template.instantiate(&"Goodbye!".to_string());
///
/// assert_eq!(hello.freeze().to_string(), "<p>Hello, world!</p>");
/// assert_eq!(goodbye.freeze().to_string(), "<p>Goodbye!</p>");
/// ```
pub struct Template<Param, D: InstantiableDom = DefaultDom>(PhantomData<(Param, D)>);
impl<Param: 'static, D: InstantiableDom> Dom for Template<Param, D> {}
impl<Param: 'static, D: InstantiableDom> private::Dom for Template<Param, D> {
type Element = TemplateElement<Param, D>;
type Node = TemplateNode<Param, D>;
type Text = TemplateText<D>;
}