docs: add tachys docs

This commit is contained in:
Greg Johnston 2024-07-13 13:01:40 -04:00
parent ddb596feb5
commit 64bc2580ff
44 changed files with 1092 additions and 345 deletions

View file

@ -1,6 +1,5 @@
use once_cell::unsync::Lazy;
use wasm_bindgen::JsCast;
use web_sys::{Document, HtmlElement, Node, Window};
use web_sys::{Document, HtmlElement, Window};
thread_local! {
pub(crate) static WINDOW: web_sys::Window = web_sys::window().unwrap();
@ -20,27 +19,22 @@ pub fn window() -> Window {
///
/// This is cached as a thread-local variable, so calling `document()` multiple times
/// requires only one call out to JavaScript.
///
/// ## Panics
/// Panics if called outside a browser environment.
pub fn document() -> Document {
DOCUMENT.with(Clone::clone)
}
/// The `<body>` element.
///
/// ## Panics
/// Panics if there is no `<body>` in the current document, or if it is called outside a browser
/// environment.
pub fn body() -> HtmlElement {
document().body().unwrap()
}
pub fn comment() -> Node {
thread_local! {
static COMMENT: Lazy<Node> = Lazy::new(|| {
document().create_comment("").unchecked_into()
});
}
COMMENT.with(|n| n.clone_node().unwrap())
}
pub fn log(s: &str) {
web_sys::console::log_1(&wasm_bindgen::JsValue::from_str(s));
}
/// Helper function to extract [`Event.target`](https://developer.mozilla.org/en-US/docs/Web/API/Event/target)
/// from any event.
pub fn event_target<T>(event: &web_sys::Event) -> T

View file

@ -8,6 +8,7 @@ use std::{
#[cfg(feature = "ssr")]
use std::{future::Future, pin::Pin};
/// A type-erased container for any [`Attribute`].
pub struct AnyAttribute<R: Renderer> {
type_id: TypeId,
html_len: usize,
@ -40,6 +41,7 @@ where
}
}
/// View state for [`AnyAttribute`].
pub struct AnyAttributeState<R>
where
R: Renderer,
@ -50,10 +52,12 @@ where
rndr: PhantomData<R>,
}
/// Converts an [`Attribute`] into [`AnyAttribute`].
pub trait IntoAnyAttribute<R>
where
R: Renderer,
{
/// Wraps the given attribute.
fn into_any_attr(self) -> AnyAttribute<R>;
}

View file

@ -3,12 +3,14 @@ use crate::{
view::add_attr::AddAnyAttr,
};
/// Applies ARIA attributes to an HTML element.
pub trait AriaAttributes<Rndr, V>
where
Self: Sized + AddAnyAttr<Rndr>,
V: AttributeValue<Rndr>,
Rndr: Renderer,
{
/// Identifies the currently active descendant of a composite widget.
fn aria_activedescendant(
self,
value: V,
@ -17,6 +19,7 @@ where
self.add_any_attr(aria_activedescendant(value))
}
/// Indicates whether assistive technologies will present all, or only parts of, the changed region based on the change notifications defined by the `aria-relevant` attribute.
fn aria_atomic(
self,
value: V,
@ -24,6 +27,7 @@ where
self.add_any_attr(aria_atomic(value))
}
/// Indicates whether user input completion suggestions are provided.
fn aria_autocomplete(
self,
value: V,
@ -32,6 +36,7 @@ where
self.add_any_attr(aria_autocomplete(value))
}
/// Indicates whether an element, and its subtree, are currently being updated.
fn aria_busy(
self,
value: V,
@ -39,6 +44,7 @@ where
self.add_any_attr(aria_busy(value))
}
/// Indicates the current "checked" state of checkboxes, radio buttons, and other widgets.
fn aria_checked(
self,
value: V,
@ -46,6 +52,7 @@ where
self.add_any_attr(aria_checked(value))
}
/// Defines the number of columns in a table, grid, or treegrid.
fn aria_colcount(
self,
value: V,
@ -53,6 +60,7 @@ where
self.add_any_attr(aria_colcount(value))
}
/// Defines an element's column index or position with respect to the total number of columns within a table, grid, or treegrid.
fn aria_colindex(
self,
value: V,
@ -60,6 +68,7 @@ where
self.add_any_attr(aria_colindex(value))
}
/// Defines the number of columns spanned by a cell or gridcell within a table, grid, or treegrid.
fn aria_colspan(
self,
value: V,
@ -67,6 +76,7 @@ where
self.add_any_attr(aria_colspan(value))
}
/// Identifies the element (or elements) whose contents or presence are controlled by the current element.
fn aria_controls(
self,
value: V,
@ -74,6 +84,7 @@ where
self.add_any_attr(aria_controls(value))
}
/// Indicates the element that represents the current item within a container or set of related elements.
fn aria_current(
self,
value: V,
@ -81,6 +92,7 @@ where
self.add_any_attr(aria_current(value))
}
/// Identifies the element (or elements) that describes the object.
fn aria_describedby(
self,
value: V,
@ -89,6 +101,7 @@ where
self.add_any_attr(aria_describedby(value))
}
/// Defines a string value that describes or annotates the current element.
fn aria_description(
self,
value: V,
@ -97,6 +110,7 @@ where
self.add_any_attr(aria_description(value))
}
/// Identifies the element that provides additional information related to the object.
fn aria_details(
self,
value: V,
@ -104,6 +118,7 @@ where
self.add_any_attr(aria_details(value))
}
/// Indicates that the element is perceivable but disabled, so it is not editable or otherwise operable.
fn aria_disabled(
self,
value: V,
@ -111,6 +126,7 @@ where
self.add_any_attr(aria_disabled(value))
}
/// Indicates what functions can be performed when a dragged object is released on the drop target.
fn aria_dropeffect(
self,
value: V,
@ -118,6 +134,7 @@ where
self.add_any_attr(aria_dropeffect(value))
}
/// Defines the element that provides an error message related to the object.
fn aria_errormessage(
self,
value: V,
@ -126,6 +143,7 @@ where
self.add_any_attr(aria_errormessage(value))
}
/// Indicates whether the element, or another grouping element it controls, is currently expanded or collapsed.
fn aria_expanded(
self,
value: V,
@ -133,6 +151,7 @@ where
self.add_any_attr(aria_expanded(value))
}
/// Identifies the next element (or elements) in an alternate reading order of content.
fn aria_flowto(
self,
value: V,
@ -140,6 +159,7 @@ where
self.add_any_attr(aria_flowto(value))
}
/// Indicates an element's "grabbed" state in a drag-and-drop operation.
fn aria_grabbed(
self,
value: V,
@ -147,6 +167,7 @@ where
self.add_any_attr(aria_grabbed(value))
}
/// Indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by an element.
fn aria_haspopup(
self,
value: V,
@ -154,6 +175,7 @@ where
self.add_any_attr(aria_haspopup(value))
}
/// Indicates whether the element is exposed to an accessibility API.
fn aria_hidden(
self,
value: V,
@ -161,6 +183,7 @@ where
self.add_any_attr(aria_hidden(value))
}
/// Indicates the entered value does not conform to the format expected by the application.
fn aria_invalid(
self,
value: V,
@ -168,6 +191,7 @@ where
self.add_any_attr(aria_invalid(value))
}
/// Indicates keyboard shortcuts that an author has implemented to activate or give focus to an element.
fn aria_keyshortcuts(
self,
value: V,
@ -176,6 +200,7 @@ where
self.add_any_attr(aria_keyshortcuts(value))
}
/// Defines a string value that labels the current element.
fn aria_label(
self,
value: V,
@ -183,6 +208,7 @@ where
self.add_any_attr(aria_label(value))
}
/// Identifies the element (or elements) that labels the current element.
fn aria_labelledby(
self,
value: V,
@ -190,6 +216,7 @@ where
self.add_any_attr(aria_labelledby(value))
}
/// Indicates that an element will be updated, and describes the types of updates the user agents, assistive technologies, and user can expect from the live region.
fn aria_live(
self,
value: V,
@ -197,6 +224,7 @@ where
self.add_any_attr(aria_live(value))
}
/// Indicates whether an element is modal when displayed.
fn aria_modal(
self,
value: V,
@ -204,6 +232,7 @@ where
self.add_any_attr(aria_modal(value))
}
/// Indicates whether a text box accepts multiple lines of input or only a single line.
fn aria_multiline(
self,
value: V,
@ -211,6 +240,7 @@ where
self.add_any_attr(aria_multiline(value))
}
/// Indicates that the user may select more than one item from the current selectable descendants.
fn aria_multiselectable(
self,
value: V,
@ -219,6 +249,7 @@ where
self.add_any_attr(aria_multiselectable(value))
}
/// Indicates whether the element's orientation is horizontal, vertical, or undefined.
fn aria_orientation(
self,
value: V,
@ -227,6 +258,7 @@ where
self.add_any_attr(aria_orientation(value))
}
/// Identifies an element (or elements) in order to define a visual, functional, or contextual parent/child relationship between DOM elements where the DOM hierarchy cannot be used to represent the relationship.
fn aria_owns(
self,
value: V,
@ -234,6 +266,7 @@ where
self.add_any_attr(aria_owns(value))
}
/// Defines a short hint (a word or short phrase) intended to help the user with data entry when the control has no value.
fn aria_placeholder(
self,
value: V,
@ -242,6 +275,7 @@ where
self.add_any_attr(aria_placeholder(value))
}
/// Defines an element's number or position in the current set of listitems or treeitems.
fn aria_posinset(
self,
value: V,
@ -249,6 +283,7 @@ where
self.add_any_attr(aria_posinset(value))
}
/// Indicates the current "pressed" state of toggle buttons.
fn aria_pressed(
self,
value: V,
@ -256,6 +291,7 @@ where
self.add_any_attr(aria_pressed(value))
}
/// Indicates that the element is not editable, but is otherwise operable.
fn aria_readonly(
self,
value: V,
@ -263,6 +299,7 @@ where
self.add_any_attr(aria_readonly(value))
}
/// Indicates what notifications the user agent will trigger when the accessibility tree within a live region is modified.
fn aria_relevant(
self,
value: V,
@ -270,6 +307,7 @@ where
self.add_any_attr(aria_relevant(value))
}
/// Indicates that user input is required on the element before a form may be submitted.
fn aria_required(
self,
value: V,
@ -277,6 +315,7 @@ where
self.add_any_attr(aria_required(value))
}
/// Defines a human-readable, author-localized description for the role of an element.
fn aria_roledescription(
self,
value: V,
@ -285,6 +324,7 @@ where
self.add_any_attr(aria_roledescription(value))
}
/// Defines the total number of rows in a table, grid, or treegrid.
fn aria_rowcount(
self,
value: V,
@ -292,6 +332,7 @@ where
self.add_any_attr(aria_rowcount(value))
}
/// Defines an element's row index or position with respect to the total number of rows within a table, grid, or treegrid.
fn aria_rowindex(
self,
value: V,
@ -299,6 +340,7 @@ where
self.add_any_attr(aria_rowindex(value))
}
/// Defines the number of rows spanned by a cell or gridcell within a table, grid, or treegrid.
fn aria_rowspan(
self,
value: V,
@ -306,6 +348,7 @@ where
self.add_any_attr(aria_rowspan(value))
}
/// Indicates the current "selected" state of various widgets.
fn aria_selected(
self,
value: V,
@ -313,6 +356,7 @@ where
self.add_any_attr(aria_selected(value))
}
/// Defines the number of items in the current set of listitems or treeitems.
fn aria_setsize(
self,
value: V,
@ -320,6 +364,7 @@ where
self.add_any_attr(aria_setsize(value))
}
/// Indicates if items in a table or grid are sorted in ascending or descending order.
fn aria_sort(
self,
value: V,
@ -327,6 +372,7 @@ where
self.add_any_attr(aria_sort(value))
}
/// Defines the maximum allowed value for a range widget.
fn aria_valuemax(
self,
value: V,
@ -334,6 +380,7 @@ where
self.add_any_attr(aria_valuemax(value))
}
/// Defines the minimum allowed value for a range widget.
fn aria_valuemin(
self,
value: V,
@ -341,6 +388,7 @@ where
self.add_any_attr(aria_valuemin(value))
}
/// Defines the current value for a range widget.
fn aria_valuenow(
self,
value: V,
@ -348,6 +396,7 @@ where
self.add_any_attr(aria_valuenow(value))
}
/// Defines the human-readable text alternative of `aria-valuenow` for a range widget.
fn aria_valuetext(
self,
value: V,

View file

@ -6,6 +6,7 @@ use crate::{
};
use std::{borrow::Cow, marker::PhantomData, sync::Arc};
/// Adds a custom attribute with any key-value combintion.
#[inline(always)]
pub fn custom_attribute<K, V, R>(key: K, value: V) -> CustomAttr<K, V, R>
where
@ -20,6 +21,7 @@ where
}
}
/// A custom attribute with any key-value combination.
#[derive(Debug)]
pub struct CustomAttr<K, V, R>
where
@ -154,7 +156,9 @@ where
}
// TODO this needs to be a method, not a const
/// Defines a custom attribute key.
pub trait CustomAttributeKey: Clone + AsRef<str> + Send + 'static {
/// The attribute name.
const KEY: &'static str;
}
@ -181,6 +185,7 @@ impl<const K: &'static str> CustomAttributeKey
const KEY: &'static str = K;
}
/// Adds a custom attribute to an element.
pub trait CustomAttribute<K, V, Rndr>
where
K: CustomAttributeKey,
@ -188,6 +193,7 @@ where
Rndr: DomRenderer,
Self: Sized + AddAnyAttr<Rndr>,
{
/// Adds an HTML attribute by key and value.
fn attr(
self,
key: K,

View file

@ -13,13 +13,16 @@ use crate::{
};
use core::convert::From;
/// Adds an attribute that modifies the `class`.
pub trait ClassAttribute<C, Rndr>
where
C: IntoClass<Rndr>,
Rndr: DomRenderer,
{
/// The type of the element with the new attribute added.
type Output;
/// Adds a CSS class to an element.
fn class(self, value: C) -> Self::Output;
}
@ -36,13 +39,16 @@ where
}
}
/// Adds an attribute that modifies the DOM properties.
pub trait PropAttribute<K, P, Rndr>
where
P: IntoProperty<Rndr>,
Rndr: DomRenderer,
{
/// The type of the element with the new attribute added.
type Output;
/// Adds a DOM property to an element.
fn prop(self, key: K, value: P) -> Self::Output;
}
@ -54,18 +60,22 @@ where
Rndr: DomRenderer,
{
type Output = <Self as AddAnyAttr<Rndr>>::Output<Property<K, P, Rndr>>;
fn prop(self, key: K, value: P) -> Self::Output {
self.add_any_attr(prop(key, value))
}
}
/// Adds an attribute that modifies the CSS styles.
pub trait StyleAttribute<S, Rndr>
where
S: IntoStyle<Rndr>,
Rndr: DomRenderer,
{
/// The type of the element with the new attribute added.
type Output;
/// Adds a CSS style to an element.
fn style(self, value: S) -> Self::Output;
}
@ -82,9 +92,12 @@ where
}
}
/// Adds an event listener to an element definition.
pub trait OnAttribute<E, F, Rndr> {
/// The type of the element with the event listener added.
type Output;
/// Adds an event listener to an element.
fn on(self, event: E, cb: F) -> Self::Output;
}
@ -104,9 +117,12 @@ where
}
}
/// Adds an event listener with a typed target to an element definition.
pub trait OnTargetAttribute<E, F, T, Rndr> {
/// The type of the element with the new attribute added.
type Output;
/// Adds an event listener with a typed target to an element definition.
fn on_target(self, event: E, cb: F) -> Self::Output;
}
@ -130,12 +146,14 @@ where
}
}
/// Global attributes can be added to any HTML element.
pub trait GlobalAttributes<Rndr, V>
where
Self: Sized + AddAnyAttr<Rndr>,
V: AttributeValue<Rndr>,
Rndr: Renderer,
{
/// The `accesskey` global attribute provides a hint for generating a keyboard shortcut for the current element.
fn accesskey(
self,
value: V,
@ -143,6 +161,7 @@ where
self.add_any_attr(accesskey(value))
}
/// The `autocapitalize` global attribute controls whether and how text input is automatically capitalized as it is entered/edited by the user.
fn autocapitalize(
self,
value: V,
@ -150,6 +169,7 @@ where
self.add_any_attr(autocapitalize(value))
}
/// The `autofocus` global attribute is a Boolean attribute indicating that an element should receive focus as soon as the page is loaded.
fn autofocus(
self,
value: V,
@ -157,6 +177,7 @@ where
self.add_any_attr(autofocus(value))
}
/// The `contenteditable` global attribute is an enumerated attribute indicating if the element should be editable by the user.
fn contenteditable(
self,
value: V,
@ -165,6 +186,7 @@ where
self.add_any_attr(contenteditable(value))
}
/// The `dir` global attribute is an enumerated attribute indicating the directionality of the element's text.
fn dir(
self,
value: V,
@ -172,6 +194,7 @@ where
self.add_any_attr(dir(value))
}
/// The `draggable` global attribute is an enumerated attribute indicating whether the element can be dragged.
fn draggable(
self,
value: V,
@ -179,6 +202,7 @@ where
self.add_any_attr(draggable(value))
}
/// The `enterkeyhint` global attribute is used to customize the enter key on virtual keyboards.
fn enterkeyhint(
self,
value: V,
@ -186,6 +210,7 @@ where
self.add_any_attr(enterkeyhint(value))
}
/// The `hidden` global attribute is a Boolean attribute indicating that the element is not yet, or is no longer, relevant.
fn hidden(
self,
value: V,
@ -193,6 +218,7 @@ where
self.add_any_attr(hidden(value))
}
/// The `id` global attribute defines a unique identifier (ID) which must be unique in the whole document.
fn id(
self,
value: V,
@ -200,6 +226,7 @@ where
self.add_any_attr(id(value))
}
/// The `inert` global attribute is a Boolean attribute that makes an element behave inertly.
fn inert(
self,
value: V,
@ -207,6 +234,7 @@ where
self.add_any_attr(inert(value))
}
/// The `inputmode` global attribute provides a hint to browsers for which virtual keyboard to display.
fn inputmode(
self,
value: V,
@ -214,6 +242,7 @@ where
self.add_any_attr(inputmode(value))
}
/// The `is` global attribute allows you to specify that a standard HTML element should behave like a custom built-in element.
fn is(
self,
value: V,
@ -221,6 +250,7 @@ where
self.add_any_attr(is(value))
}
/// The `itemid` global attribute is used to specify the unique, global identifier of an item.
fn itemid(
self,
value: V,
@ -228,6 +258,7 @@ where
self.add_any_attr(itemid(value))
}
/// The `itemprop` global attribute is used to add properties to an item.
fn itemprop(
self,
value: V,
@ -235,6 +266,7 @@ where
self.add_any_attr(itemprop(value))
}
/// The `itemref` global attribute is used to refer to other elements.
fn itemref(
self,
value: V,
@ -242,6 +274,7 @@ where
self.add_any_attr(itemref(value))
}
/// The `itemscope` global attribute is used to create a new item.
fn itemscope(
self,
value: V,
@ -249,6 +282,7 @@ where
self.add_any_attr(itemscope(value))
}
/// The `itemtype` global attribute is used to specify the types of items.
fn itemtype(
self,
value: V,
@ -256,6 +290,7 @@ where
self.add_any_attr(itemtype(value))
}
/// The `lang` global attribute helps define the language of an element.
fn lang(
self,
value: V,
@ -263,6 +298,7 @@ where
self.add_any_attr(lang(value))
}
/// The `nonce` global attribute is used to specify a cryptographic nonce.
fn nonce(
self,
value: V,
@ -270,6 +306,7 @@ where
self.add_any_attr(nonce(value))
}
/// The `part` global attribute identifies the element as a part of a component.
fn part(
self,
value: V,
@ -277,6 +314,7 @@ where
self.add_any_attr(part(value))
}
/// The `popover` global attribute defines the popover's behavior.
fn popover(
self,
value: V,
@ -284,6 +322,7 @@ where
self.add_any_attr(popover(value))
}
/// The `role` global attribute defines the role of an element in ARIA.
fn role(
self,
value: V,
@ -291,6 +330,7 @@ where
self.add_any_attr(role(value))
}
/// The `slot` global attribute assigns a slot in a shadow DOM.
fn slot(
self,
value: V,
@ -298,6 +338,7 @@ where
self.add_any_attr(slot(value))
}
/// The `spellcheck` global attribute is an enumerated attribute that defines whether the element may be checked for spelling errors.
fn spellcheck(
self,
value: V,
@ -305,6 +346,7 @@ where
self.add_any_attr(spellcheck(value))
}
/// The `tabindex` global attribute indicates if the element can take input focus.
fn tabindex(
self,
value: V,
@ -312,6 +354,7 @@ where
self.add_any_attr(tabindex(value))
}
/// The `title` global attribute contains text representing advisory information.
fn title(
self,
value: V,
@ -319,6 +362,7 @@ where
self.add_any_attr(title(value))
}
/// The `translate` global attribute is an enumerated attribute that specifies whether an element's attribute values and text content should be translated when the page is localized.
fn translate(
self,
value: V,
@ -326,6 +370,7 @@ where
self.add_any_attr(translate(value))
}
/// The `virtualkeyboardpolicy` global attribute specifies the behavior of the virtual keyboard.
fn virtualkeyboardpolicy(
self,
value: V,

View file

@ -2,14 +2,18 @@ use super::{Attr, AttributeValue};
use crate::renderer::Renderer;
use std::{fmt::Debug, marker::PhantomData};
/// An HTML attribute key.
pub trait AttributeKey: Clone + Send + 'static {
/// The name of the attribute.
const KEY: &'static str;
}
macro_rules! attributes {
($($key:ident $html:literal),* $(,)?) => {
($(#[$meta:meta] $key:ident $html:literal),* $(,)?) => {
paste::paste! {
$(
#[$meta]
#[track_caller]
pub fn $key<V, Rndr>(value: V) -> Attr<[<$key:camel>], V, Rndr>
where V: AttributeValue<Rndr>,
Rndr: Renderer
@ -17,6 +21,7 @@ macro_rules! attributes {
Attr([<$key:camel>], value, PhantomData)
}
#[$meta]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct [<$key:camel>];
@ -28,311 +33,618 @@ macro_rules! attributes {
}
}
// TODO attribute names with underscores should be kebab-cased
attributes! {
// HTML
abbr "abbr", // [],
accept_charset "accept-charset", // [],
accept "accept", // [],
accesskey "accesskey", // [], // [GlobalAttribute]
action "action", // [],
align "align", // [],
allow "allow", // [],
allowfullscreen "allowfullscreen", // [],
allowpaymentrequest "allowpaymentrequest", // [],
alt "alt", // [],
/// The `abbr` attribute specifies an abbreviated form of the element's content.
abbr "abbr",
/// The `accept-charset` attribute specifies the character encodings that are to be used for the form submission.
accept_charset "accept-charset",
/// The `accept` attribute specifies a list of types the server accepts, typically a file type.
accept "accept",
/// The `accesskey` attribute specifies a shortcut key to activate or focus an element.
accesskey "accesskey",
/// The `action` attribute defines the URL to which the form data will be sent.
action "action",
/// The `align` attribute specifies the alignment of an element.
align "align",
/// The `allow` attribute defines a feature policy for the content in an iframe.
allow "allow",
/// The `allowfullscreen` attribute allows the iframe to be displayed in fullscreen mode.
allowfullscreen "allowfullscreen",
/// The `allowpaymentrequest` attribute allows a cross-origin iframe to invoke the Payment Request API.
allowpaymentrequest "allowpaymentrequest",
/// The `alt` attribute provides alternative text for an image, if the image cannot be displayed.
alt "alt",
// ARIA
/// The `aria-activedescendant` attribute identifies the currently active element when DOM focus is on a composite widget, textbox, group, or application.
aria_activedescendant "aria-activedescendant",
aria_atomic "aria-atomic", // [], // [GlobalAttribute] // [AriaAttribute],
/// The `aria-atomic` attribute indicates whether assistive technologies will present all, or only parts of, the changed region based on the change notifications defined by the aria-relevant attribute.
aria_atomic "aria-atomic",
/// The `aria-autocomplete` attribute indicates whether user input completion suggestions are provided.
aria_autocomplete "aria-autocomplete",
aria_busy "aria-busy", // [], // [GlobalAttribute] // [AriaAttribute],
/// The `aria-busy` attribute indicates whether an element, and its subtree, are currently being updated.
aria_busy "aria-busy",
/// The `aria-checked` attribute indicates the current "checked" state of checkboxes, radio buttons, and other widgets.
aria_checked "aria-checked",
/// The `aria-colcount` attribute defines the total number of columns in a table, grid, or treegrid.
aria_colcount "aria-colcount",
/// The `aria-colindex` attribute defines an element's column index or position with respect to the total number of columns within a table, grid, or treegrid.
aria_colindex "aria-colindex",
/// The `aria-colspan` attribute defines the number of columns spanned by a cell or gridcell within a table, grid, or treegrid.
aria_colspan "aria-colspan",
aria_controls "aria-controls", // [], // [GlobalAttribute] // [AriaAttribute],
aria_current "aria-current", // [], // [GlobalAttribute] // [AriaAttribute],
aria_describedby "aria-describedby", // [], // [GlobalAttribute] // [AriaAttribute],
aria_description "aria-description", // [], // [GlobalAttribute] // [AriaAttribute],
aria_details "aria-details", // [], // [GlobalAttribute] // [AriaAttribute],
aria_disabled "aria-disabled", // [], // [GlobalAttribute] // [AriaAttribute],
aria_dropeffect "aria-dropeffect", // [], // [GlobalAttribute] // [AriaAttribute],
aria_errormessage "aria-errormessage", // [], // [GlobalAttribute] // [AriaAttribute],
/// The `aria-controls` attribute identifies the element (or elements) whose contents or presence are controlled by the current element.
aria_controls "aria-controls",
/// The `aria-current` attribute indicates the element representing the current item within a container or set of related elements.
aria_current "aria-current",
/// The `aria-describedby` attribute identifies the element (or elements) that describes the object.
aria_describedby "aria-describedby",
/// The `aria-description` attribute provides a string value that describes or annotates the current element.
aria_description "aria-description",
/// The `aria-details` attribute identifies the element that provides a detailed, extended description for the object.
aria_details "aria-details",
/// The `aria-disabled` attribute indicates that the element is perceivable but disabled, so it is not editable or otherwise operable.
aria_disabled "aria-disabled",
/// The `aria-dropeffect` attribute indicates what functions can be performed when a dragged object is released on the drop target.
aria_dropeffect "aria-dropeffect",
/// The `aria-errormessage` attribute identifies the element that provides an error message for the object.
aria_errormessage "aria-errormessage",
/// The `aria-expanded` attribute indicates whether an element, or another grouping element it controls, is currently expanded or collapsed.
aria_expanded "aria-expanded",
aria_flowto "aria-flowto", // [], // [GlobalAttribute] // [AriaAttribute],
aria_grabbed "aria-grabbed", // [], // [GlobalAttribute] // [AriaAttribute],
aria_haspopup "aria-haspopup", // [], // [GlobalAttribute] // [AriaAttribute],
aria_hidden "aria-hidden", // [], // [GlobalAttribute] // [AriaAttribute],
aria_invalid "aria-invalid", // [], // [GlobalAttribute] // [AriaAttribute],
aria_keyshortcuts "aria-keyshortcuts", // [], // [GlobalAttribute] // [AriaAttribute],
aria_label "aria-label", // [], // [GlobalAttribute] // [AriaAttribute],
aria_labelledby "aria-labelledby", // [], // [GlobalAttribute] // [AriaAttribute],
aria_live "aria-live", // [], // [GlobalAttribute] // [AriaAttribute],
/// The `aria-flowto` attribute identifies the next element (or elements) in an alternate reading order of content.
aria_flowto "aria-flowto",
/// The `aria-grabbed` attribute indicates an element's "grabbed" state in a drag-and-drop operation.
aria_grabbed "aria-grabbed",
/// The `aria-haspopup` attribute indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by an element.
aria_haspopup "aria-haspopup",
/// The `aria-hidden` attribute indicates whether the element is exposed to an accessibility API.
aria_hidden "aria-hidden",
/// The `aria-invalid` attribute indicates the entered value does not conform to the format expected by the application.
aria_invalid "aria-invalid",
/// The `aria-keyshortcuts` attribute indicates keyboard shortcuts that an author has implemented to activate or give focus to an element.
aria_keyshortcuts "aria-keyshortcuts",
/// The `aria-label` attribute defines a string value that labels the current element.
aria_label "aria-label",
/// The `aria-labelledby` attribute identifies the element (or elements) that labels the current element.
aria_labelledby "aria-labelledby",
/// The `aria-live` attribute indicates that an element will be updated, and describes the types of updates the user agents, assistive technologies, and user can expect from the live region.
aria_live "aria-live",
/// The `aria-modal` attribute indicates whether an element is modal when displayed.
aria_modal "aria-modal",
/// The `aria-multiline` attribute indicates whether a text box accepts multiple lines of input or only a single line.
aria_multiline "aria-multiline",
/// The `aria-multiselectable` attribute indicates that the user may select more than one item from the current selectable descendants.
aria_multiselectable "aria-multiselectable",
/// The `aria-orientation` attribute indicates whether the element's orientation is horizontal, vertical, or unknown/ambiguous.
aria_orientation "aria-orientation",
aria_owns "aria-owns", // [], // [GlobalAttribute] // [AriaAttribute],
/// The `aria-owns` attribute identifies an element (or elements) in order to define a relationship between the element with `aria-owns` and the target element.
aria_owns "aria-owns",
/// The `aria-placeholder` attribute defines a short hint (a word or short phrase) intended to aid the user with data entry when the control has no value.
aria_placeholder "aria-placeholder",
/// The `aria-posinset` attribute defines an element's position within a set or treegrid.
aria_posinset "aria-posinset",
/// The `aria-pressed` attribute indicates the current "pressed" state of toggle buttons.
aria_pressed "aria-pressed",
/// The `aria-readonly` attribute indicates that the element is not editable, but is otherwise operable.
aria_readonly "aria-readonly",
aria_relevant "aria-relevant", // [], // [GlobalAttribute] // [AriaAttribute],
/// The `aria-relevant` attribute indicates what user agent changes to the accessibility tree should be monitored.
aria_relevant "aria-relevant",
/// The `aria-required` attribute indicates that user input is required on the element before a form may be submitted.
aria_required "aria-required",
aria_roledescription "aria-roledescription", // [], // [GlobalAttribute] // [AriaAttribute],
/// The `aria-roledescription` attribute defines a human-readable, author-localized description for the role of an element.
aria_roledescription "aria-roledescription",
/// The `aria-rowcount` attribute defines the total number of rows in a table, grid, or treegrid.
aria_rowcount "aria-rowcount",
/// The `aria-rowindex` attribute defines an element's row index or position with respect to the total number of rows within a table, grid, or treegrid.
aria_rowindex "aria-rowindex",
/// The `aria-rowspan` attribute defines the number of rows spanned by a cell or gridcell within a table, grid, or treegrid.
aria_rowspan "aria-rowspan",
/// The `aria-selected` attribute indicates the current "selected" state of various widgets.
aria_selected "aria-selected",
/// The `aria-setsize` attribute defines the number of items in the current set of listitems or treeitems.
aria_setsize "aria-setsize",
/// The `aria-sort` attribute indicates if items in a table or grid are sorted in ascending or descending order.
aria_sort "aria-sort",
/// The `aria-valuemax` attribute defines the maximum allowed value for a range widget.
aria_valuemax "aria-valuemax",
/// The `aria-valuemin` attribute defines the minimum allowed value for a range widget.
aria_valuemin "aria-valuemin",
/// The `aria-valuenow` attribute defines the current value for a range widget.
aria_valuenow "aria-valuenow",
/// The `aria-valuetext` attribute defines the human-readable text alternative of aria-valuenow for a range widget.
aria_valuetext "aria-valuetext",
r#as "as", // [],
r#async "async", // [],
autocapitalize "autocapitalize", // [], // [GlobalAttribute]
autocomplete "autocomplete", // [],
autofocus "autofocus", // [], // [GlobalAttribute]
autoplay "autoplay", // [],
background "background", // [],
bgcolor "bgcolor", // [],
blocking "blocking", // [],
border "border", // [],
buffered "buffered", // [],
capture "capture", // [],
challenge "challenge", // [],
charset "charset", // [],
checked "checked", // [],
cite "cite", // [],
/// The `as` attribute specifies the type of destination for the content of the link.
r#as "as",
/// The `async` attribute indicates that the script should be executed asynchronously.
r#async "async",
/// The `autocapitalize` attribute controls whether and how text input is automatically capitalized as it is entered/edited by the user.
autocapitalize "autocapitalize",
/// The `autocomplete` attribute indicates whether an input field can have its value automatically completed by the browser.
autocomplete "autocomplete",
/// The `autofocus` attribute indicates that an element should be focused on page load.
autofocus "autofocus",
/// The `autoplay` attribute indicates that the media should start playing as soon as it is loaded.
autoplay "autoplay",
/// The `background` attribute sets the URL of the background image for the document.
background "background",
/// The `bgcolor` attribute sets the background color of an element.
bgcolor "bgcolor",
/// The `blocking` attribute indicates that the script will block the page loading until it is executed.
blocking "blocking",
/// The `border` attribute sets the width of an element's border.
border "border",
/// The `buffered` attribute contains the time ranges that the media has been buffered.
buffered "buffered",
/// The `capture` attribute indicates that the user must capture media using a camera or microphone instead of selecting a file from the file picker.
capture "capture",
/// The `challenge` attribute specifies the challenge string that is paired with the keygen element.
challenge "challenge",
/// The `charset` attribute specifies the character encoding of the HTML document.
charset "charset",
/// The `checked` attribute indicates whether an input element is checked or not.
checked "checked",
/// The `cite` attribute contains a URL that points to the source of the quotation or change.
cite "cite",
// class is handled in ../class.rs instead
//class "class", // [],
code "code", // [],
color "color", // [],
cols "cols", // [],
colspan "colspan", // [],
content "content", // [],
contenteditable "contenteditable", // [], // [GlobalAttribute]
contextmenu "contextmenu", // [], // [GlobalAttribute]
controls "controls", // [],
controlslist "controlslist", // [],
coords "coords", // [],
crossorigin "crossorigin", // [],
csp "csp", // [],
data "data", // [],
datetime "datetime", // [],
decoding "decoding", // [],
default "default", // [],
defer "defer", // [],
dir "dir", // [], // [GlobalAttribute]
dirname "dirname", // [],
disabled "disabled", // [],
disablepictureinpicture "disablepictureinpicture", // [],
disableremoteplayback "disableremoteplayback", // [],
download "download", // [],
draggable "draggable", // [], // [GlobalAttribute]
enctype "enctype", // [],
enterkeyhint "enterkeyhint", // [], // [GlobalAttribute]
exportparts "exportparts", // [], // [GlobalAttribute]
fetchpriority "fetchprioty", // [],
r#for "for", // [],
form "form", // [],
formaction "formaction", // [],
formenctype "formenctype", // [],
formmethod "formmethod", // [],
formnovalidate "formnovalidate", // [],
formtarget "formtarget", // [],
headers "headers", // [],
height "height", // [],
hidden "hidden", // [], // [GlobalAttribute]
high "high", // [],
href "href", // [],
hreflang "hreflang", // [],
http_equiv "http-equiv", // [],
icon "icon", // [],
id "id", // [], // [GlobalAttribute]
//class "class",
/// The `code` attribute specifies the URL of the applet's class file to be loaded and executed.
code "code",
/// The `color` attribute specifies the color of an element's text.
color "color",
/// The `cols` attribute specifies the visible width of a text area.
cols "cols",
/// The `colspan` attribute defines the number of columns a cell should span.
colspan "colspan",
/// The `content` attribute gives the value associated with the http-equiv or name attribute.
content "content",
/// The `contenteditable` attribute indicates whether the element's content is editable.
contenteditable "contenteditable",
/// The `contextmenu` attribute specifies the ID of a `<menu>` element to open as a context menu.
contextmenu "contextmenu",
/// The `controls` attribute indicates whether the browser should display playback controls for the media.
controls "controls",
/// The `controlslist` attribute allows the control of which controls to show on the media element whenever the browser shows its native controls.
controlslist "controlslist",
/// The `coords` attribute specifies the coordinates of an area in an image map.
coords "coords",
/// The `crossorigin` attribute indicates whether the resource should be fetched with a CORS request.
crossorigin "crossorigin",
/// The `csp` attribute allows the embedding document to define the Content Security Policy that an embedded document must agree to enforce upon itself.
csp "csp",
/// The `data` attribute specifies the URL of the resource that is being embedded.
data "data",
/// The `datetime` attribute specifies the date and time.
datetime "datetime",
/// The `decoding` attribute indicates the preferred method for decoding images.
decoding "decoding",
/// The `default` attribute indicates that the track should be enabled unless the user's preferences indicate that another track is more appropriate.
default "default",
/// The `defer` attribute indicates that the script should be executed after the document has been parsed.
defer "defer",
/// The `dir` attribute specifies the text direction for the content in an element.
dir "dir",
/// The `dirname` attribute identifies the text directionality of an input element.
dirname "dirname",
/// The `disabled` attribute indicates whether the element is disabled.
disabled "disabled",
/// The `disablepictureinpicture` attribute indicates that the element is not allowed to be displayed in Picture-in-Picture mode.
disablepictureinpicture "disablepictureinpicture",
/// The `disableremoteplayback` attribute indicates that the element is not allowed to be displayed using remote playback.
disableremoteplayback "disableremoteplayback",
/// The `download` attribute indicates that the linked resource is intended to be downloaded rather than displayed in the browser.
download "download",
/// The `draggable` attribute indicates whether the element is draggable.
draggable "draggable",
/// The `enctype` attribute specifies the MIME type of the form submission.
enctype "enctype",
/// The `enterkeyhint` attribute allows authors to specify what kind of action label or icon will be presented to users in a virtual keyboard's enter key.
enterkeyhint "enterkeyhint",
/// The `exportparts` attribute enables the sharing of parts of an element's shadow DOM with a containing document.
exportparts "exportparts",
/// The `fetchpriority` attribute allows developers to specify the priority of a resource fetch request.
fetchpriority "fetchpriority",
/// The `for` attribute specifies which form element a label is bound to.
r#for "for",
/// The `form` attribute associates the element with a form element.
form "form",
/// The `formaction` attribute specifies the URL that processes the form submission.
formaction "formaction",
/// The `formenctype` attribute specifies how the form data should be encoded when submitted.
formenctype "formenctype",
/// The `formmethod` attribute specifies the HTTP method to use when submitting the form.
formmethod "formmethod",
/// The `formnovalidate` attribute indicates that the form should not be validated when submitted.
formnovalidate "formnovalidate",
/// The `formtarget` attribute specifies where to display the response after submitting the form.
formtarget "formtarget",
/// The `headers` attribute specifies the headers associated with the element.
headers "headers",
/// The `height` attribute specifies the height of an element.
height "height",
/// The `hidden` attribute indicates that the element is not yet, or is no longer, relevant.
hidden "hidden",
/// The `high` attribute specifies the range that is considered to be a high value.
high "high",
/// The `href` attribute specifies the URL of a linked resource.
href "href",
/// The `hreflang` attribute specifies the language of the linked resource.
hreflang "hreflang",
/// The `http-equiv` attribute provides an HTTP header for the information/value of the content attribute.
http_equiv "http-equiv",
/// The `icon` attribute specifies the URL of an image to be used as a graphical icon for the element.
icon "icon",
/// The `id` attribute specifies a unique id for an element.
id "id",
/// The `imagesizes` attribute specifies image sizes for different page layouts.
imagesizes "imagesizes",
/// The `imagesrcset` attribute specifies the URLs of multiple images to be used in different situations.
imagesrcset "imagesrcset",
importance "importance", // [],
inert "inert", // [], // [GlobalAttribute]
inputmode "inputmode", // [], // [GlobalAttribute]
integrity "integrity", // [],
intrinsicsize "intrinsicsize", // [],
is "is", // [], // [GlobalAttribute]
ismap "ismap", // [],
itemid "itemid", // [], // [GlobalAttribute]
itemprop "itemprop", // [], // [GlobalAttribute]
itemref "itemref", // [], // [GlobalAttribute]
itemscope "itemscope", // [], // [GlobalAttribute]
itemtype "itemtype", // [], // [GlobalAttribute]
keytype "keytype", // [],
kind "kind", // [],
label "label", // [],
lang "lang", // [], // [GlobalAttribute]
language "language", // [],
list "list", // [],
loading "loading", // [],
r#loop "loop", // [],
low "low", // [],
manifest "manifest", // [],
max "max", // [],
maxlength "maxlength", // [],
media "media", // [],
method "method", // [],
min "min", // [],
minlength "minlength", // [],
multiple "multiple", // [],
muted "muted", // [],
name "name", // [],
nomodule "nomodule", // [],
nonce "nonce", // [], // [GlobalAttribute]
novalidate "novalidate", // [],
open "open", // [],
optimum "optimum", // [],
part "part", // [], // [GlobalAttribute]
pattern "pattern", // [],
ping "ping", // [],
placeholder "placeholder", // [],
playsinline "playsinline", // [],
popover "popover", // [], // [GlobalAttribute]
/// The `importance` attribute specifies the relative importance of the element.
importance "importance",
/// The `inert` attribute indicates that the element is non-interactive and won't be accessible to user interactions or assistive technologies.
inert "inert",
/// The `inputmode` attribute specifies the type of data that the user will enter.
inputmode "inputmode",
/// The `integrity` attribute contains a hash value that the browser can use to verify that the resource hasn't been altered.
integrity "integrity",
/// The `intrinsicsize` attribute specifies the intrinsic size of an image or video.
intrinsicsize "intrinsicsize",
/// The `is` attribute allows you to specify the name of a custom element.
is "is",
/// The `ismap` attribute indicates that the image is part of a server-side image map.
ismap "ismap",
/// The `itemid` attribute assigns a unique identifier to an item.
itemid "itemid",
/// The `itemprop` attribute adds a property to an item.
itemprop "itemprop",
/// The `itemref` attribute provides a list of element IDs that have additional properties for the item.
itemref "itemref",
/// The `itemscope` attribute creates a new item and adds it to the page's items.
itemscope "itemscope",
/// The `itemtype` attribute specifies the type of an item.
itemtype "itemtype",
/// The `keytype` attribute specifies the type of key used by the `<keygen>` element.
keytype "keytype",
/// The `kind` attribute specifies the kind of text track.
kind "kind",
/// The `label` attribute provides a user-readable title for an element.
label "label",
/// The `lang` attribute specifies the language of the element's content.
lang "lang",
/// The `language` attribute specifies the scripting language used for the script.
language "language",
/// The `list` attribute identifies a `<datalist>` element that contains pre-defined options for an `<input>` element.
list "list",
/// The `loading` attribute indicates how the browser should load the image.
loading "loading",
/// The `loop` attribute indicates whether the media should start over again when it reaches the end.
r#loop "loop",
/// The `low` attribute specifies the range that is considered to be a low value.
low "low",
/// The `manifest` attribute specifies the URL of a document's cache manifest.
manifest "manifest",
/// The `max` attribute specifies the maximum value for an input element.
max "max",
/// The `maxlength` attribute specifies the maximum number of characters that an input element can accept.
maxlength "maxlength",
/// The `media` attribute specifies what media/device the linked resource is optimized for.
media "media",
/// The `method` attribute specifies the HTTP method to use when submitting the form.
method "method",
/// The `min` attribute specifies the minimum value for an input element.
min "min",
/// The `minlength` attribute specifies the minimum number of characters that an input element can accept.
minlength "minlength",
/// The `multiple` attribute indicates whether the user can enter more than one value.
multiple "multiple",
/// The `muted` attribute indicates whether the audio will be initially silenced on page load.
muted "muted",
/// The `name` attribute specifies the name of the element.
name "name",
/// The `nomodule` attribute indicates that the script should not be executed in browsers that support ES modules.
nomodule "nomodule",
/// The `nonce` attribute provides a cryptographic nonce to ensure that a script or style is approved for execution.
nonce "nonce",
/// The `novalidate` attribute indicates that the form should not be validated when submitted.
novalidate "novalidate",
/// The `open` attribute indicates whether the details element is open or closed.
open "open",
/// The `optimum` attribute specifies the range that is considered to be an optimum value.
optimum "optimum",
/// The `part` attribute identifies the element as a shadow DOM part.
part "part",
/// The `pattern` attribute specifies a regular expression that the input element's value is checked against.
pattern "pattern",
/// The `ping` attribute contains a space-separated list of URLs to be notified if the user follows the hyperlink.
ping "ping",
/// The `placeholder` attribute provides a short hint that describes the expected value of the input element.
placeholder "placeholder",
/// The `playsinline` attribute indicates that the video should play inline in the element's playback area.
playsinline "playsinline",
/// The `popover` attribute indicates that an element is a popover and specifies the event that causes the popover to be shown.
popover "popover",
/// The `popovertarget` attribute specifies the ID of an element to toggle a popover.
popovertarget "popovertarget",
/// The `popovertargetaction` attribute specifies the action that shows the popover.
popovertargetaction "popovertargetaction",
poster "poster", // [],
preload "preload", // [],
radiogroup "radiogroup", // [],
readonly "readonly", // [],
referrerpolicy "referrerpolicy", // [],
rel "rel", // [],
required "required", // [],
reversed "reversed", // [],
role "role", // [], // [GlobalAttribute] // [AriaAttribute],
rows "rows", // [],
rowspan "rowspan", // [],
sandbox "sandbox", // [],
scope "scope", // [],
scoped "scoped", // [],
selected "selected", // [],
shape "shape", // [],
size "size", // [],
sizes "sizes", // [],
slot "slot", // [], // [GlobalAttribute]
span "span", // [],
spellcheck "spellcheck", // [], // [GlobalAttribute]
src "src", // [],
srcdoc "srcdoc", // [],
srclang "srclang", // [],
srcset "srcset", // [],
start "start", // [],
step "step", // [],
/// The `poster` attribute specifies an image to be shown while the video is downloading or until the user hits the play button.
poster "poster",
/// The `preload` attribute specifies if and how the author thinks that the media file should be loaded when the page loads.
preload "preload",
/// The `radiogroup` attribute specifies the name of the group to which the element belongs.
radiogroup "radiogroup",
/// The `readonly` attribute indicates that the user cannot modify the value of the input element.
readonly "readonly",
/// The `referrerpolicy` attribute specifies which referrer information to include with requests.
referrerpolicy "referrerpolicy",
/// The `rel` attribute specifies the relationship between the current document and the linked document.
rel "rel",
/// The `required` attribute indicates that the user must fill in the input element before submitting the form.
required "required",
/// The `reversed` attribute indicates that the list should be displayed in a descending order.
reversed "reversed",
/// The `role` attribute defines the role of an element in the context of a web application.
role "role",
/// The `rows` attribute specifies the number of visible text lines for a text area.
rows "rows",
/// The `rowspan` attribute defines the number of rows a cell should span.
rowspan "rowspan",
/// The `sandbox` attribute applies extra restrictions to the content in the `<iframe>`.
sandbox "sandbox",
/// The `scope` attribute specifies whether a header cell is a header for a column, row, or group of columns or rows.
scope "scope",
/// The `scoped` attribute indicates that the styles in a `<style>` element are scoped to the parent element.
scoped "scoped",
/// The `selected` attribute indicates that the option is selected.
selected "selected",
/// The `shape` attribute specifies the shape of the area.
shape "shape",
/// The `size` attribute specifies the width of the input element.
size "size",
/// The `sizes` attribute specifies the sizes of icons for visual media.
sizes "sizes",
/// The `slot` attribute assigns a slot to an element.
slot "slot",
/// The `span` attribute defines the number of columns in a `<colgroup>` or the number of rows in a `<rowgroup>`.
span "span",
/// The `spellcheck` attribute indicates whether spell checking is allowed for the element.
spellcheck "spellcheck",
/// The `src` attribute specifies the URL of the media resource.
src "src",
/// The `srcdoc` attribute specifies the HTML content of the page to show in the `<iframe>`.
srcdoc "srcdoc",
/// The `srclang` attribute specifies the language of the text track.
srclang "srclang",
/// The `srcset` attribute specifies the URLs of multiple images to be used in different situations.
srcset "srcset",
/// The `start` attribute specifies the start value of the list.
start "start",
/// The `step` attribute specifies the legal number intervals for an input element.
step "step",
// style is handled in ../style.rs instead
// style "style", // [],
summary "summary", // [],
tabindex "tabindex", // [], // [GlobalAttribute]
target "target", // [],
title "title", // [], // [GlobalAttribute]
translate "translate", // [], // [GlobalAttribute]
r#type "type", // [],
usemap "usemap", // [],
value "value", // [],
virtualkeyboardpolicy "virtualkeyboardpolicy", // [], // [GlobalAttribute]
width "width", // [],
wrap "wrap", // [],
// style "style",
/// The `summary` attribute provides a summary of the content of the table.
summary "summary",
/// The `tabindex` attribute specifies the tab order of an element.
tabindex "tabindex",
/// The `target` attribute specifies where to open the linked document.
target "target",
/// The `title` attribute provides additional information about an element.
title "title",
/// The `translate` attribute specifies whether the content of an element should be translated or not.
translate "translate",
/// The `type` attribute specifies the type of the element.
r#type "type",
/// The `usemap` attribute specifies the image map to be used by an `<img>` element.
usemap "usemap",
/// The `value` attribute specifies the value of the element.
value "value",
/// The `virtualkeyboardpolicy` attribute controls the policy for virtual keyboards.
virtualkeyboardpolicy "virtualkeyboardpolicy",
/// The `width` attribute specifies the width of an element.
width "width",
/// The `wrap` attribute specifies how the text in a text area is to be wrapped when submitted in a form.
wrap "wrap",
// Event Handler Attributes
onabort "onabort", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onautocomplete "onautocomplete", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onautocompleteerror "onautocompleteerror", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onblur "onblur", // [], // [GlobalAttribute] // [EventHandlerAttribute],
oncancel "oncancel", // [], // [GlobalAttribute] // [EventHandlerAttribute],
oncanplay "oncanplay", // [], // [GlobalAttribute] // [EventHandlerAttribute],
oncanplaythrough "oncanplaythrough", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onchange "onchange", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onclick "onclick", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onclose "onclose", // [], // [GlobalAttribute] // [EventHandlerAttribute],
oncontextmenu "oncontextmenu", // [], // [GlobalAttribute] // [EventHandlerAttribute],
oncuechange "oncuechange", // [], // [GlobalAttribute] // [EventHandlerAttribute],
ondblclick "ondblclick", // [], // [GlobalAttribute] // [EventHandlerAttribute],
ondrag "ondrag", // [], // [GlobalAttribute] // [EventHandlerAttribute],
ondragend "ondragend", // [], // [GlobalAttribute] // [EventHandlerAttribute],
ondragenter "ondragenter", // [], // [GlobalAttribute] // [EventHandlerAttribute],
ondragleave "ondragleave", // [], // [GlobalAttribute] // [EventHandlerAttribute],
ondragover "ondragover", // [], // [GlobalAttribute] // [EventHandlerAttribute],
ondragstart "ondragstart", // [], // [GlobalAttribute] // [EventHandlerAttribute],
ondrop "ondrop", // [], // [GlobalAttribute] // [EventHandlerAttribute],
ondurationchange "ondurationchange", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onemptied "onemptied", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onended "onended", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onerror "onerror", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onfocus "onfocus", // [], // [GlobalAttribute] // [EventHandlerAttribute],
oninput "oninput", // [], // [GlobalAttribute] // [EventHandlerAttribute],
oninvalid "oninvalid", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onkeydown "onkeydown", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onkeypress "onkeypress", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onkeyup "onkeyup", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onload "onload", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onloadeddata "onloadeddata", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onloadedmetadata "onloadedmetadata", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onloadstart "onloadstart", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onmousedown "onmousedown", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onmouseenter "onmouseenter", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onmouseleave "onmouseleave", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onmousemove "onmousemove", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onmouseout "onmouseout", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onmouseover "onmouseover", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onmouseup "onmouseup", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onmousewheel "onmousewheel", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onpause "onpause", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onplay "onplay", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onplaying "onplaying", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onprogress "onprogress", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onratechange "onratechange", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onreset "onreset", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onresize "onresize", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onscroll "onscroll", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onseeked "onseeked", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onseeking "onseeking", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onselect "onselect", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onshow "onshow", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onsort "onsort", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onstalled "onstalled", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onsubmit "onsubmit", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onsuspend "onsuspend", // [], // [GlobalAttribute] // [EventHandlerAttribute],
ontimeupdate "ontimeupdate", // [], // [GlobalAttribute] // [EventHandlerAttribute],
ontoggle "ontoggle", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onvolumechange "onvolumechange", // [], // [GlobalAttribute] // [EventHandlerAttribute],
onwaiting "onwaiting", // [], // [GlobalAttribute] // [EventHandlerAttribute],
/// The `onabort` attribute specifies the event handler for the abort event.
onabort "onabort",
/// The `onautocomplete` attribute specifies the event handler for the autocomplete event.
onautocomplete "onautocomplete",
/// The `onautocompleteerror` attribute specifies the event handler for the autocompleteerror event.
onautocompleteerror "onautocompleteerror",
/// The `onblur` attribute specifies the event handler for the blur event.
onblur "onblur",
/// The `oncancel` attribute specifies the event handler for the cancel event.
oncancel "oncancel",
/// The `oncanplay` attribute specifies the event handler for the canplay event.
oncanplay "oncanplay",
/// The `oncanplaythrough` attribute specifies the event handler for the canplaythrough event.
oncanplaythrough "oncanplaythrough",
/// The `onchange` attribute specifies the event handler for the change event.
onchange "onchange",
/// The `onclick` attribute specifies the event handler for the click event.
onclick "onclick",
/// The `onclose` attribute specifies the event handler for the close event.
onclose "onclose",
/// The `oncontextmenu` attribute specifies the event handler for the contextmenu event.
oncontextmenu "oncontextmenu",
/// The `oncuechange` attribute specifies the event handler for the cuechange event.
oncuechange "oncuechange",
/// The `ondblclick` attribute specifies the event handler for the double click event.
ondblclick "ondblclick",
/// The `ondrag` attribute specifies the event handler for the drag event.
ondrag "ondrag",
/// The `ondragend` attribute specifies the event handler for the dragend event.
ondragend "ondragend",
/// The `ondragenter` attribute specifies the event handler for the dragenter event.
ondragenter "ondragenter",
/// The `ondragleave` attribute specifies the event handler for the dragleave event.
ondragleave "ondragleave",
/// The `ondragover` attribute specifies the event handler for the dragover event.
ondragover "ondragover",
/// The `ondragstart` attribute specifies the event handler for the dragstart event.
ondragstart "ondragstart",
/// The `ondrop` attribute specifies the event handler for the drop event.
ondrop "ondrop",
/// The `ondurationchange` attribute specifies the event handler for the durationchange event.
ondurationchange "ondurationchange",
/// The `onemptied` attribute specifies the event handler for the emptied event.
onemptied "onemptied",
/// The `onended` attribute specifies the event handler for the ended event.
onended "onended",
/// The `onerror` attribute specifies the event handler for the error event.
onerror "onerror",
/// The `onfocus` attribute specifies the event handler for the focus event.
onfocus "onfocus",
/// The `onformdata` attribute specifies the event handler for the formdata event.
onformdata "onformdata",
/// The `oninput` attribute specifies the event handler for the input event.
oninput "oninput",
/// The `oninvalid` attribute specifies the event handler for the invalid event.
oninvalid "oninvalid",
/// The `onkeydown` attribute specifies the event handler for the keydown event.
onkeydown "onkeydown",
/// The `onkeypress` attribute specifies the event handler for the keypress event.
onkeypress "onkeypress",
/// The `onkeyup` attribute specifies the event handler for the keyup event.
onkeyup "onkeyup",
/// The `onlanguagechange` attribute specifies the event handler for the languagechange event.
onlanguagechange "onlanguagechange",
/// The `onload` attribute specifies the event handler for the load event.
onload "onload",
/// The `onloadeddata` attribute specifies the event handler for the loadeddata event.
onloadeddata "onloadeddata",
/// The `onloadedmetadata` attribute specifies the event handler for the loadedmetadata event.
onloadedmetadata "onloadedmetadata",
/// The `onloadstart` attribute specifies the event handler for the loadstart event.
onloadstart "onloadstart",
/// The `onmousedown` attribute specifies the event handler for the mousedown event.
onmousedown "onmousedown",
/// The `onmouseenter` attribute specifies the event handler for the mouseenter event.
onmouseenter "onmouseenter",
/// The `onmouseleave` attribute specifies the event handler for the mouseleave event.
onmouseleave "onmouseleave",
/// The `onmousemove` attribute specifies the event handler for the mousemove event.
onmousemove "onmousemove",
/// The `onmouseout` attribute specifies the event handler for the mouseout event.
onmouseout "onmouseout",
/// The `onmouseover` attribute specifies the event handler for the mouseover event.
onmouseover "onmouseover",
/// The `onmouseup` attribute specifies the event handler for the mouseup event.
onmouseup "onmouseup",
/// The `onpause` attribute specifies the event handler for the pause event.
onpause "onpause",
/// The `onplay` attribute specifies the event handler for the play event.
onplay "onplay",
/// The `onplaying` attribute specifies the event handler for the playing event.
onplaying "onplaying",
/// The `onprogress` attribute specifies the event handler for the progress event.
onprogress "onprogress",
/// The `onratechange` attribute specifies the event handler for the ratechange event.
onratechange "onratechange",
/// The `onreset` attribute specifies the event handler for the reset event.
onreset "onreset",
/// The `onresize` attribute specifies the event handler for the resize event.
onresize "onresize",
/// The `onscroll` attribute specifies the event handler for the scroll event.
onscroll "onscroll",
/// The `onsecuritypolicyviolation` attribute specifies the event handler for the securitypolicyviolation event.
onsecuritypolicyviolation "onsecuritypolicyviolation",
/// The `onseeked` attribute specifies the event handler for the seeked event.
onseeked "onseeked",
/// The `onseeking` attribute specifies the event handler for the seeking event.
onseeking "onseeking",
/// The `onselect` attribute specifies the event handler for the select event.
onselect "onselect",
/// The `onslotchange` attribute specifies the event handler for the slotchange event.
onslotchange "onslotchange",
/// The `onstalled` attribute specifies the event handler for the stalled event.
onstalled "onstalled",
/// The `onsubmit` attribute specifies the event handler for the submit event.
onsubmit "onsubmit",
/// The `onsuspend` attribute specifies the event handler for the suspend event.
onsuspend "onsuspend",
/// The `ontimeupdate` attribute specifies the event handler for the timeupdate event.
ontimeupdate "ontimeupdate",
/// The `ontoggle` attribute specifies the event handler for the toggle event.
ontoggle "ontoggle",
/// The `onvolumechange` attribute specifies the event handler for the volumechange event.
onvolumechange "onvolumechange",
/// The `onwaiting` attribute specifies the event handler for the waiting event.
onwaiting "onwaiting",
/// The `onwebkitanimationend` attribute specifies the event handler for the webkitanimationend event.
onwebkitanimationend "onwebkitanimationend",
/// The `onwebkitanimationiteration` attribute specifies the event handler for the webkitanimationiteration event.
onwebkitanimationiteration "onwebkitanimationiteration",
/// The `onwebkitanimationstart` attribute specifies the event handler for the webkitanimationstart event.
onwebkitanimationstart "onwebkitanimationstart",
/// The `onwebkittransitionend` attribute specifies the event handler for the webkittransitionend event.
onwebkittransitionend "onwebkittransitionend",
/// The `onwheel` attribute specifies the event handler for the wheel event.
onwheel "onwheel",
// MathML attributes that aren't in HTML
// MathML attributes
/// The `accent` attribute specifies whether the element should be treated as an accent.
accent "accent",
/// The `accentunder` attribute specifies whether the element should be treated as an accent under the base element.
accentunder "accentunder",
/// The `columnalign` attribute specifies the alignment of columns.
columnalign "columnalign",
/// The `columnlines` attribute specifies the presence of lines between columns.
columnlines "columnlines",
/// The `columnspacing` attribute specifies the spacing between columns.
columnspacing "columnspacing",
/// The `columnspan` attribute specifies the number of columns the element should span.
columnspan "columnspan",
/// The `depth` attribute specifies the depth of the element.
depth "depth",
/// The `display` attribute specifies the display style of the element.
display "display",
/// The `displaystyle` attribute specifies whether the element is displayed in display style.
displaystyle "displaystyle",
/// The `fence` attribute specifies whether the element should act as a fence.
fence "fence",
/// The `frame` attribute specifies the type of frame for the element.
frame "frame",
/// The `framespacing` attribute specifies the spacing around frames.
framespacing "framespacing",
/// The `linethickness` attribute specifies the thickness of lines.
linethickness "linethickness",
/// The `lspace` attribute specifies the space on the left side of the element.
lspace "lspace",
/// The `mathbackground` attribute specifies the background color of the element.
mathbackground "mathbackground",
/// The `mathcolor` attribute specifies the color of the element.
mathcolor "mathcolor",
/// The `mathsize` attribute specifies the size of the element.
mathsize "mathsize",
/// The `mathvariant` attribute specifies the mathematical variant of the element.
mathvariant "mathvariant",
/// The `maxsize` attribute specifies the maximum size of the element.
maxsize "maxsize",
/// The `minsize` attribute specifies the minimum size of the element.
minsize "minsize",
/// The `movablelimits` attribute specifies whether the limits of the element are movable.
movablelimits "movablelimits",
/// The `notation` attribute specifies the type of notation for the element.
notation "notation",
/// The `rowalign` attribute specifies the alignment of rows.
rowalign "rowalign",
/// The `rowlines` attribute specifies the presence of lines between rows.
rowlines "rowlines",
/// The `rowspacing` attribute specifies the spacing between rows.
rowspacing "rowspacing",
/// The `rspace` attribute specifies the space on the right side of the element.
rspace "rspace",
/// The `scriptlevel` attribute specifies the script level of the element.
scriptlevel "scriptlevel",
/// The `separator` attribute specifies whether the element is a separator.
separator "separator",
/// The `stretchy` attribute specifies whether the element is stretchy.
stretchy "stretchy",
/// The `symmetric` attribute specifies whether the element is symmetric.
symmetric "symmetric",
/// The `voffset` attribute specifies the vertical offset of the element.
voffset "voffset",
/// The `xmlns` attribute specifies the XML namespace of the element.
xmlns "xmlns",
}

View file

@ -1,6 +1,10 @@
/// A type-erased `AnyAttribute`.
pub mod any_attribute;
/// Types for ARIA attributes.
pub mod aria;
/// Types for custom attributes.
pub mod custom;
/// Traits to define global attribute methods on all HTML elements.
pub mod global;
mod key;
mod value;
@ -12,16 +16,29 @@ pub use key::*;
use std::{fmt::Debug, future::Future, marker::PhantomData};
pub use value::*;
/// Defines an attribute: anything that can modify an element.
pub trait Attribute<R: Renderer>: NextAttribute<R> + Send {
/// The minimum length of this attribute in HTML.
const MIN_LENGTH: usize;
/// The state that should be retained between building and rebuilding.
type State;
/// The type once all async data have loaded.
type AsyncOutput: Attribute<R>;
/// An equivalent to this attribute that can be cloned to be shared across elements.
type Cloneable: Attribute<R> + Clone;
/// An equivalent to this attribute that can be cloned to be shared across elements, and
/// captures no references shorter than `'static`.
type CloneableOwned: Attribute<R> + Clone + 'static;
/// An approximation of the actual length of this attribute in HTML.
fn html_len(&self) -> usize;
/// Renders the attribute to HTML.
///
/// This separates a general buffer for attribute values from the `class` and `style`
/// attributes, so that multiple classes or styles can be combined, and also allows for an
/// `inner_html` attribute that sets the child HTML instead of an attribute.
fn to_html(
self,
buf: &mut String,
@ -30,24 +47,39 @@ pub trait Attribute<R: Renderer>: NextAttribute<R> + Send {
inner_html: &mut String,
);
/// Adds interactivity as necessary, given DOM nodes that were created from HTML that has
/// either been rendered on the server, or cloned for a `<template>`.
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State;
/// Adds this attribute to the element during client-side rendering.
fn build(self, el: &R::Element) -> Self::State;
/// Applies a new value for the attribute.
fn rebuild(self, state: &mut Self::State);
/// Converts this attribute into an equivalent that can be cloned.
fn into_cloneable(self) -> Self::Cloneable;
/// Converts this attributes into an equivalent that can be cloned and is `'static`.
fn into_cloneable_owned(self) -> Self::CloneableOwned;
/// “Runs” the attribute without other side effects. For primitive types, this is a no-op. For
/// reactive types, this can be used to gather data about reactivity or about asynchronous data
/// that needs to be loaded.
fn dry_resolve(&mut self);
/// “Resolves” this into a type that is not waiting for any asynchronous data.
fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;
}
/// Adds another attribute to this one, returning a new attribute.
///
/// This is typically achieved by creating or extending a tuple of attributes.
pub trait NextAttribute<R: Renderer> {
/// The type of the new, combined attribute.
type Output<NewAttr: Attribute<R>>: Attribute<R>;
/// Adds a new attribute.
fn add_any_attr<NewAttr: Attribute<R>>(
self,
new_attr: NewAttr,
@ -112,6 +144,7 @@ where
}
}
/// An attribute with a key and value.
#[derive(Debug)]
pub struct Attr<K, V, R>(pub K, pub V, pub PhantomData<R>)
where

View file

@ -11,8 +11,12 @@ use std::{
sync::Arc,
};
/// A possible value for an HTML attribute.
pub trait AttributeValue<R: Renderer>: Send {
/// The state that should be retained between building and rebuilding.
type State;
/// The type once all async data have loaded.
type AsyncOutput: AttributeValue<R>;
/// A version of the value that can be cloned. This can be the same type, or a
@ -28,28 +32,41 @@ pub trait AttributeValue<R: Renderer>: Send {
/// cloneable type has worse performance than the cloneable type, so they are separate.
type CloneableOwned: AttributeValue<R> + Clone + 'static;
/// An approximation of the actual length of this attribute in HTML.
fn html_len(&self) -> usize;
/// Renders the attribute value to HTML.
fn to_html(self, key: &str, buf: &mut String);
/// Renders the attribute value to HTML for a `<template>`.
fn to_template(key: &str, buf: &mut String);
/// Adds interactivity as necessary, given DOM nodes that were created from HTML that has
/// either been rendered on the server, or cloned for a `<template>`.
fn hydrate<const FROM_SERVER: bool>(
self,
key: &str,
el: &R::Element,
) -> Self::State;
/// Adds this attribute to the element during client-side rendering.
fn build(self, el: &R::Element, key: &str) -> Self::State;
/// Applies a new value for the attribute.
fn rebuild(self, key: &str, state: &mut Self::State);
/// Converts this attribute into an equivalent that can be cloned.
fn into_cloneable(self) -> Self::Cloneable;
/// Converts this attributes into an equivalent that can be cloned and is `'static`.
fn into_cloneable_owned(self) -> Self::CloneableOwned;
/// “Runs” the attribute without other side effects. For primitive types, this is a no-op. For
/// reactive types, this can be used to gather data about reactivity or about asynchronous data
/// that needs to be loaded.
fn dry_resolve(&mut self);
/// “Resolves” this into a form that is not waiting for any asynchronous data.
fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;
}

View file

@ -5,6 +5,7 @@ use crate::{
};
use std::{future::Future, marker::PhantomData, sync::Arc};
/// Adds a CSS class.
#[inline(always)]
pub fn class<C, R>(class: C) -> Class<C, R>
where
@ -17,6 +18,7 @@ where
}
}
/// A CSS class.
#[derive(Debug)]
pub struct Class<C, R> {
class: C,
@ -133,34 +135,54 @@ where
}
}
/// A possible value for a CSS class.
pub trait IntoClass<R: DomRenderer>: Send {
/// The HTML that should be included in a `<template>`.
const TEMPLATE: &'static str = "";
/// The minimum length of the HTML.
const MIN_LENGTH: usize = Self::TEMPLATE.len();
/// The type after all async data have resolved.
type AsyncOutput: IntoClass<R>;
/// The view state retained between building and rebuilding.
type State;
/// An equivalent value that can be cloned.
type Cloneable: IntoClass<R> + Clone;
/// An equivalent value that can be cloned and is `'static`.
type CloneableOwned: IntoClass<R> + Clone + 'static;
/// The estimated length of the HTML.
fn html_len(&self) -> usize;
/// Renders the class to HTML.
fn to_html(self, class: &mut String);
/// Renders the class to HTML for a `<template>`.
#[allow(unused)] // it's used with `nightly` feature
fn to_template(class: &mut String) {}
/// Adds interactivity as necessary, given DOM nodes that were created from HTML that has
/// either been rendered on the server, or cloned for a `<template>`.
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State;
/// Adds this class to the element during client-side rendering.
fn build(self, el: &R::Element) -> Self::State;
/// Updates the value.
fn rebuild(self, state: &mut Self::State);
/// Converts this to a cloneable type.
fn into_cloneable(self) -> Self::Cloneable;
/// Converts this to a cloneable, owned type.
fn into_cloneable_owned(self) -> Self::CloneableOwned;
/// “Runs” the attribute without other side effects. For primitive types, this is a no-op. For
/// reactive types, this can be used to gather data about reactivity or about asynchronous data
/// that needs to be loaded.
fn dry_resolve(&mut self);
/// “Resolves” this into a type that is not waiting for any asynchronous data.
fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;
}

View file

@ -7,13 +7,18 @@ use crate::{
use send_wrapper::SendWrapper;
use std::{marker::PhantomData, sync::Arc};
/// Adds a directive to the element, which runs some custom logic in the browser when the element
/// is created or hydrated.
pub trait DirectiveAttribute<T, P, D, Rndr>
where
D: IntoDirective<T, P, Rndr>,
Rndr: Renderer,
{
/// The type of the element with the directive added.
type Output;
/// Adds a directive to the element, which runs some custom logic in the browser when the element
/// is created or hydrated.
fn directive(self, handler: D, param: P) -> Self::Output;
}
@ -32,6 +37,8 @@ where
}
}
/// Adds a directive to the element, which runs some custom logic in the browser when the element
/// is created or hydrated.
#[inline(always)]
pub fn directive<T, P, D, R>(handler: D, param: P) -> Directive<T, D, P, R>
where
@ -46,6 +53,7 @@ where
}))
}
/// Custom logic that runs in the browser when the element is created or hydrated.
#[derive(Debug)]
pub struct Directive<T, D, P, R>(SendWrapper<DirectiveInner<T, D, P, R>>);
@ -60,7 +68,7 @@ where
}
#[derive(Debug)]
pub struct DirectiveInner<T, D, P, R> {
struct DirectiveInner<T, D, P, R> {
handler: D,
param: P,
t: PhantomData<T>,
@ -229,11 +237,13 @@ impl<T, D, P, R> ToTemplate for Directive<T, D, P, R> {
/// The first is the element the directive is added to and the optional
/// second is the parameter that is provided in the attribute.
pub trait IntoDirective<T: ?Sized, P, R: Renderer> {
/// An equivalent to this directive that is cloneable and owned.
type Cloneable: IntoDirective<T, P, R> + Clone + 'static;
/// Calls the handler function
fn run(&self, el: R::Element, param: P);
/// Converts this into a cloneable type.
fn into_cloneable(self) -> Self::Cloneable;
}

View file

@ -6,6 +6,7 @@ use crate::{
use std::{borrow::Cow, fmt::Debug, marker::PhantomData, sync::Arc};
// FIXME custom element HTML rendering is broken because tag names aren't static
/// Creates a custom element.
#[track_caller]
pub fn custom<E, Rndr>(tag: E) -> HtmlElement<Custom<E>, (), (), Rndr>
where
@ -22,6 +23,7 @@ where
}
}
/// A custom HTML element.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Custom<E>(E)
where
@ -57,8 +59,10 @@ where
}
}
/// The element name for a custom element.
// TODO these are all broken for custom elements
pub trait CustomElementKey: AsRef<str> + Send {
/// The element name.
const KEY: &'static str;
}

