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}