tachys/renderer/
mod.rs

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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
use crate::view::{Mountable, ToTemplate};
use std::{borrow::Cow, fmt::Debug};
use wasm_bindgen::JsValue;

/// A DOM renderer.
pub mod dom;

/// The renderer being used for the application.
///
/// ### Note
/// This was designed to be included as a generic on view types, to support different rendering
/// backends using the same view tree structure. However, adding the number of generics that was
/// required to make this work caused catastrophic compile times and linker errors on larger
/// applications, so this "generic rendering" approach was removed before 0.7.0 release.
///
/// It is possible that we will try a different approach to achieve the same functionality in the
/// future, so to the extent possible the rest of the crate tries to stick to using
/// [`Renderer`].
/// methods rather than directly manipulating the DOM inline.
pub type Rndr = dom::Dom;

/// Types used by the renderer.
///
/// See [`Rndr`] for additional information on this rendering approach.
pub mod types {
    pub use super::dom::{
        ClassList, CssStyleDeclaration, Element, Event, Node, Placeholder,
        TemplateElement, Text,
    };
}

/* #[cfg(feature = "testing")]
/// A renderer based on a mock DOM.
pub mod mock_dom;
/// A DOM renderer optimized for element creation.
#[cfg(feature = "sledgehammer")]
pub mod sledgehammer; */

/// Implements the instructions necessary to render an interface on some platform.
///
/// By default, this is implemented for the Document Object Model (DOM) in a Web
/// browser, but implementing this trait for some other platform allows you to use
/// the library to render any tree-based UI.
pub trait Renderer: Send + Sized + Debug + 'static {
    /// The basic type of node in the view tree.
    type Node: Mountable + Clone + 'static;
    /// A visible element in the view tree.
    type Element: AsRef<Self::Node>
        + CastFrom<Self::Node>
        + Mountable
        + Clone
        + 'static;
    /// A text node in the view tree.
    type Text: AsRef<Self::Node>
        + CastFrom<Self::Node>
        + Mountable
        + Clone
        + 'static;
    /// A placeholder node, which can be inserted into the tree but does not
    /// appear (e.g., a comment node in the DOM).
    type Placeholder: AsRef<Self::Node>
        + CastFrom<Self::Node>
        + Mountable
        + Clone
        + 'static;

    /// Interns a string slice, if that is available on this platform and useful as an optimization.
    fn intern(text: &str) -> &str;

    /// Creates a new text node.
    fn create_text_node(text: &str) -> Self::Text;

    /// Creates a new placeholder node.
    fn create_placeholder() -> Self::Placeholder;

    /// Sets the text content of the node. If it's not a text node, this does nothing.
    fn set_text(node: &Self::Text, text: &str);

    /// Sets the given attribute on the given node by key and value.
    fn set_attribute(node: &Self::Element, name: &str, value: &str);

    /// Removes the given attribute on the given node.
    fn remove_attribute(node: &Self::Element, name: &str);

    /// Appends the new child to the parent, before the anchor node. If `anchor` is `None`,
    /// append to the end of the parent's children.
    fn insert_node(
        parent: &Self::Element,
        new_child: &Self::Node,
        marker: Option<&Self::Node>,
    );

    /// Removes the child node from the parents, and returns the removed node.
    fn remove_node(
        parent: &Self::Element,
        child: &Self::Node,
    ) -> Option<Self::Node>;

    /// Removes all children from the parent element.
    fn clear_children(parent: &Self::Element);

    /// Removes the node.
    fn remove(node: &Self::Node);

    /// Gets the parent of the given node, if any.
    fn get_parent(node: &Self::Node) -> Option<Self::Node>;

    /// Returns the first child node of the given node, if any.
    fn first_child(node: &Self::Node) -> Option<Self::Node>;

    /// Returns the next sibling of the given node, if any.
    fn next_sibling(node: &Self::Node) -> Option<Self::Node>;

    /// Logs the given node in a platform-appropriate way.
    fn log_node(node: &Self::Node);
}

/// A function that can be called to remove an event handler from an element after it has been added.
#[must_use = "This will invalidate the event handler when it is dropped. You \
              should store it in some other data structure to clean it up \
              later to avoid dropping it immediately, or leak it with \
              std::mem::forget() to never drop it."]
pub struct RemoveEventHandler<T>(Box<dyn FnOnce(&T) + Send + Sync>);

impl<T> RemoveEventHandler<T> {
    /// Creates a new container with a function that will be called when it is dropped.
    pub(crate) fn new(remove: impl FnOnce(&T) + Send + Sync + 'static) -> Self {
        Self(Box::new(remove))
    }

    pub(crate) fn into_inner(self) -> Box<dyn FnOnce(&T) + Send + Sync> {
        self.0
    }
}

/// Additional rendering behavior that applies only to DOM nodes.
pub trait DomRenderer: Renderer {
    /// Generic event type, from which any specific event can be converted.
    type Event;
    /// The list of CSS classes for an element.
    type ClassList: Clone + 'static;
    /// The CSS styles for an element.
    type CssStyleDeclaration: Clone + 'static;
    /// The type of a `<template>` element.
    type TemplateElement;

    /// Sets a JavaScript object property on a DOM element.
    fn set_property(el: &Self::Element, key: &str, value: &JsValue);

    /// Adds an event listener to an element.
    ///
    /// Returns a function to remove the listener.
    fn add_event_listener(
        el: &Self::Element,
        name: &str,
        cb: Box<dyn FnMut(Self::Event)>,
    ) -> RemoveEventHandler<Self::Element>;

    /// Adds an event listener to an element, delegated to the window if possible.
    ///
    /// Returns a function to remove the listener.
    fn add_event_listener_delegated(
        el: &Self::Element,
        name: Cow<'static, str>,
        delegation_key: Cow<'static, str>,
        cb: Box<dyn FnMut(Self::Event)>,
    ) -> RemoveEventHandler<Self::Element>;

    /// Return the `event.target`, cast to the given type.
    fn event_target<T>(ev: &Self::Event) -> T
    where
        T: CastFrom<Self::Element>;

    /// The list of CSS classes for an element.
    fn class_list(el: &Self::Element) -> Self::ClassList;

    /// Add a class to the list.
    fn add_class(class_list: &Self::ClassList, name: &str);

    /// Remove a class from the list.
    fn remove_class(class_list: &Self::ClassList, name: &str);

    /// The set of styles for an element.
    fn style(el: &Self::Element) -> Self::CssStyleDeclaration;

    /// Sets a CSS property.
    fn set_css_property(
        style: &Self::CssStyleDeclaration,
        name: &str,
        value: &str,
    );

    /// Sets the `innerHTML` of a DOM element, without escaping any values.
    fn set_inner_html(el: &Self::Element, html: &str);

    /// Returns a cached template element created from the given type.
    fn get_template<V>() -> Self::TemplateElement
    where
        V: ToTemplate + 'static;

    /// Deeply clones a template.
    fn clone_template(tpl: &Self::TemplateElement) -> Self::Element;

    /// Creates a single element from a string of HTML.
    fn create_element_from_html(html: &str) -> Self::Element;
}

/// Attempts to cast from one type to another.
///
/// This works in a similar way to `TryFrom`. We implement it as a separate trait
/// simply so we don't have to create wrappers for the `web_sys` types; it can't be
/// implemented on them directly because of the orphan rules.
pub trait CastFrom<T>
where
    Self: Sized,
{
    /// Casts a node from one type to another.
    fn cast_from(source: T) -> Option<Self>;
}