View file

@ -27,18 +27,22 @@ use web_sys::Element;
/// let remove = element.on(ev::blur, move |_| /* ... */);
/// ```
pub trait ElementExt {
/// Adds an attribute to the element, at runtime.
fn attr<At>(&self, attribute: At) -> At::State
where
At: Attribute<Dom>;
/// Adds a class to the element, at runtime.
fn class<C>(&self, class: C) -> C::State
where
C: IntoClass<Dom>;
/// Adds a style to the element, at runtime.
fn style<S>(&self, style: S) -> S::State
where
S: IntoStyle<Dom>;
/// Adds an event listener to the element, at runtime.
fn on<E>(
&self,
ev: E,

View file

@ -6,6 +6,14 @@ use crate::{
};
use std::{future::Future, marker::PhantomData, sync::Arc};
/// Returns an [`Attribute`] that sets the inner HTML of an element.
///
/// No children should be given to this element, as this HTML will be used instead.
///
/// # Security
/// Be very careful when using this method. Always remember to
/// sanitize the input to avoid a cross-site scripting (XSS)
/// vulnerability.
#[inline(always)]
pub fn inner_html<T, R>(value: T) -> InnerHtml<T, R>
where
@ -18,6 +26,7 @@ where
}
}
/// Sets the inner HTML of an element.
#[derive(Debug)]
pub struct InnerHtml<T, R> {
value: T,
@ -118,12 +127,21 @@ where
}
}
/// Sets the inner HTML of an element.
pub trait InnerHtmlAttribute<T, Rndr>
where
T: InnerHtmlValue<Rndr>,
Rndr: DomRenderer,
Self: Sized + AddAnyAttr<Rndr>,
{
/// Sets the inner HTML of this element.
///
/// No children should be given to this element, as this HTML will be used instead.
///
/// # Security
/// Be very careful when using this method. Always remember to
/// sanitize the input to avoid a cross-site scripting (XSS)
/// vulnerability.
fn inner_html(
self,
value: T,
@ -149,30 +167,48 @@ where
}
}
/// A possible value for [`InnerHtml`].
pub trait InnerHtmlValue<R: DomRenderer>: Send {
/// The type after all async data have resolved.
type AsyncOutput: InnerHtmlValue<R>;
/// The view state retained between building and rebuilding.
type State;
/// An equivalent value that can be cloned.
type Cloneable: InnerHtmlValue<R> + Clone;
/// An equivalent value that can be cloned and is `'static`.
type CloneableOwned: InnerHtmlValue<R> + Clone + 'static;
/// The estimated length of the HTML.
fn html_len(&self) -> usize;
/// Renders the class to HTML.
fn to_html(self, buf: &mut String);
/// Renders the class to HTML for a `<template>`.
fn to_template(buf: &mut String);
/// Adds interactivity as necessary, given DOM nodes that were created from HTML that has
/// either been rendered on the server, or cloned for a `<template>`.
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State;
/// Adds this class to the element during client-side rendering.
fn build(self, el: &R::Element) -> Self::State;
/// Updates the value.
fn rebuild(self, state: &mut Self::State);
/// Converts this to a cloneable type.
fn into_cloneable(self) -> Self::Cloneable;
/// Converts this to a cloneable, owned type.
fn into_cloneable_owned(self) -> Self::CloneableOwned;
/// “Runs” the attribute without other side effects. For primitive types, this is a no-op. For
/// reactive types, this can be used to gather data about reactivity or about asynchronous data
/// that needs to be loaded.
fn dry_resolve(&mut self);
/// “Resolves” this into a type that is not waiting for any asynchronous data.
fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;
}

