tachys/renderer/
mod.rs

1use crate::view::{Mountable, ToTemplate};
2use std::{borrow::Cow, fmt::Debug, marker::PhantomData};
3use wasm_bindgen::JsValue;
4
5/// A DOM renderer.
6pub mod dom;
7
8/// The renderer being used for the application.
9///
10/// ### Note
11/// This was designed to be included as a generic on view types, to support different rendering
12/// backends using the same view tree structure. However, adding the number of generics that was
13/// required to make this work caused catastrophic compile times and linker errors on larger
14/// applications, so this "generic rendering" approach was removed before 0.7.0 release.
15///
16/// It is possible that we will try a different approach to achieve the same functionality in the
17/// future, so to the extent possible the rest of the crate tries to stick to using
18/// [`Renderer`].
19/// methods rather than directly manipulating the DOM inline.
20pub type Rndr = dom::Dom;
21
22/// Types used by the renderer.
23///
24/// See [`Rndr`] for additional information on this rendering approach.
25pub mod types {
26    pub use super::dom::{
27        ClassList, CssStyleDeclaration, Element, Event, Node, Placeholder,
28        TemplateElement, Text,
29    };
30}
31
32/* #[cfg(feature = "testing")]
33/// A renderer based on a mock DOM.
34pub mod mock_dom;
35/// A DOM renderer optimized for element creation.
36#[cfg(feature = "sledgehammer")]
37pub mod sledgehammer; */
38
39/// Implements the instructions necessary to render an interface on some platform.
40///
41/// By default, this is implemented for the Document Object Model (DOM) in a Web
42/// browser, but implementing this trait for some other platform allows you to use
43/// the library to render any tree-based UI.
44pub trait Renderer: Send + Sized + Debug + 'static {
45    /// The basic type of node in the view tree.
46    type Node: Mountable + Clone + 'static;
47    /// A visible element in the view tree.
48    type Element: AsRef<Self::Node>
49        + CastFrom<Self::Node>
50        + Mountable
51        + Clone
52        + 'static;
53    /// A text node in the view tree.
54    type Text: AsRef<Self::Node>
55        + CastFrom<Self::Node>
56        + Mountable
57        + Clone
58        + 'static;
59    /// A placeholder node, which can be inserted into the tree but does not
60    /// appear (e.g., a comment node in the DOM).
61    type Placeholder: AsRef<Self::Node>
62        + CastFrom<Self::Node>
63        + Mountable
64        + Clone
65        + 'static;
66
67    /// Interns a string slice, if that is available on this platform and useful as an optimization.
68    fn intern(text: &str) -> &str;
69
70    /// Creates a new text node.
71    fn create_text_node(text: &str) -> Self::Text;
72
73    /// Creates a new placeholder node.
74    fn create_placeholder() -> Self::Placeholder;
75
76    /// Sets the text content of the node. If it's not a text node, this does nothing.
77    fn set_text(node: &Self::Text, text: &str);
78
79    /// Sets the given attribute on the given node by key and value.
80    fn set_attribute(node: &Self::Element, name: &str, value: &str);
81
82    /// Removes the given attribute on the given node.
83    fn remove_attribute(node: &Self::Element, name: &str);
84
85    /// Appends the new child to the parent, before the anchor node. If `anchor` is `None`,
86    /// append to the end of the parent's children.
87    fn insert_node(
88        parent: &Self::Element,
89        new_child: &Self::Node,
90        marker: Option<&Self::Node>,
91    );
92
93    /// Removes the child node from the parents, and returns the removed node.
94    fn remove_node(
95        parent: &Self::Element,
96        child: &Self::Node,
97    ) -> Option<Self::Node>;
98
99    /// Removes all children from the parent element.
100    fn clear_children(parent: &Self::Element);
101
102    /// Removes the node.
103    fn remove(node: &Self::Node);
104
105    /// Gets the parent of the given node, if any.
106    fn get_parent(node: &Self::Node) -> Option<Self::Node>;
107
108    /// Returns the first child node of the given node, if any.
109    fn first_child(node: &Self::Node) -> Option<Self::Node>;
110
111    /// Returns the next sibling of the given node, if any.
112    fn next_sibling(node: &Self::Node) -> Option<Self::Node>;
113
114    /// Logs the given node in a platform-appropriate way.
115    fn log_node(node: &Self::Node);
116}
117
118/// A function that can be called to remove an event handler from an element after it has been added.
119#[must_use = "This will invalidate the event handler when it is dropped. You \
120              should store it in some other data structure to clean it up \
121              later to avoid dropping it immediately, or leak it with \
122              std::mem::forget() to never drop it."]
123#[allow(clippy::type_complexity)]
124pub struct RemoveEventHandler<T>(
125    Option<Box<dyn FnOnce() + Send + Sync>>,
126    // only here to keep the generic, removing which would be a breaking change
127    // TODO remove generic in 0.9
128    PhantomData<fn() -> T>,
129);
130
131impl<T> RemoveEventHandler<T> {
132    /// Creates a new container with a function that will be called when it is dropped.
133    pub(crate) fn new(remove: impl FnOnce() + Send + Sync + 'static) -> Self {
134        Self(Some(Box::new(remove)), PhantomData)
135    }
136
137    #[allow(clippy::type_complexity)]
138    pub(crate) fn into_inner(
139        mut self,
140    ) -> Option<Box<dyn FnOnce() + Send + Sync>> {
141        self.0.take()
142    }
143}
144
145impl<T> Drop for RemoveEventHandler<T> {
146    fn drop(&mut self) {
147        if let Some(cb) = self.0.take() {
148            cb()
149        }
150    }
151}
152
153/// Additional rendering behavior that applies only to DOM nodes.
154pub trait DomRenderer: Renderer {
155    /// Generic event type, from which any specific event can be converted.
156    type Event;
157    /// The list of CSS classes for an element.
158    type ClassList: Clone + 'static;
159    /// The CSS styles for an element.
160    type CssStyleDeclaration: Clone + 'static;
161    /// The type of a `<template>` element.
162    type TemplateElement;
163
164    /// Sets a JavaScript object property on a DOM element.
165    fn set_property(el: &Self::Element, key: &str, value: &JsValue);
166
167    /// Adds an event listener to an element.
168    ///
169    /// Returns a function to remove the listener.
170    fn add_event_listener(
171        el: &Self::Element,
172        name: &str,
173        cb: Box<dyn FnMut(Self::Event)>,
174    ) -> RemoveEventHandler<Self::Element>;
175
176    /// Adds an event listener to an element, delegated to the window if possible.
177    ///
178    /// Returns a function to remove the listener.
179    fn add_event_listener_delegated(
180        el: &Self::Element,
181        name: Cow<'static, str>,
182        delegation_key: Cow<'static, str>,
183        cb: Box<dyn FnMut(Self::Event)>,
184    ) -> RemoveEventHandler<Self::Element>;
185
186    /// Return the `event.target`, cast to the given type.
187    fn event_target<T>(ev: &Self::Event) -> T
188    where
189        T: CastFrom<Self::Element>;
190
191    /// The list of CSS classes for an element.
192    fn class_list(el: &Self::Element) -> Self::ClassList;
193
194    /// Add a class to the list.
195    fn add_class(class_list: &Self::ClassList, name: &str);
196
197    /// Remove a class from the list.
198    fn remove_class(class_list: &Self::ClassList, name: &str);
199
200    /// The set of styles for an element.
201    fn style(el: &Self::Element) -> Self::CssStyleDeclaration;
202
203    /// Sets a CSS property.
204    fn set_css_property(
205        style: &Self::CssStyleDeclaration,
206        name: &str,
207        value: &str,
208    );
209
210    /// Sets the `innerHTML` of a DOM element, without escaping any values.
211    fn set_inner_html(el: &Self::Element, html: &str);
212
213    /// Returns a cached template element created from the given type.
214    fn get_template<V>() -> Self::TemplateElement
215    where
216        V: ToTemplate + 'static;
217
218    /// Deeply clones a template.
219    fn clone_template(tpl: &Self::TemplateElement) -> Self::Element;
220
221    /// Creates a single element from a string of HTML.
222    fn create_element_from_html(html: &str) -> Self::Element;
223}
224
225/// Attempts to cast from one type to another.
226///
227/// This works in a similar way to `TryFrom`. We implement it as a separate trait
228/// simply so we don't have to create wrappers for the `web_sys` types; it can't be
229/// implemented on them directly because of the orphan rules.
230pub trait CastFrom<T>
231where
232    Self: Sized,
233{
234    /// Casts a node from one type to another.
235    fn cast_from(source: T) -> Option<Self>;
236}