View file

@ -25,6 +25,7 @@ pub use element_ext::*;
pub use elements::*;
pub use inner_html::*;
/// The typed representation of an HTML element.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct HtmlElement<E, At, Ch, Rndr> {
pub(crate) tag: E,
@ -50,24 +51,6 @@ where
}
}*/
impl<E, At, Ch, Rndr> HtmlElement<E, At, Ch, Rndr> {
pub fn children(&self) -> &Ch {
&self.children
}
pub fn children_mut(&mut self) -> &mut Ch {
&mut self.children
}
pub fn attributes(&self) -> &At {
&self.attributes
}
pub fn attributes_mut(&mut self) -> &mut At {
&mut self.attributes
}
}
impl<E, At, Ch, NewChild, Rndr> ElementChild<Rndr, NewChild>
for HtmlElement<E, At, Ch, Rndr>
where
@ -136,34 +119,48 @@ where
}
}
/// Adds a child to the element.
pub trait ElementChild<Rndr, NewChild>
where
NewChild: Render<Rndr>,
Rndr: Renderer,
{
/// The type of the element, with the child added.
type Output;
/// Adds a child to an element.
fn child(self, child: NewChild) -> Self::Output;
}
/// An HTML element.
pub trait ElementType: Send {
/// The underlying native widget type that this represents.
type Output;
/// The element's tag.
const TAG: &'static str;
/// Whether the element is self-closing.
const SELF_CLOSING: bool;
/// Whether the element's children should be escaped. This should be `true` except for elements
/// like `<style>` and `<script>`, which include other languages that should not use HTML
/// entity escaping.
const ESCAPE_CHILDREN: bool;
/// The element's tag.
fn tag(&self) -> &str;
}
/// Denotes that the type that implements this has a particular HTML element type.
pub trait HasElementType {
/// The element type.
type ElementType;
}
pub trait ElementWithChildren {}
pub(crate) trait ElementWithChildren {}
/// Creates an element.
pub trait CreateElement<R: Renderer> {
/// Creates an element.
fn create_element(&self) -> R::Element;
}
@ -386,6 +383,7 @@ where
}
}
/// Renders an [`Attribute`] (which can be one or more HTML attributes) into an HTML buffer.
pub fn attributes_to_html<At, R>(attr: At, buf: &mut String) -> String
where
At: Attribute<R>,
@ -423,10 +421,11 @@ where
inner_html
}
/// The retained view state for an HTML element.
pub struct ElementState<At, Ch, R: Renderer> {
pub el: R::Element,
pub attrs: At,
pub children: Option<Ch>,
pub(crate) el: R::Element,
pub(crate) attrs: At,
pub(crate) children: Option<Ch>,
rndr: PhantomData<R>,
}

View file

@ -14,11 +14,15 @@ use std::{
};
use wasm_bindgen::convert::FromWasmAbi;
/// A cloneable event callback.
pub type SharedEventCallback<E> = Rc<RefCell<dyn FnMut(E)>>;
/// A function that can be called in response to an event.
pub trait EventCallback<E>: 'static {
/// Runs the event handler.
fn invoke(&mut self, event: E);
/// Converts this into a cloneable/shared event handler.
fn into_shared(self) -> SharedEventCallback<E>;
}
@ -46,6 +50,7 @@ where
}
}
/// An event listener with a typed event target.
pub struct Targeted<E, T, R> {
event: E,
el_ty: PhantomData<T>,
@ -53,10 +58,12 @@ pub struct Targeted<E, T, R> {
}
impl<E, T, R> Targeted<E, T, R> {
/// Returns the inner event.
pub fn into_inner(self) -> E {
self.event
}
/// Returns the event's target, as an HTML element of the correct type.
pub fn target(&self) -> T
where
T: CastFrom<R::Element>,
@ -93,6 +100,7 @@ impl<E, T, R> From<E> for Targeted<E, T, R> {
}
}
/// Creates an [`Attribute`] that will add an event listener to an element.
pub fn on<E, F, R>(event: E, cb: F) -> On<E, F, R>
where
F: FnMut(E::EventType) + 'static,
@ -108,6 +116,7 @@ where
}
}
/// Creates an [`Attribute`] that will add an event listener with a typed target to an element.
#[allow(clippy::type_complexity)]
pub fn on_target<E, T, R, F>(
event: E,
@ -125,6 +134,7 @@ where
on(event, Box::new(move |ev: E::EventType| cb(ev.into())))
}
/// An [`Attribute`] that adds an event listener to an element.
pub struct On<E, F, R> {
event: E,
cb: SendWrapper<F>,
@ -153,6 +163,7 @@ where
R: DomRenderer,
E::EventType: From<R::Event>,
{
/// Attaches the event listener to the element.
pub fn attach(self, el: &R::Element) -> RemoveEventHandler<R::Element> {
fn attach_inner<R: DomRenderer>(
el: &R::Element,

View file

@ -9,6 +9,7 @@ use crate::{
use std::marker::PhantomData;
// TODO serialized props, too
/// An island of interactivity in an otherwise-inert HTML document.
pub struct Island<Rndr, View> {
component: &'static str,
view: View,
@ -18,6 +19,7 @@ const ISLAND_TAG: &str = "leptos-island";
const ISLAND_CHILDREN_TAG: &str = "leptos-children";
impl<Rndr, View> Island<Rndr, View> {
/// Creates a new island with the given component name.
pub fn new(component: &'static str, view: View) -> Self {
Island {
component,
@ -152,12 +154,14 @@ where
}
}
/// The children that will be projected into an [`Island`].
pub struct IslandChildren<Rndr, View> {
view: View,
rndr: PhantomData<Rndr>,
}
impl<Rndr, View> IslandChildren<Rndr, View> {
/// Creates a new representation of the children.
pub fn new(view: View) -> Self {
IslandChildren {
view,

View file

@ -5,21 +5,32 @@ use crate::{
};
use std::marker::PhantomData;
/// Types for HTML attributes.
pub mod attribute;
/// Types for manipulating the `class` attribute and `classList`.
pub mod class;
/// Types for creating user-defined attributes with custom behavior (directives).
pub mod directive;
/// Types for HTML elements.
pub mod element;
/// Types for DOM events.
pub mod event;
/// Types for adding interactive islands to inert HTML pages.
pub mod islands;
/// Types for accessing a reference to an HTML element.
pub mod node_ref;
/// Types for DOM properties.
pub mod property;
/// Types for the `style` attribute and individual style manipulation.
pub mod style;
/// A `<!DOCTYPE>` declaration.
pub struct Doctype<R: Renderer> {
value: &'static str,
rndr: PhantomData<R>,
}
/// Creates a `<!DOCTYPE>`.
pub fn doctype<R: Renderer>(value: &'static str) -> Doctype<R> {
Doctype {
value,

View file

@ -8,14 +8,17 @@ use crate::{
};
use std::marker::PhantomData;
/// Describes a container that can be used to hold a reference to an HTML element.
pub trait NodeRefContainer<E, Rndr>: Send + Clone
where
E: ElementType,
Rndr: Renderer,
{
/// Fills the container with the element.
fn load(self, el: &Rndr::Element);
}
/// An [`Attribute`] that will fill a [`NodeRefContainer`] with an HTML element.
#[derive(Debug)]
pub struct NodeRefAttr<E, C, Rndr> {
container: C,
@ -36,6 +39,7 @@ where
}
}
/// Creates an attribute that will fill a [`NodeRefContainer`] with the element it is applied to.
pub fn node_ref<E, C, Rndr>(container: C) -> NodeRefAttr<E, C, Rndr>
where
E: ElementType,
@ -125,6 +129,7 @@ where
}
}
/// Adds the `node_ref` attribute to an element.
pub trait NodeRefAttribute<E, C, Rndr>
where
E: ElementType,
@ -132,6 +137,7 @@ where
Rndr: Renderer,
Rndr::Element: PartialEq,
{
/// Binds this HTML element to a [`NodeRefContainer`].
fn node_ref(
self,
container: C,

View file

@ -7,6 +7,7 @@ use send_wrapper::SendWrapper;
use std::{borrow::Cow, marker::PhantomData, sync::Arc};
use wasm_bindgen::JsValue;
/// Creates an [`Attribute`] that will set a DOM property on an element.
#[inline(always)]
pub fn prop<K, P, R>(key: K, value: P) -> Property<K, P, R>
where
@ -21,6 +22,7 @@ where
}
}
/// An [`Attribute`] that will set a DOM property on an element.
#[derive(Debug)]
pub struct Property<K, P, R> {
key: K,
@ -139,23 +141,32 @@ where
}
}
/// A possible value for a DOM property.
pub trait IntoProperty<R: DomRenderer> {
/// The view state retained between building and rebuilding.
type State;
/// An equivalent value that can be cloned.
type Cloneable: IntoProperty<R> + Clone;
/// An equivalent value that can be cloned and is `'static`.
type CloneableOwned: IntoProperty<R> + Clone + 'static;
/// Adds the property on an element created from HTML.
fn hydrate<const FROM_SERVER: bool>(
self,
el: &R::Element,
key: &str,
) -> Self::State;
/// Adds the property during client-side rendering.
fn build(self, el: &R::Element, key: &str) -> Self::State;
/// Updates the property with a new value.
fn rebuild(self, state: &mut Self::State, key: &str);
/// Converts this to a cloneable type.
fn into_cloneable(self) -> Self::Cloneable;
/// Converts this to a cloneable, owned type.
fn into_cloneable_owned(self) -> Self::CloneableOwned;
}

View file

@ -5,11 +5,9 @@ use crate::{
renderer::DomRenderer,
view::{Position, ToTemplate},
};
use std::{borrow::Cow, future::Future, marker::PhantomData, sync::Arc};
use std::{future::Future, marker::PhantomData, sync::Arc};
/// Adds to the style attribute of the parent element.
///
/// This can take a plain string value, which will be assigned to the `style`
/// Returns an [`Attribute`] that will add to an element's CSS styles.
#[inline(always)]
pub fn style<S, R>(style: S) -> Style<S, R>
where
@ -22,6 +20,7 @@ where
}
}
/// An [`Attribute`] that will add to an element's CSS styles.
#[derive(Debug)]
pub struct Style<S, R> {
style: S,
@ -140,42 +139,43 @@ where
/// Any type that can be added to the `style` attribute or set as a style in
/// the [`CssStyleDeclaration`]. This could be a plain string, or a property name-value pair.
pub trait IntoStyle<R: DomRenderer>: Send {
/// The type after all async data have resolved.
type AsyncOutput: IntoStyle<R>;
/// The view state retained between building and rebuilding.
type State;
/// An equivalent value that can be cloned.
type Cloneable: IntoStyle<R> + Clone;
/// An equivalent value that can be cloned and is `'static`.
type CloneableOwned: IntoStyle<R> + Clone + 'static;
/// Renders the style to HTML.
fn to_html(self, style: &mut String);
/// Adds interactivity as necessary, given DOM nodes that were created from HTML that has
/// either been rendered on the server, or cloned for a `<template>`.
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State;
/// Adds this style to the element during client-side rendering.
fn build(self, el: &R::Element) -> Self::State;
/// Updates the value.
fn rebuild(self, state: &mut Self::State);
/// Converts this to a cloneable type.
fn into_cloneable(self) -> Self::Cloneable;
/// Converts this to a cloneable, owned type.
fn into_cloneable_owned(self) -> Self::CloneableOwned;
/// “Runs” the attribute without other side effects. For primitive types, this is a no-op. For
/// reactive types, this can be used to gather data about reactivity or about asynchronous data
/// that needs to be loaded.
fn dry_resolve(&mut self);
/// “Resolves” this into a type that is not waiting for any asynchronous data.
fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;
}
pub trait StylePropertyValue<R: DomRenderer> {
type State;
fn to_html(self, name: &str, style: &mut String);
fn hydrate<const FROM_SERVER: bool>(
self,
name: Cow<'static, str>,
el: &R::Element,
) -> Self::State;
fn rebuild(self, name: Cow<'static, str>, state: &mut Self::State);
}
impl<'a, R> IntoStyle<R> for &'a str
where
R: DomRenderer,

View file

@ -4,6 +4,11 @@ use crate::{
};
use std::{cell::RefCell, rc::Rc};
/// Hydration works by walking over the DOM, adding interactivity as needed.
///
/// This cursor tracks the location in the DOM that is currently being hydrated. Each that type
/// implements [`RenderHtml`](crate::view::RenderHtml) knows how to advance the cursor to access
/// the nodes it needs.
#[derive(Debug)]
pub struct Cursor<R: Renderer>(Rc<RefCell<R::Node>>);
@ -19,14 +24,19 @@ where
R::Element: AsRef<R::Node>,
{
/// Creates a new cursor starting at the root element.
pub fn new(root: R::Element) -> Self {
Self(Rc::new(RefCell::new(root.as_ref().clone())))
}
/// Returns the node at which the cursor is currently located.
pub fn current(&self) -> R::Node {
self.0.borrow().clone()
}
/// Advances to the next child of the node at which the cursor is located.
///
/// Does nothing if there is no child.
pub fn child(&self) {
//crate::log("advancing to next child of ");
//R::log_node(&self.current());
@ -39,6 +49,9 @@ where
//R::log_node(&self.current());
}
/// Advances to the next sibling of the node at which the cursor is located.
///
/// Does nothing if there is no sibling.
pub fn sibling(&self) {
//crate::log("advancing to next sibling of ");
//R::log_node(&self.current());
@ -51,6 +64,9 @@ where
//R::log_node(&self.current());
}
/// Moves to the parent of the node at which the cursor is located.
///
/// Does nothing if there is no parent.
pub fn parent(&self) {
let mut inner = self.0.borrow_mut();
if let Some(node) = R::get_parent(&*inner) {
@ -58,10 +74,12 @@ where
}
}
/// Sets the cursor to some node.
pub fn set(&self, node: R::Node) {
*self.0.borrow_mut() = node;
}
/// Advances to the next placeholder node.
pub fn next_placeholder(&self, position: &PositionState) -> R::Placeholder {
//crate::dom::log("looking for placeholder after");
//R::log_node(&self.current());

View file

@ -1,9 +1,14 @@
//! Allows rendering user interfaces based on a statically-typed view tree.
//!
//! This view tree is generic over rendering backends, and agnostic about reactivity/change
//! detection.
#![allow(incomplete_features)] // yolo
#![cfg_attr(feature = "nightly", feature(adt_const_params))]
#![deny(missing_docs)]
/// Commonly-used traits.
pub mod prelude {
#[cfg(feature = "reactive_graph")]
pub use crate::reactive_graph::FutureViewExt;
pub use crate::{
html::{
attribute::{
@ -29,30 +34,38 @@ pub mod prelude {
use wasm_bindgen::JsValue;
use web_sys::Node;
/// Helpers for interacting with the DOM.
pub mod dom;
/// Types for building a statically-typed HTML view tree.
pub mod html;
/// Supports adding interactivity to HTML.
pub mod hydration;
/// Types for MathML.
pub mod mathml;
/// Defines various backends that can render views.
pub mod renderer;
/// Rendering views to HTML.
pub mod ssr;
/// Types for SVG.
pub mod svg;
/// Core logic for manipulating views.
pub mod view;
pub use either_of as either;
#[cfg(feature = "islands")]
#[doc(hidden)]
pub use wasm_bindgen;
#[cfg(feature = "islands")]
#[doc(hidden)]
pub use web_sys;
/// View implementations for the `oco_ref` crate (cheaply-cloned string types).
#[cfg(feature = "oco")]
pub mod oco;
/// View implementations for the `reactive_graph` crate.
#[cfg(feature = "reactive_graph")]
pub mod reactive_graph;
pub fn log(text: &str) {
web_sys::console::log_1(&JsValue::from_str(text));
}
pub(crate) trait UnwrapOrDebug {
type Output;
@ -118,6 +131,7 @@ impl<T> UnwrapOrDebug for Result<T, JsValue> {
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! or_debug {
($action:expr, $el:expr, $label:literal) => {
@ -129,6 +143,7 @@ macro_rules! or_debug {
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! ok_or_debug {
($action:expr, $el:expr, $label:literal) => {

View file

@ -15,6 +15,7 @@ use std::{fmt::Debug, marker::PhantomData};
macro_rules! mathml_global {
($tag:ty, $attr:ty) => {
paste::paste! {
/// A MathML attribute.
pub fn $attr<V>(self, value: V) -> HtmlElement <
[<$tag:camel>],
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>,
@ -47,6 +48,7 @@ macro_rules! mathml_elements {
paste::paste! {
$(
// `tag()` function
/// A MathML element.
#[track_caller]
pub fn $tag<Rndr>() -> HtmlElement<[<$tag:camel>], (), (), Rndr>
where
@ -62,6 +64,7 @@ macro_rules! mathml_elements {
}
}
/// A MathML element.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct [<$tag:camel>];
@ -81,6 +84,7 @@ macro_rules! mathml_elements {
mathml_global!($tag, scriptlevel);
$(
/// A MathML attribute.
pub fn $attr<V>(self, value: V) -> HtmlElement <
[<$tag:camel>],
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>,

View file

@ -8,6 +8,7 @@ use crate::{
};
use oco_ref::Oco;
/// Retained view state for [`Oco`].
pub struct OcoStrState<R: Renderer> {
node: R::Text,
str: Oco<'static, str>,

View file

@ -20,6 +20,7 @@ use std::{
mod class;
mod guards;
mod inner_html;
/// Provides a reactive [`NodeRef`](node_ref::NodeRef) type.
pub mod node_ref;
mod owned;
mod property;
@ -78,6 +79,8 @@ where
old.unmount();
}
}
/// Retained view state for a [`RenderEffect`].
pub struct RenderEffectState<T: 'static>(Option<RenderEffect<T>>);
impl<T> From<RenderEffect<T>> for RenderEffectState<T> {
@ -359,7 +362,9 @@ where
fn to_html(self, _key: &str, _buf: &mut String) {
#[cfg(feature = "tracing")]
tracing::warn!("Suspended attributes cannot be used outside Suspense.");
tracing::error!(
"Suspended attributes cannot be used outside Suspense."
);
}
fn to_template(_key: &str, _buf: &mut String) {}
@ -411,12 +416,12 @@ where
fn into_cloneable(self) -> Self::Cloneable {
#[cfg(feature = "tracing")]
tracing::warn!("Suspended attributes cannot be spread");
tracing::error!("Suspended attributes cannot be spread");
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
#[cfg(feature = "tracing")]
tracing::warn!("Suspended attributes cannot be spread");
tracing::error!("Suspended attributes cannot be spread");
}
fn dry_resolve(&mut self) {}
@ -426,13 +431,18 @@ where
}
}
/// A reactive function that can be shared across multiple locations and across threads.
pub type SharedReactiveFunction<T> = Arc<Mutex<dyn FnMut() -> T + Send>>;
/// A reactive view function.
pub trait ReactiveFunction: Send + 'static {
/// The return type of the function.
type Output;
/// Call the function.
fn invoke(&mut self) -> Self::Output;
/// Converts the function into a cloneable, shared type.
fn into_shared(self) -> Arc<Mutex<dyn FnMut() -> Self::Output + Send>>;
}

View file

@ -9,6 +9,7 @@ use reactive_graph::{
use send_wrapper::SendWrapper;
use wasm_bindgen::JsCast;
/// A reactive reference to a DOM node that can be used with the `node_ref` attribute.
#[derive(Debug)]
pub struct NodeRef<E>(RwSignal<Option<SendWrapper<E::Output>>>)
where
@ -20,6 +21,7 @@ where
E: ElementType,
E::Output: 'static,
{
/// Creates a new node reference.
#[track_caller]
pub fn new() -> Self {
Self(RwSignal::new(None))

View file

@ -9,6 +9,7 @@ use crate::{
use reactive_graph::{computed::ScopedFuture, owner::Owner};
use std::marker::PhantomData;
/// A view wrapper that sets the reactive [`Owner`] to a particular owner whenever it is rendered.
#[derive(Debug, Clone)]
pub struct OwnedView<T, R> {
owner: Owner,
@ -37,6 +38,7 @@ impl<T, R> OwnedView<T, R> {
}
}
/// Retained view state for an [`OwnedView`].
#[derive(Debug, Clone)]
pub struct OwnedViewState<T, R>
where

View file

@ -19,25 +19,7 @@ use reactive_graph::{
};
use std::{cell::RefCell, fmt::Debug, future::Future, pin::Pin, rc::Rc};
pub trait FutureViewExt: Sized {
fn wait(self) -> Suspend<Self>
where
Self: Future,
{
Suspend(self)
}
}
impl<F> FutureViewExt for F where F: Future + Sized {}
/* // TODO remove in favor of Suspend()?
#[macro_export]
macro_rules! suspend {
($fut:expr) => {
move || $crate::prelude::FutureViewExt::wait(async move { $fut })
};
}*/
/// A suspended `Future`, which can be used in the view.
#[derive(Clone)]
pub struct Suspend<Fut>(pub Fut);
@ -47,6 +29,7 @@ impl<Fut> Debug for Suspend<Fut> {
}
}
/// Retained view state for [`Suspend`].
pub struct SuspendState<T, Rndr>
where
T: Render<Rndr>,

View file

@ -14,6 +14,7 @@ use web_sys::{
Event, HtmlElement, HtmlTemplateElement, Node, Text,
};
/// A [`Renderer`] that uses `web-sys` to manipulate DOM elements in the browser.
#[derive(Debug)]
pub struct Dom;
@ -37,7 +38,12 @@ impl Renderer for Dom {
}
fn create_placeholder() -> Self::Placeholder {
document().create_comment("")
thread_local! {
static COMMENT: Lazy<Comment> = Lazy::new(|| {
document().create_comment("")
});
}
COMMENT.with(|n| n.clone_node().unwrap().unchecked_into())
}
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))]

View file

@ -13,22 +13,31 @@ use slotmap::{new_key_type, SlotMap};
use std::{borrow::Cow, cell::RefCell, collections::HashMap, rc::Rc};
use wasm_bindgen::JsValue;
/// A [`Renderer`] that uses a mock DOM structure running in Rust code.
///
/// This is intended as a rendering background that can be used to test component logic, without
/// running a browser.
#[derive(Debug)]
pub struct MockDom;
new_key_type! {
struct NodeId;
/// A unique identifier for a mock DOM node.
pub struct NodeId;
}
/// A mock DOM node.
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Node(NodeId);
/// A mock element.
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Element(Node);
/// A mock text node.
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Text(Node);
/// A mock comment node.
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Placeholder(Node);
@ -56,6 +65,7 @@ impl AsRef<Node> for Placeholder {
}
}
/// Tests whether two nodes are references to the same underlying node.
pub fn node_eq(a: impl AsRef<Node>, b: impl AsRef<Node>) -> bool {
a.as_ref() == b.as_ref()
}
@ -79,6 +89,7 @@ impl From<Placeholder> for Node {
}
impl Element {
/// Outputs an HTML form of the element, for testing and debugging purposes.
pub fn to_debug_html(&self) -> String {
let mut buf = String::new();
self.debug_html(&mut buf);
@ -86,9 +97,12 @@ impl Element {
}
}
/// The DOM data associated with a particular node.
#[derive(Debug, PartialEq, Eq)]
pub struct NodeData {
parent: Option<NodeId>,
/// The node's parent.
pub parent: Option<NodeId>,
/// The node itself.
pub ty: NodeType,
}
@ -153,10 +167,12 @@ impl DebugHtml for NodeData {
}
}
/// The mock DOM document.
#[derive(Clone)]
pub struct Document(Rc<RefCell<SlotMap<NodeId, NodeData>>>);
impl Document {
/// Creates a new document.
pub fn new() -> Self {
Document(Default::default())
}
@ -180,6 +196,7 @@ impl Document {
})
}
/// Resets the document's contents.
pub fn reset(&self) {
self.0.borrow_mut().clear();
}
@ -295,18 +312,26 @@ thread_local! {
static DOCUMENT: Document = Document::new();
}
/// Returns the global document.
pub fn document() -> Document {
DOCUMENT.with(Clone::clone)
}
/// The type of mock DOM node.
#[derive(Debug, PartialEq, Eq)]
pub enum NodeType {
/// A text node.
Text(String),
/// An element.
Element {
/// The HTML tag name.
tag: Cow<'static, str>,
/// The attributes.
attrs: HashMap<String, String>,
/// The element's children.
children: Vec<Node>,
},
/// A placeholder.
Placeholder,
}

View file

@ -5,9 +5,12 @@ use crate::{
use std::{borrow::Cow, fmt::Debug};
use wasm_bindgen::JsValue;
/// A DOM renderer.
pub mod dom;
#[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;
@ -38,6 +41,7 @@ pub trait Renderer: Send + Sized + Debug + 'static {
+ 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 element node.
@ -123,6 +127,7 @@ pub trait Renderer: Send + Sized + Debug + 'static {
/// 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);
}
@ -208,7 +213,7 @@ pub trait DomRenderer: Renderer {
fn get_template<V>() -> Self::TemplateElement
where
V: ToTemplate + 'static;
/// Deeply clones a template.
fn clone_template(tpl: &Self::TemplateElement) -> Self::Element;
}
@ -222,5 +227,6 @@ pub trait CastFrom<T>
where
Self: Sized,
{
/// Casts a node from one type to another.
fn cast_from(source: T) -> Option<Self>;
}

View file

@ -1,4 +1,6 @@
use super::{CastFrom, DomRenderer, Renderer};
#![allow(missing_docs)] // Allow missing docs for experimental backend
use super::{CastFrom, DomRenderer, RemoveEventHandler, Renderer};
use crate::{
dom::window,
view::{Mountable, ToTemplate},
@ -374,7 +376,7 @@ impl DomRenderer for Sledgehammer {
el: &Self::Element,
name: &str,
cb: Box<dyn FnMut(Self::Event)>,
) -> Box<dyn FnOnce(&Self::Element) + Send> {
) -> RemoveEventHandler<Self::Element> {
let cb = wasm_bindgen::closure::Closure::wrap(cb).into_js_value();
CHANNEL.with_borrow_mut(|channel| {
channel.add_listener(el.0 .0, name);
@ -383,7 +385,7 @@ impl DomRenderer for Sledgehammer {
});
// return the remover
Box::new(move |_el| todo!())
RemoveEventHandler(Box::new(move |_el| todo!()))
}
fn event_target<T>(_ev: &Self::Event) -> T
@ -404,7 +406,7 @@ impl DomRenderer for Sledgehammer {
name: Cow<'static, str>,
delegation_key: Cow<'static, str>,
cb: Box<dyn FnMut(Self::Event)>,
) -> Box<dyn FnOnce(&Self::Element) + Send> {
) -> RemoveEventHandler<Self::Element> {
let cb = Closure::wrap(cb).into_js_value();
CHANNEL.with_borrow_mut(|channel| {
channel.set_property(el.0 .0, &delegation_key);
@ -481,7 +483,7 @@ impl DomRenderer for Sledgehammer {
});
// return the remover
Box::new(move |_el| todo!())
RemoveEventHandler(Box::new(move |_el| todo!()))
}
fn class_list(el: &Self::Element) -> Self::ClassList {

View file

@ -12,10 +12,11 @@ use std::{
task::{Context, Poll},
};
/// Manages streaming HTML rendering for the response to a single request.
#[derive(Default)]
pub struct StreamBuilder {
sync_buf: String,
pub chunks: VecDeque<StreamChunk>,
pub(crate) chunks: VecDeque<StreamChunk>,
pending: Option<ChunkFuture>,
pending_ooo: VecDeque<PinnedFuture<OooChunk>>,
id: Option<Vec<u16>>,
@ -25,10 +26,12 @@ type PinnedFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;
type ChunkFuture = PinnedFuture<VecDeque<StreamChunk>>;
impl StreamBuilder {
/// Creates a new HTML stream.
pub fn new(id: Option<Vec<u16>>) -> Self {
Self::with_capacity(0, id)
}
/// Creates a new stream with a given capacity in the synchronous buffer and an identifier.
pub fn with_capacity(capacity: usize, id: Option<Vec<u16>>) -> Self {
Self {
id,
@ -37,14 +40,17 @@ impl StreamBuilder {
}
}
/// Reserves additional space in the synchronous buffer.
pub fn reserve(&mut self, additional: usize) {
self.sync_buf.reserve(additional);
}
/// Pushes text into the synchronous buffer.
pub fn push_sync(&mut self, string: &str) {
self.sync_buf.push_str(string);
}
/// Pushes an async block into the stream.
pub fn push_async(
&mut self,
fut: impl Future<Output = VecDeque<StreamChunk>> + Send + 'static,
@ -59,10 +65,12 @@ impl StreamBuilder {
});
}
/// Mutates the synchronous buffer.
pub fn with_buf(&mut self, fun: impl FnOnce(&mut String)) {
fun(&mut self.sync_buf)
}
/// Takes all chunks currently available in the stream, including the synchronous buffer.
pub fn take_chunks(&mut self) -> VecDeque<StreamChunk> {
let sync = mem::take(&mut self.sync_buf);
if !sync.is_empty() {
@ -71,11 +79,13 @@ impl StreamBuilder {
mem::take(&mut self.chunks)
}
/// Appends another stream to this one.
pub fn append(&mut self, mut other: StreamBuilder) {
self.chunks.append(&mut other.chunks);
self.sync_buf.push_str(&other.sync_buf);
}
/// Completes the stream.
pub fn finish(mut self) -> Self {
let sync_buf_remaining = mem::take(&mut self.sync_buf);
if sync_buf_remaining.is_empty() {
@ -89,6 +99,7 @@ impl StreamBuilder {
}
// Out-of-Order Streaming
/// Pushes a fallback for out-of-order streaming.
pub fn push_fallback<View, Rndr>(
&mut self,
fallback: View,
@ -103,16 +114,19 @@ impl StreamBuilder {
*position = Position::NextChild;
}
/// Increments the chunk ID.
pub fn next_id(&mut self) {
if let Some(last) = self.id.as_mut().and_then(|ids| ids.last_mut()) {
*last += 1;
}
}
/// Returns the current ID.
pub fn clone_id(&self) -> Option<Vec<u16>> {
self.id.clone()
}
/// Returns an ID that is a child of the current one.
pub fn child_id(&self) -> Option<Vec<u16>> {
let mut child = self.id.clone();
if let Some(child) = child.as_mut() {
@ -121,6 +135,7 @@ impl StreamBuilder {
child
}
/// Inserts a marker for the current out-of-order chunk.
pub fn write_chunk_marker(&mut self, opening: bool) {
if let Some(id) = &self.id {
self.sync_buf.reserve(11 + (id.len() * 2));
@ -136,6 +151,7 @@ impl StreamBuilder {
}
}
/// Injects an out-of-order chunk into the stream.
pub fn push_async_out_of_order<View, Rndr>(
&mut self,
view: impl Future<Output = Option<View>> + Send + 'static,
@ -193,16 +209,23 @@ impl Debug for StreamBuilder {
}
}
/// A chunk of the HTML stream.
pub enum StreamChunk {
/// Some synchronously-available HTML.
Sync(String),
/// The chunk can be rendered asynchronously in order.
Async {
/// A collection of in-order chunks.
chunks: PinnedFuture<VecDeque<StreamChunk>>,
},
/// The chunk can be rendered asynchronously out of order.
OutOfOrder {
/// A collection of out-of-order chunks
chunks: PinnedFuture<OooChunk>,
},
}
/// A chunk of the out-of-order stream.
#[derive(Debug)]
pub struct OooChunk {
id: String,
@ -211,6 +234,7 @@ pub struct OooChunk {
}
impl OooChunk {
/// Pushes an opening `<template>` tag into the buffer.
pub fn push_start(id: &str, buf: &mut String) {
buf.push_str("<template id=\"");
buf.push_str(id);
@ -218,6 +242,7 @@ impl OooChunk {
buf.push_str("\">");
}
/// Pushes a closing `</template>` and update script into the buffer.
pub fn push_end(replace: bool, id: &str, buf: &mut String) {
buf.push_str("</template>");
@ -411,6 +436,7 @@ impl Stream for StreamBuilder {
}
}
/*
#[cfg(test)]
mod tests {
use crate::{
@ -635,3 +661,4 @@ mod tests {
assert!(stream.next().await.is_none());
}
}
*/

View file

@ -15,6 +15,7 @@ macro_rules! svg_elements {
($($tag:ident [$($attr:ty),*]),* $(,)?) => {
paste::paste! {
$(
/// An SVG attribute.
// `tag()` function
#[allow(non_snake_case)]
pub fn $tag<Rndr>() -> HtmlElement<[<$tag:camel>], (), (), Rndr>
@ -31,6 +32,7 @@ macro_rules! svg_elements {
}
}
/// An SVG element.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct [<$tag:camel>];

View file

@ -12,8 +12,10 @@ pub trait AddAnyAttr<Rndr>
where
Rndr: Renderer,
{
/// The new type once the attribute has been added.
type Output<SomeNewAttr: Attribute<Rndr>>: RenderHtml<Rndr>;
/// Adds an attribute to the view.
fn add_any_attr<NewAttr: Attribute<Rndr>>(
self,
attr: NewAttr,
@ -22,6 +24,7 @@ where
Self::Output<NewAttr>: RenderHtml<Rndr>;
}
/// Declares that spreading attributes onto a particular type has no effect.
#[macro_export]
macro_rules! no_attrs {
($ty_name:ty) => {

View file

@ -14,6 +14,16 @@ use std::{
#[cfg(feature = "ssr")]
use std::{future::Future, pin::Pin};
/// A type-erased view. This can be used if control flow requires that multiple different types of
/// view must be received, and it is either impossible or too cumbersome to use the `EitherOf___`
/// enums.
///
/// It can also be used to create recursive components, which otherwise cannot return themselves
/// due to the static typing of the view tree.
///
/// Generally speaking, using `AnyView` restricts the amount of information available to the
/// compiler and should be limited to situations in which it is necessary to preserve the maximum
/// amount of type information possible.
pub struct AnyView<R>
where
R: Renderer,
@ -49,6 +59,7 @@ where
fn(Box<dyn Any>, &Cursor<R>, &PositionState) -> AnyViewState<R>,
}
/// Retained view state for [`AnyView`].
pub struct AnyViewState<R>
where
R: Renderer,
@ -77,10 +88,12 @@ where
}
}
/// Allows converting some view into [`AnyView`].
pub trait IntoAny<R>
where
R: Renderer,
{
/// Converts the view into a type-erased [`AnyView`].
fn into_any(self) -> AnyView<R>;
}

View file

@ -198,11 +198,15 @@ where
/// Stores each value in the view state, overwriting it only if `Some(_)` is provided.
pub struct EitherKeepAlive<A, B> {
/// The first possibility.
pub a: Option<A>,
/// The second possibility.
pub b: Option<B>,
/// If `true`, then `b` will be shown.
pub show_b: bool,
}
/// Retained view state for [`EitherKeepAlive`].
pub struct EitherKeepAliveState<A, B> {
a: Option<A>,
b: Option<B>,
@ -392,12 +396,15 @@ where
macro_rules! tuples {
($num:literal => $($ty:ident),*) => {
paste::paste! {
#[doc = concat!("Retained view state for ", stringify!([<EitherOf $num>]), ".")]
pub struct [<EitherOf $num State>]<$($ty,)* Rndr>
where
$($ty: Render<Rndr>,)*
Rndr: Renderer
{
/// Which child view state is being displayed.
pub state: [<EitherOf $num>]<$($ty::State,)*>,
/// The renderer.
pub rndr: PhantomData<Rndr>
}

View file

@ -7,7 +7,9 @@ pub struct Fragment<R: Renderer> {
pub nodes: Vec<AnyView<R>>,
}
/// Converts some view into a type-erased collection of views.
pub trait IntoFragment<R: Renderer> {
/// Converts some view into a type-erased collection of views.
fn into_fragment(self) -> Fragment<R>;
}

View file

@ -9,6 +9,7 @@ use crate::{
use either_of::Either;
use itertools::Itertools;
/// Retained view state for an `Option`.
pub type OptionState<T, R> =
Either<<T as Render<R>>::State, <() as Render<R>>::State>;
@ -182,6 +183,7 @@ where
}
}
/// Retained view state for a `Vec<_>`.
pub struct VecState<T, R>
where
T: Mountable<R>,

View file

@ -18,6 +18,7 @@ use std::{
type FxIndexSet<T> = IndexSet<T, BuildHasherDefault<FxHasher>>;
/// Creates a keyed list of views.
pub fn keyed<T, I, K, KF, VF, V, Rndr>(
items: I,
key_fn: KF,
@ -39,6 +40,7 @@ where
}
}
/// A keyed list of views.
pub struct Keyed<T, I, K, KF, VF, V, Rndr>
where
I: IntoIterator<Item = T>,
@ -53,6 +55,7 @@ where
rndr: PhantomData<Rndr>,
}
/// Retained view state for a keyed list.
pub struct KeyedState<K, V, Rndr>
where
K: Eq + Hash + 'static,

View file

@ -3,18 +3,29 @@ use crate::{hydration::Cursor, renderer::Renderer, ssr::StreamBuilder};
use parking_lot::RwLock;
use std::{cell::RefCell, future::Future, rc::Rc, sync::Arc};
/// Add attributes to typed views.
pub mod add_attr;
/// A typed-erased view type.
pub mod any_view;
/// Allows choosing between one of several views.
pub mod either;
/// View rendering for `Result<_, _>` types.
pub mod error_boundary;
/// A type-erased view collection.
pub mod fragment;
/// View implementations for several iterable types.
pub mod iterators;
/// Keyed list iteration.
pub mod keyed;
mod primitives;
/// Optimized types for static strings known at compile time.
#[cfg(feature = "nightly")]
pub mod static_types;
/// View implementation for string types.
pub mod strings;
/// Optimizations for creating views via HTML `<template>` nodes.
pub mod template;
/// View implementations for tuples.
pub mod tuples;
/// The `Render` trait allows rendering something as part of the user interface.
@ -35,17 +46,6 @@ pub trait Render<R: Renderer>: Sized {
fn rebuild(self, state: &mut Self::State);
}
#[derive(Debug, Clone, Copy)]
pub struct NeverError;
impl core::fmt::Display for NeverError {
fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
Ok(())
}
}
impl std::error::Error for NeverError {}
/// The `RenderHtml` trait allows rendering something to HTML, and transforming
/// that HTML into an interactive interface.
///
@ -65,11 +65,15 @@ where
/// The type of the view after waiting for all asynchronous data to load.
type AsyncOutput: RenderHtml<R>;
/// The minimum length of HTML created when this view is rendered.
const MIN_LENGTH: usize;
/// Whether this should actually exist in the DOM, if it is the child of an element.
const EXISTS: bool = true;
/// “Runs” the view without other side effects. For primitive types, this is a no-op. For
/// reactive types, this can be used to gather data about reactivity or about asynchronous data
/// that needs to be loaded.
fn dry_resolve(&mut self);
/// Waits for any asynchronous sections of the view to load and returns the output.
@ -282,9 +286,13 @@ where
/// Allows data to be added to a static template.
pub trait ToTemplate {
/// The HTML content of the static template.
const TEMPLATE: &'static str = "";
/// The `class` attribute content known at compile time.
const CLASS: &'static str = "";
/// The `style` attribute content known at compile time.
const STYLE: &'static str = "";
/// The length of the template.
const LEN: usize = Self::TEMPLATE.as_bytes().len();
/// Renders a view type to a template. This does not take actual view data,
@ -299,35 +307,49 @@ pub trait ToTemplate {
);
}
/// Keeps track of what position the item currently being hydrated is in, relative to its siblings
/// and parents.
#[derive(Debug, Default, Clone)]
pub struct PositionState(Arc<RwLock<Position>>);
impl PositionState {
/// Creates a new position tracker.
pub fn new(position: Position) -> Self {
Self(Arc::new(RwLock::new(position)))
}
/// Sets the current position.
pub fn set(&self, position: Position) {
*self.0.write() = position;
}
/// Gets the current position.
pub fn get(&self) -> Position {
*self.0.read()
}
/// Creates a new [`PositionState`], which starts with the same [`Position`], but no longer
/// shares data with this `PositionState`.
pub fn deep_clone(&self) -> Self {
let current = self.get();
Self(Arc::new(RwLock::new(current)))
}
}
/// The position of this element, relative to others.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
pub enum Position {
/// This is the current node.
Current,
/// This is the first child of its parent.
#[default]
FirstChild,
/// This is the next child after another child.
NextChild,
/// This is the next child after a text node.
NextChildAfterText,
/// This is the only child of its parent.
OnlyChild,
/// This is the last child of its parent.
LastChild,
}

View file

@ -42,6 +42,7 @@ impl<K: AttributeKey, const V: &'static str> PartialEq for StaticAttr<K, V> {
}
}
/// Creates an [`Attribute`] whose key and value are both known at compile time.
pub fn static_attr<K: AttributeKey, const V: &'static str>() -> StaticAttr<K, V>
{
StaticAttr { ty: PhantomData }
@ -132,6 +133,8 @@ where
}
}
/// A static string that is known at compile time and can be optimized by including its type in the
/// view tree.
#[derive(Debug, Clone, Copy)]
pub struct Static<const V: &'static str>;

View file

@ -13,8 +13,9 @@ no_attrs!(String);
no_attrs!(Arc<str>);
no_attrs!(Cow<'a, str>);
/// Retained view state for `&str`.
pub struct StrState<'a, R: Renderer> {
pub node: R::Text,
pub(crate) node: R::Text,
str: &'a str,
}
@ -142,6 +143,7 @@ where
}
}
/// Retained view state for `String`.
pub struct StringState<R: Renderer> {
node: R::Text,
str: String,
@ -240,6 +242,7 @@ impl<R: Renderer> Mountable<R> for StringState<R> {
}
}
/// Retained view state for `Rc<str>`.
pub struct RcStrState<R: Renderer> {
node: R::Text,
str: Rc<str>,
@ -331,6 +334,7 @@ impl<R: Renderer> Mountable<R> for RcStrState<R> {
}
}
/// Retained view state for `Arc<str>`.
pub struct ArcStrState<R: Renderer> {
node: R::Text,
str: Arc<str>,
@ -426,6 +430,7 @@ impl<R: Renderer> Mountable<R> for ArcStrState<R> {
}
}
/// Retained view state for `Cow<'_, str>`.
pub struct CowStrState<'a, R: Renderer> {
node: R::Text,
str: Cow<'a, str>,

View file

@ -7,6 +7,11 @@ use crate::{
};
use std::marker::PhantomData;
/// A view wrapper that uses a `<template>` node to optimize DOM node creation.
///
/// Rather than creating all of the DOM nodes each time it is built, this template will create a
/// single `<template>` node once, then use `.cloneNode(true)` to clone that entire tree, and
/// hydrate it to add event listeners and interactivity for this instance.
pub struct ViewTemplate<V, R> {
view: V,
rndr: PhantomData<R>,
@ -17,6 +22,7 @@ where
V: Render<R> + ToTemplate + 'static,
R: DomRenderer,
{
/// Creates a new view template.
pub fn new(view: V) -> Self {
Self {
view,