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 wasm_bindgen::JsCast;
use web_sys::{Document, HtmlElement, Node, Window}; use web_sys::{Document, HtmlElement, Window};
thread_local! { thread_local! {
pub(crate) static WINDOW: web_sys::Window = web_sys::window().unwrap(); 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 /// This is cached as a thread-local variable, so calling `document()` multiple times
/// requires only one call out to JavaScript. /// requires only one call out to JavaScript.
///
/// ## Panics
/// Panics if called outside a browser environment.
pub fn document() -> Document { pub fn document() -> Document {
DOCUMENT.with(Clone::clone) 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 { pub fn body() -> HtmlElement {
document().body().unwrap() 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) /// Helper function to extract [`Event.target`](https://developer.mozilla.org/en-US/docs/Web/API/Event/target)
/// from any event. /// from any event.
pub fn event_target<T>(event: &web_sys::Event) -> T pub fn event_target<T>(event: &web_sys::Event) -> T

View File

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

View File

@ -3,12 +3,14 @@ use crate::{
view::add_attr::AddAnyAttr, view::add_attr::AddAnyAttr,
}; };
/// Applies ARIA attributes to an HTML element.
pub trait AriaAttributes<Rndr, V> pub trait AriaAttributes<Rndr, V>
where where
Self: Sized + AddAnyAttr<Rndr>, Self: Sized + AddAnyAttr<Rndr>,
V: AttributeValue<Rndr>, V: AttributeValue<Rndr>,
Rndr: Renderer, Rndr: Renderer,
{ {
/// Identifies the currently active descendant of a composite widget.
fn aria_activedescendant( fn aria_activedescendant(
self, self,
value: V, value: V,
@ -17,6 +19,7 @@ where
self.add_any_attr(aria_activedescendant(value)) 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( fn aria_atomic(
self, self,
value: V, value: V,
@ -24,6 +27,7 @@ where
self.add_any_attr(aria_atomic(value)) self.add_any_attr(aria_atomic(value))
} }
/// Indicates whether user input completion suggestions are provided.
fn aria_autocomplete( fn aria_autocomplete(
self, self,
value: V, value: V,
@ -32,6 +36,7 @@ where
self.add_any_attr(aria_autocomplete(value)) self.add_any_attr(aria_autocomplete(value))
} }
/// Indicates whether an element, and its subtree, are currently being updated.
fn aria_busy( fn aria_busy(
self, self,
value: V, value: V,
@ -39,6 +44,7 @@ where
self.add_any_attr(aria_busy(value)) self.add_any_attr(aria_busy(value))
} }
/// Indicates the current "checked" state of checkboxes, radio buttons, and other widgets.
fn aria_checked( fn aria_checked(
self, self,
value: V, value: V,
@ -46,6 +52,7 @@ where
self.add_any_attr(aria_checked(value)) self.add_any_attr(aria_checked(value))
} }
/// Defines the number of columns in a table, grid, or treegrid.
fn aria_colcount( fn aria_colcount(
self, self,
value: V, value: V,
@ -53,6 +60,7 @@ where
self.add_any_attr(aria_colcount(value)) 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( fn aria_colindex(
self, self,
value: V, value: V,
@ -60,6 +68,7 @@ where
self.add_any_attr(aria_colindex(value)) 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( fn aria_colspan(
self, self,
value: V, value: V,
@ -67,6 +76,7 @@ where
self.add_any_attr(aria_colspan(value)) 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( fn aria_controls(
self, self,
value: V, value: V,
@ -74,6 +84,7 @@ where
self.add_any_attr(aria_controls(value)) 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( fn aria_current(
self, self,
value: V, value: V,
@ -81,6 +92,7 @@ where
self.add_any_attr(aria_current(value)) self.add_any_attr(aria_current(value))
} }
/// Identifies the element (or elements) that describes the object.
fn aria_describedby( fn aria_describedby(
self, self,
value: V, value: V,
@ -89,6 +101,7 @@ where
self.add_any_attr(aria_describedby(value)) self.add_any_attr(aria_describedby(value))
} }
/// Defines a string value that describes or annotates the current element.
fn aria_description( fn aria_description(
self, self,
value: V, value: V,
@ -97,6 +110,7 @@ where
self.add_any_attr(aria_description(value)) self.add_any_attr(aria_description(value))
} }
/// Identifies the element that provides additional information related to the object.
fn aria_details( fn aria_details(
self, self,
value: V, value: V,
@ -104,6 +118,7 @@ where
self.add_any_attr(aria_details(value)) 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( fn aria_disabled(
self, self,
value: V, value: V,
@ -111,6 +126,7 @@ where
self.add_any_attr(aria_disabled(value)) 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( fn aria_dropeffect(
self, self,
value: V, value: V,
@ -118,6 +134,7 @@ where
self.add_any_attr(aria_dropeffect(value)) self.add_any_attr(aria_dropeffect(value))
} }
/// Defines the element that provides an error message related to the object.
fn aria_errormessage( fn aria_errormessage(
self, self,
value: V, value: V,
@ -126,6 +143,7 @@ where
self.add_any_attr(aria_errormessage(value)) 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( fn aria_expanded(
self, self,
value: V, value: V,
@ -133,6 +151,7 @@ where
self.add_any_attr(aria_expanded(value)) self.add_any_attr(aria_expanded(value))
} }
/// Identifies the next element (or elements) in an alternate reading order of content.
fn aria_flowto( fn aria_flowto(
self, self,
value: V, value: V,
@ -140,6 +159,7 @@ where
self.add_any_attr(aria_flowto(value)) self.add_any_attr(aria_flowto(value))
} }
/// Indicates an element's "grabbed" state in a drag-and-drop operation.
fn aria_grabbed( fn aria_grabbed(
self, self,
value: V, value: V,
@ -147,6 +167,7 @@ where
self.add_any_attr(aria_grabbed(value)) 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( fn aria_haspopup(
self, self,
value: V, value: V,
@ -154,6 +175,7 @@ where
self.add_any_attr(aria_haspopup(value)) self.add_any_attr(aria_haspopup(value))
} }
/// Indicates whether the element is exposed to an accessibility API.
fn aria_hidden( fn aria_hidden(
self, self,
value: V, value: V,
@ -161,6 +183,7 @@ where
self.add_any_attr(aria_hidden(value)) self.add_any_attr(aria_hidden(value))
} }
/// Indicates the entered value does not conform to the format expected by the application.
fn aria_invalid( fn aria_invalid(
self, self,
value: V, value: V,
@ -168,6 +191,7 @@ where
self.add_any_attr(aria_invalid(value)) 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( fn aria_keyshortcuts(
self, self,
value: V, value: V,
@ -176,6 +200,7 @@ where
self.add_any_attr(aria_keyshortcuts(value)) self.add_any_attr(aria_keyshortcuts(value))
} }
/// Defines a string value that labels the current element.
fn aria_label( fn aria_label(
self, self,
value: V, value: V,
@ -183,6 +208,7 @@ where
self.add_any_attr(aria_label(value)) self.add_any_attr(aria_label(value))
} }
/// Identifies the element (or elements) that labels the current element.
fn aria_labelledby( fn aria_labelledby(
self, self,
value: V, value: V,
@ -190,6 +216,7 @@ where
self.add_any_attr(aria_labelledby(value)) 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( fn aria_live(
self, self,
value: V, value: V,
@ -197,6 +224,7 @@ where
self.add_any_attr(aria_live(value)) self.add_any_attr(aria_live(value))
} }
/// Indicates whether an element is modal when displayed.
fn aria_modal( fn aria_modal(
self, self,
value: V, value: V,
@ -204,6 +232,7 @@ where
self.add_any_attr(aria_modal(value)) 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( fn aria_multiline(
self, self,
value: V, value: V,
@ -211,6 +240,7 @@ where
self.add_any_attr(aria_multiline(value)) 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( fn aria_multiselectable(
self, self,
value: V, value: V,
@ -219,6 +249,7 @@ where
self.add_any_attr(aria_multiselectable(value)) self.add_any_attr(aria_multiselectable(value))
} }
/// Indicates whether the element's orientation is horizontal, vertical, or undefined.
fn aria_orientation( fn aria_orientation(
self, self,
value: V, value: V,
@ -227,6 +258,7 @@ where
self.add_any_attr(aria_orientation(value)) 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( fn aria_owns(
self, self,
value: V, value: V,
@ -234,6 +266,7 @@ where
self.add_any_attr(aria_owns(value)) 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( fn aria_placeholder(
self, self,
value: V, value: V,
@ -242,6 +275,7 @@ where
self.add_any_attr(aria_placeholder(value)) 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( fn aria_posinset(
self, self,
value: V, value: V,
@ -249,6 +283,7 @@ where
self.add_any_attr(aria_posinset(value)) self.add_any_attr(aria_posinset(value))
} }
/// Indicates the current "pressed" state of toggle buttons.
fn aria_pressed( fn aria_pressed(
self, self,
value: V, value: V,
@ -256,6 +291,7 @@ where
self.add_any_attr(aria_pressed(value)) self.add_any_attr(aria_pressed(value))
} }
/// Indicates that the element is not editable, but is otherwise operable.
fn aria_readonly( fn aria_readonly(
self, self,
value: V, value: V,
@ -263,6 +299,7 @@ where
self.add_any_attr(aria_readonly(value)) 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( fn aria_relevant(
self, self,
value: V, value: V,
@ -270,6 +307,7 @@ where
self.add_any_attr(aria_relevant(value)) 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( fn aria_required(
self, self,
value: V, value: V,
@ -277,6 +315,7 @@ where
self.add_any_attr(aria_required(value)) self.add_any_attr(aria_required(value))
} }
/// Defines a human-readable, author-localized description for the role of an element.
fn aria_roledescription( fn aria_roledescription(
self, self,
value: V, value: V,
@ -285,6 +324,7 @@ where
self.add_any_attr(aria_roledescription(value)) self.add_any_attr(aria_roledescription(value))
} }
/// Defines the total number of rows in a table, grid, or treegrid.
fn aria_rowcount( fn aria_rowcount(
self, self,
value: V, value: V,
@ -292,6 +332,7 @@ where
self.add_any_attr(aria_rowcount(value)) 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( fn aria_rowindex(
self, self,
value: V, value: V,
@ -299,6 +340,7 @@ where
self.add_any_attr(aria_rowindex(value)) 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( fn aria_rowspan(
self, self,
value: V, value: V,
@ -306,6 +348,7 @@ where
self.add_any_attr(aria_rowspan(value)) self.add_any_attr(aria_rowspan(value))
} }
/// Indicates the current "selected" state of various widgets.
fn aria_selected( fn aria_selected(
self, self,
value: V, value: V,
@ -313,6 +356,7 @@ where
self.add_any_attr(aria_selected(value)) self.add_any_attr(aria_selected(value))
} }
/// Defines the number of items in the current set of listitems or treeitems.
fn aria_setsize( fn aria_setsize(
self, self,
value: V, value: V,
@ -320,6 +364,7 @@ where
self.add_any_attr(aria_setsize(value)) 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( fn aria_sort(
self, self,
value: V, value: V,
@ -327,6 +372,7 @@ where
self.add_any_attr(aria_sort(value)) self.add_any_attr(aria_sort(value))
} }
/// Defines the maximum allowed value for a range widget.
fn aria_valuemax( fn aria_valuemax(
self, self,
value: V, value: V,
@ -334,6 +380,7 @@ where
self.add_any_attr(aria_valuemax(value)) self.add_any_attr(aria_valuemax(value))
} }
/// Defines the minimum allowed value for a range widget.
fn aria_valuemin( fn aria_valuemin(
self, self,
value: V, value: V,
@ -341,6 +388,7 @@ where
self.add_any_attr(aria_valuemin(value)) self.add_any_attr(aria_valuemin(value))
} }
/// Defines the current value for a range widget.
fn aria_valuenow( fn aria_valuenow(
self, self,
value: V, value: V,
@ -348,6 +396,7 @@ where
self.add_any_attr(aria_valuenow(value)) self.add_any_attr(aria_valuenow(value))
} }
/// Defines the human-readable text alternative of `aria-valuenow` for a range widget.
fn aria_valuetext( fn aria_valuetext(
self, self,
value: V, value: V,

View File

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

View File

@ -13,13 +13,16 @@ use crate::{
}; };
use core::convert::From; use core::convert::From;
/// Adds an attribute that modifies the `class`.
pub trait ClassAttribute<C, Rndr> pub trait ClassAttribute<C, Rndr>
where where
C: IntoClass<Rndr>, C: IntoClass<Rndr>,
Rndr: DomRenderer, Rndr: DomRenderer,
{ {
/// The type of the element with the new attribute added.
type Output; type Output;
/// Adds a CSS class to an element.
fn class(self, value: C) -> Self::Output; 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> pub trait PropAttribute<K, P, Rndr>
where where
P: IntoProperty<Rndr>, P: IntoProperty<Rndr>,
Rndr: DomRenderer, Rndr: DomRenderer,
{ {
/// The type of the element with the new attribute added.
type Output; type Output;
/// Adds a DOM property to an element.
fn prop(self, key: K, value: P) -> Self::Output; fn prop(self, key: K, value: P) -> Self::Output;
} }
@ -54,18 +60,22 @@ where
Rndr: DomRenderer, Rndr: DomRenderer,
{ {
type Output = <Self as AddAnyAttr<Rndr>>::Output<Property<K, P, Rndr>>; type Output = <Self as AddAnyAttr<Rndr>>::Output<Property<K, P, Rndr>>;
fn prop(self, key: K, value: P) -> Self::Output { fn prop(self, key: K, value: P) -> Self::Output {
self.add_any_attr(prop(key, value)) self.add_any_attr(prop(key, value))
} }
} }
/// Adds an attribute that modifies the CSS styles.
pub trait StyleAttribute<S, Rndr> pub trait StyleAttribute<S, Rndr>
where where
S: IntoStyle<Rndr>, S: IntoStyle<Rndr>,
Rndr: DomRenderer, Rndr: DomRenderer,
{ {
/// The type of the element with the new attribute added.
type Output; type Output;
/// Adds a CSS style to an element.
fn style(self, value: S) -> Self::Output; 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> { pub trait OnAttribute<E, F, Rndr> {
/// The type of the element with the event listener added.
type Output; type Output;
/// Adds an event listener to an element.
fn on(self, event: E, cb: F) -> Self::Output; 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> { pub trait OnTargetAttribute<E, F, T, Rndr> {
/// The type of the element with the new attribute added.
type Output; type Output;
/// Adds an event listener with a typed target to an element definition.
fn on_target(self, event: E, cb: F) -> Self::Output; 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> pub trait GlobalAttributes<Rndr, V>
where where
Self: Sized + AddAnyAttr<Rndr>, Self: Sized + AddAnyAttr<Rndr>,
V: AttributeValue<Rndr>, V: AttributeValue<Rndr>,
Rndr: Renderer, Rndr: Renderer,
{ {
/// The `accesskey` global attribute provides a hint for generating a keyboard shortcut for the current element.
fn accesskey( fn accesskey(
self, self,
value: V, value: V,
@ -143,6 +161,7 @@ where
self.add_any_attr(accesskey(value)) 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( fn autocapitalize(
self, self,
value: V, value: V,
@ -150,6 +169,7 @@ where
self.add_any_attr(autocapitalize(value)) 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( fn autofocus(
self, self,
value: V, value: V,
@ -157,6 +177,7 @@ where
self.add_any_attr(autofocus(value)) 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( fn contenteditable(
self, self,
value: V, value: V,
@ -165,6 +186,7 @@ where
self.add_any_attr(contenteditable(value)) self.add_any_attr(contenteditable(value))
} }
/// The `dir` global attribute is an enumerated attribute indicating the directionality of the element's text.
fn dir( fn dir(
self, self,
value: V, value: V,
@ -172,6 +194,7 @@ where
self.add_any_attr(dir(value)) self.add_any_attr(dir(value))
} }
/// The `draggable` global attribute is an enumerated attribute indicating whether the element can be dragged.
fn draggable( fn draggable(
self, self,
value: V, value: V,
@ -179,6 +202,7 @@ where
self.add_any_attr(draggable(value)) self.add_any_attr(draggable(value))
} }
/// The `enterkeyhint` global attribute is used to customize the enter key on virtual keyboards.
fn enterkeyhint( fn enterkeyhint(
self, self,
value: V, value: V,
@ -186,6 +210,7 @@ where
self.add_any_attr(enterkeyhint(value)) 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( fn hidden(
self, self,
value: V, value: V,
@ -193,6 +218,7 @@ where
self.add_any_attr(hidden(value)) 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( fn id(
self, self,
value: V, value: V,
@ -200,6 +226,7 @@ where
self.add_any_attr(id(value)) self.add_any_attr(id(value))
} }
/// The `inert` global attribute is a Boolean attribute that makes an element behave inertly.
fn inert( fn inert(
self, self,
value: V, value: V,
@ -207,6 +234,7 @@ where
self.add_any_attr(inert(value)) self.add_any_attr(inert(value))
} }
/// The `inputmode` global attribute provides a hint to browsers for which virtual keyboard to display.
fn inputmode( fn inputmode(
self, self,
value: V, value: V,
@ -214,6 +242,7 @@ where
self.add_any_attr(inputmode(value)) 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( fn is(
self, self,
value: V, value: V,
@ -221,6 +250,7 @@ where
self.add_any_attr(is(value)) self.add_any_attr(is(value))
} }
/// The `itemid` global attribute is used to specify the unique, global identifier of an item.
fn itemid( fn itemid(
self, self,
value: V, value: V,
@ -228,6 +258,7 @@ where
self.add_any_attr(itemid(value)) self.add_any_attr(itemid(value))
} }
/// The `itemprop` global attribute is used to add properties to an item.
fn itemprop( fn itemprop(
self, self,
value: V, value: V,
@ -235,6 +266,7 @@ where
self.add_any_attr(itemprop(value)) self.add_any_attr(itemprop(value))
} }
/// The `itemref` global attribute is used to refer to other elements.
fn itemref( fn itemref(
self, self,
value: V, value: V,
@ -242,6 +274,7 @@ where
self.add_any_attr(itemref(value)) self.add_any_attr(itemref(value))
} }
/// The `itemscope` global attribute is used to create a new item.
fn itemscope( fn itemscope(
self, self,
value: V, value: V,
@ -249,6 +282,7 @@ where
self.add_any_attr(itemscope(value)) self.add_any_attr(itemscope(value))
} }
/// The `itemtype` global attribute is used to specify the types of items.
fn itemtype( fn itemtype(
self, self,
value: V, value: V,
@ -256,6 +290,7 @@ where
self.add_any_attr(itemtype(value)) self.add_any_attr(itemtype(value))
} }
/// The `lang` global attribute helps define the language of an element.
fn lang( fn lang(
self, self,
value: V, value: V,
@ -263,6 +298,7 @@ where
self.add_any_attr(lang(value)) self.add_any_attr(lang(value))
} }
/// The `nonce` global attribute is used to specify a cryptographic nonce.
fn nonce( fn nonce(
self, self,
value: V, value: V,
@ -270,6 +306,7 @@ where
self.add_any_attr(nonce(value)) self.add_any_attr(nonce(value))
} }
/// The `part` global attribute identifies the element as a part of a component.
fn part( fn part(
self, self,
value: V, value: V,
@ -277,6 +314,7 @@ where
self.add_any_attr(part(value)) self.add_any_attr(part(value))
} }
/// The `popover` global attribute defines the popover's behavior.
fn popover( fn popover(
self, self,
value: V, value: V,
@ -284,6 +322,7 @@ where
self.add_any_attr(popover(value)) self.add_any_attr(popover(value))
} }
/// The `role` global attribute defines the role of an element in ARIA.
fn role( fn role(
self, self,
value: V, value: V,
@ -291,6 +330,7 @@ where
self.add_any_attr(role(value)) self.add_any_attr(role(value))
} }
/// The `slot` global attribute assigns a slot in a shadow DOM.
fn slot( fn slot(
self, self,
value: V, value: V,
@ -298,6 +338,7 @@ where
self.add_any_attr(slot(value)) 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( fn spellcheck(
self, self,
value: V, value: V,
@ -305,6 +346,7 @@ where
self.add_any_attr(spellcheck(value)) self.add_any_attr(spellcheck(value))
} }
/// The `tabindex` global attribute indicates if the element can take input focus.
fn tabindex( fn tabindex(
self, self,
value: V, value: V,
@ -312,6 +354,7 @@ where
self.add_any_attr(tabindex(value)) self.add_any_attr(tabindex(value))
} }
/// The `title` global attribute contains text representing advisory information.
fn title( fn title(
self, self,
value: V, value: V,
@ -319,6 +362,7 @@ where
self.add_any_attr(title(value)) 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( fn translate(
self, self,
value: V, value: V,
@ -326,6 +370,7 @@ where
self.add_any_attr(translate(value)) self.add_any_attr(translate(value))
} }
/// The `virtualkeyboardpolicy` global attribute specifies the behavior of the virtual keyboard.
fn virtualkeyboardpolicy( fn virtualkeyboardpolicy(
self, self,
value: V, value: V,

View File

@ -2,14 +2,18 @@ use super::{Attr, AttributeValue};
use crate::renderer::Renderer; use crate::renderer::Renderer;
use std::{fmt::Debug, marker::PhantomData}; use std::{fmt::Debug, marker::PhantomData};
/// An HTML attribute key.
pub trait AttributeKey: Clone + Send + 'static { pub trait AttributeKey: Clone + Send + 'static {
/// The name of the attribute.
const KEY: &'static str; const KEY: &'static str;
} }
macro_rules! attributes { macro_rules! attributes {
($($key:ident $html:literal),* $(,)?) => { ($(#[$meta:meta] $key:ident $html:literal),* $(,)?) => {
paste::paste! { paste::paste! {
$( $(
#[$meta]
#[track_caller]
pub fn $key<V, Rndr>(value: V) -> Attr<[<$key:camel>], V, Rndr> pub fn $key<V, Rndr>(value: V) -> Attr<[<$key:camel>], V, Rndr>
where V: AttributeValue<Rndr>, where V: AttributeValue<Rndr>,
Rndr: Renderer Rndr: Renderer
@ -17,6 +21,7 @@ macro_rules! attributes {
Attr([<$key:camel>], value, PhantomData) Attr([<$key:camel>], value, PhantomData)
} }
#[$meta]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct [<$key:camel>]; pub struct [<$key:camel>];
@ -28,311 +33,618 @@ macro_rules! attributes {
} }
} }
// TODO attribute names with underscores should be kebab-cased
attributes! { attributes! {
// HTML // HTML
abbr "abbr", // [], /// The `abbr` attribute specifies an abbreviated form of the element's content.
accept_charset "accept-charset", // [], abbr "abbr",
accept "accept", // [], /// The `accept-charset` attribute specifies the character encodings that are to be used for the form submission.
accesskey "accesskey", // [], // [GlobalAttribute] accept_charset "accept-charset",
action "action", // [], /// The `accept` attribute specifies a list of types the server accepts, typically a file type.
align "align", // [], accept "accept",
allow "allow", // [], /// The `accesskey` attribute specifies a shortcut key to activate or focus an element.
allowfullscreen "allowfullscreen", // [], accesskey "accesskey",
allowpaymentrequest "allowpaymentrequest", // [], /// The `action` attribute defines the URL to which the form data will be sent.
alt "alt", // [], 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 // 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_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_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", aria_checked "aria-checked",
/// The `aria-colcount` attribute defines the total number of columns in a table, grid, or treegrid.
aria_colcount "aria-colcount", 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", 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_colspan "aria-colspan",
aria_controls "aria-controls", // [], // [GlobalAttribute] // [AriaAttribute], /// The `aria-controls` attribute identifies the element (or elements) whose contents or presence are controlled by the current element.
aria_current "aria-current", // [], // [GlobalAttribute] // [AriaAttribute], aria_controls "aria-controls",
aria_describedby "aria-describedby", // [], // [GlobalAttribute] // [AriaAttribute], /// The `aria-current` attribute indicates the element representing the current item within a container or set of related elements.
aria_description "aria-description", // [], // [GlobalAttribute] // [AriaAttribute], aria_current "aria-current",
aria_details "aria-details", // [], // [GlobalAttribute] // [AriaAttribute], /// The `aria-describedby` attribute identifies the element (or elements) that describes the object.
aria_disabled "aria-disabled", // [], // [GlobalAttribute] // [AriaAttribute], aria_describedby "aria-describedby",
aria_dropeffect "aria-dropeffect", // [], // [GlobalAttribute] // [AriaAttribute], /// The `aria-description` attribute provides a string value that describes or annotates the current element.
aria_errormessage "aria-errormessage", // [], // [GlobalAttribute] // [AriaAttribute], 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_expanded "aria-expanded",
aria_flowto "aria-flowto", // [], // [GlobalAttribute] // [AriaAttribute], /// The `aria-flowto` attribute identifies the next element (or elements) in an alternate reading order of content.
aria_grabbed "aria-grabbed", // [], // [GlobalAttribute] // [AriaAttribute], aria_flowto "aria-flowto",
aria_haspopup "aria-haspopup", // [], // [GlobalAttribute] // [AriaAttribute], /// The `aria-grabbed` attribute indicates an element's "grabbed" state in a drag-and-drop operation.
aria_hidden "aria-hidden", // [], // [GlobalAttribute] // [AriaAttribute], aria_grabbed "aria-grabbed",
aria_invalid "aria-invalid", // [], // [GlobalAttribute] // [AriaAttribute], /// 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_keyshortcuts "aria-keyshortcuts", // [], // [GlobalAttribute] // [AriaAttribute], aria_haspopup "aria-haspopup",
aria_label "aria-label", // [], // [GlobalAttribute] // [AriaAttribute], /// The `aria-hidden` attribute indicates whether the element is exposed to an accessibility API.
aria_labelledby "aria-labelledby", // [], // [GlobalAttribute] // [AriaAttribute], aria_hidden "aria-hidden",
aria_live "aria-live", // [], // [GlobalAttribute] // [AriaAttribute], /// 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", 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", 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", 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_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", aria_placeholder "aria-placeholder",
/// The `aria-posinset` attribute defines an element's position within a set or treegrid.
aria_posinset "aria-posinset", aria_posinset "aria-posinset",
/// The `aria-pressed` attribute indicates the current "pressed" state of toggle buttons.
aria_pressed "aria-pressed", aria_pressed "aria-pressed",
/// The `aria-readonly` attribute indicates that the element is not editable, but is otherwise operable.
aria_readonly "aria-readonly", 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_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", 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", 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", aria_rowspan "aria-rowspan",
/// The `aria-selected` attribute indicates the current "selected" state of various widgets.
aria_selected "aria-selected", 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", 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", aria_sort "aria-sort",
/// The `aria-valuemax` attribute defines the maximum allowed value for a range widget.
aria_valuemax "aria-valuemax", aria_valuemax "aria-valuemax",
/// The `aria-valuemin` attribute defines the minimum allowed value for a range widget.
aria_valuemin "aria-valuemin", aria_valuemin "aria-valuemin",
/// The `aria-valuenow` attribute defines the current value for a range widget.
aria_valuenow "aria-valuenow", 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", aria_valuetext "aria-valuetext",
r#as "as", // [], /// The `as` attribute specifies the type of destination for the content of the link.
r#async "async", // [], r#as "as",
autocapitalize "autocapitalize", // [], // [GlobalAttribute] /// The `async` attribute indicates that the script should be executed asynchronously.
autocomplete "autocomplete", // [], r#async "async",
autofocus "autofocus", // [], // [GlobalAttribute] /// The `autocapitalize` attribute controls whether and how text input is automatically capitalized as it is entered/edited by the user.
autoplay "autoplay", // [], autocapitalize "autocapitalize",
background "background", // [], /// The `autocomplete` attribute indicates whether an input field can have its value automatically completed by the browser.
bgcolor "bgcolor", // [], autocomplete "autocomplete",
blocking "blocking", // [], /// The `autofocus` attribute indicates that an element should be focused on page load.
border "border", // [], autofocus "autofocus",
buffered "buffered", // [], /// The `autoplay` attribute indicates that the media should start playing as soon as it is loaded.
capture "capture", // [], autoplay "autoplay",
challenge "challenge", // [], /// The `background` attribute sets the URL of the background image for the document.
charset "charset", // [], background "background",
checked "checked", // [], /// The `bgcolor` attribute sets the background color of an element.
cite "cite", // [], 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 is handled in ../class.rs instead
//class "class", // [], //class "class",
code "code", // [], /// The `code` attribute specifies the URL of the applet's class file to be loaded and executed.
color "color", // [], code "code",
cols "cols", // [], /// The `color` attribute specifies the color of an element's text.
colspan "colspan", // [], color "color",
content "content", // [], /// The `cols` attribute specifies the visible width of a text area.
contenteditable "contenteditable", // [], // [GlobalAttribute] cols "cols",
contextmenu "contextmenu", // [], // [GlobalAttribute] /// The `colspan` attribute defines the number of columns a cell should span.
controls "controls", // [], colspan "colspan",
controlslist "controlslist", // [], /// The `content` attribute gives the value associated with the http-equiv or name attribute.
coords "coords", // [], content "content",
crossorigin "crossorigin", // [], /// The `contenteditable` attribute indicates whether the element's content is editable.
csp "csp", // [], contenteditable "contenteditable",
data "data", // [], /// The `contextmenu` attribute specifies the ID of a `<menu>` element to open as a context menu.
datetime "datetime", // [], contextmenu "contextmenu",
decoding "decoding", // [], /// The `controls` attribute indicates whether the browser should display playback controls for the media.
default "default", // [], controls "controls",
defer "defer", // [], /// The `controlslist` attribute allows the control of which controls to show on the media element whenever the browser shows its native controls.
dir "dir", // [], // [GlobalAttribute] controlslist "controlslist",
dirname "dirname", // [], /// The `coords` attribute specifies the coordinates of an area in an image map.
disabled "disabled", // [], coords "coords",
disablepictureinpicture "disablepictureinpicture", // [], /// The `crossorigin` attribute indicates whether the resource should be fetched with a CORS request.
disableremoteplayback "disableremoteplayback", // [], crossorigin "crossorigin",
download "download", // [], /// The `csp` attribute allows the embedding document to define the Content Security Policy that an embedded document must agree to enforce upon itself.
draggable "draggable", // [], // [GlobalAttribute] csp "csp",
enctype "enctype", // [], /// The `data` attribute specifies the URL of the resource that is being embedded.
enterkeyhint "enterkeyhint", // [], // [GlobalAttribute] data "data",
exportparts "exportparts", // [], // [GlobalAttribute] /// The `datetime` attribute specifies the date and time.
fetchpriority "fetchprioty", // [], datetime "datetime",
r#for "for", // [], /// The `decoding` attribute indicates the preferred method for decoding images.
form "form", // [], decoding "decoding",
formaction "formaction", // [], /// The `default` attribute indicates that the track should be enabled unless the user's preferences indicate that another track is more appropriate.
formenctype "formenctype", // [], default "default",
formmethod "formmethod", // [], /// The `defer` attribute indicates that the script should be executed after the document has been parsed.
formnovalidate "formnovalidate", // [], defer "defer",
formtarget "formtarget", // [], /// The `dir` attribute specifies the text direction for the content in an element.
headers "headers", // [], dir "dir",
height "height", // [], /// The `dirname` attribute identifies the text directionality of an input element.
hidden "hidden", // [], // [GlobalAttribute] dirname "dirname",
high "high", // [], /// The `disabled` attribute indicates whether the element is disabled.
href "href", // [], disabled "disabled",
hreflang "hreflang", // [], /// The `disablepictureinpicture` attribute indicates that the element is not allowed to be displayed in Picture-in-Picture mode.
http_equiv "http-equiv", // [], disablepictureinpicture "disablepictureinpicture",
icon "icon", // [], /// The `disableremoteplayback` attribute indicates that the element is not allowed to be displayed using remote playback.
id "id", // [], // [GlobalAttribute] 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", imagesizes "imagesizes",
/// The `imagesrcset` attribute specifies the URLs of multiple images to be used in different situations.
imagesrcset "imagesrcset", imagesrcset "imagesrcset",
importance "importance", // [], /// The `importance` attribute specifies the relative importance of the element.
inert "inert", // [], // [GlobalAttribute] importance "importance",
inputmode "inputmode", // [], // [GlobalAttribute] /// The `inert` attribute indicates that the element is non-interactive and won't be accessible to user interactions or assistive technologies.
integrity "integrity", // [], inert "inert",
intrinsicsize "intrinsicsize", // [], /// The `inputmode` attribute specifies the type of data that the user will enter.
is "is", // [], // [GlobalAttribute] inputmode "inputmode",
ismap "ismap", // [], /// The `integrity` attribute contains a hash value that the browser can use to verify that the resource hasn't been altered.
itemid "itemid", // [], // [GlobalAttribute] integrity "integrity",
itemprop "itemprop", // [], // [GlobalAttribute] /// The `intrinsicsize` attribute specifies the intrinsic size of an image or video.
itemref "itemref", // [], // [GlobalAttribute] intrinsicsize "intrinsicsize",
itemscope "itemscope", // [], // [GlobalAttribute] /// The `is` attribute allows you to specify the name of a custom element.
itemtype "itemtype", // [], // [GlobalAttribute] is "is",
keytype "keytype", // [], /// The `ismap` attribute indicates that the image is part of a server-side image map.
kind "kind", // [], ismap "ismap",
label "label", // [], /// The `itemid` attribute assigns a unique identifier to an item.
lang "lang", // [], // [GlobalAttribute] itemid "itemid",
language "language", // [], /// The `itemprop` attribute adds a property to an item.
list "list", // [], itemprop "itemprop",
loading "loading", // [], /// The `itemref` attribute provides a list of element IDs that have additional properties for the item.
r#loop "loop", // [], itemref "itemref",
low "low", // [], /// The `itemscope` attribute creates a new item and adds it to the page's items.
manifest "manifest", // [], itemscope "itemscope",
max "max", // [], /// The `itemtype` attribute specifies the type of an item.
maxlength "maxlength", // [], itemtype "itemtype",
media "media", // [], /// The `keytype` attribute specifies the type of key used by the `<keygen>` element.
method "method", // [], keytype "keytype",
min "min", // [], /// The `kind` attribute specifies the kind of text track.
minlength "minlength", // [], kind "kind",
multiple "multiple", // [], /// The `label` attribute provides a user-readable title for an element.
muted "muted", // [], label "label",
name "name", // [], /// The `lang` attribute specifies the language of the element's content.
nomodule "nomodule", // [], lang "lang",
nonce "nonce", // [], // [GlobalAttribute] /// The `language` attribute specifies the scripting language used for the script.
novalidate "novalidate", // [], language "language",
open "open", // [], /// The `list` attribute identifies a `<datalist>` element that contains pre-defined options for an `<input>` element.
optimum "optimum", // [], list "list",
part "part", // [], // [GlobalAttribute] /// The `loading` attribute indicates how the browser should load the image.
pattern "pattern", // [], loading "loading",
ping "ping", // [], /// The `loop` attribute indicates whether the media should start over again when it reaches the end.
placeholder "placeholder", // [], r#loop "loop",
playsinline "playsinline", // [], /// The `low` attribute specifies the range that is considered to be a low value.
popover "popover", // [], // [GlobalAttribute] 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", popovertarget "popovertarget",
/// The `popovertargetaction` attribute specifies the action that shows the popover.
popovertargetaction "popovertargetaction", popovertargetaction "popovertargetaction",
poster "poster", // [], /// The `poster` attribute specifies an image to be shown while the video is downloading or until the user hits the play button.
preload "preload", // [], poster "poster",
radiogroup "radiogroup", // [], /// The `preload` attribute specifies if and how the author thinks that the media file should be loaded when the page loads.
readonly "readonly", // [], preload "preload",
referrerpolicy "referrerpolicy", // [], /// The `radiogroup` attribute specifies the name of the group to which the element belongs.
rel "rel", // [], radiogroup "radiogroup",
required "required", // [], /// The `readonly` attribute indicates that the user cannot modify the value of the input element.
reversed "reversed", // [], readonly "readonly",
role "role", // [], // [GlobalAttribute] // [AriaAttribute], /// The `referrerpolicy` attribute specifies which referrer information to include with requests.
rows "rows", // [], referrerpolicy "referrerpolicy",
rowspan "rowspan", // [], /// The `rel` attribute specifies the relationship between the current document and the linked document.
sandbox "sandbox", // [], rel "rel",
scope "scope", // [], /// The `required` attribute indicates that the user must fill in the input element before submitting the form.
scoped "scoped", // [], required "required",
selected "selected", // [], /// The `reversed` attribute indicates that the list should be displayed in a descending order.
shape "shape", // [], reversed "reversed",
size "size", // [], /// The `role` attribute defines the role of an element in the context of a web application.
sizes "sizes", // [], role "role",
slot "slot", // [], // [GlobalAttribute] /// The `rows` attribute specifies the number of visible text lines for a text area.
span "span", // [], rows "rows",
spellcheck "spellcheck", // [], // [GlobalAttribute] /// The `rowspan` attribute defines the number of rows a cell should span.
src "src", // [], rowspan "rowspan",
srcdoc "srcdoc", // [], /// The `sandbox` attribute applies extra restrictions to the content in the `<iframe>`.
srclang "srclang", // [], sandbox "sandbox",
srcset "srcset", // [], /// The `scope` attribute specifies whether a header cell is a header for a column, row, or group of columns or rows.
start "start", // [], scope "scope",
step "step", // [], /// 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 is handled in ../style.rs instead
// style "style", // [], // style "style",
summary "summary", // [], /// The `summary` attribute provides a summary of the content of the table.
tabindex "tabindex", // [], // [GlobalAttribute] summary "summary",
target "target", // [], /// The `tabindex` attribute specifies the tab order of an element.
title "title", // [], // [GlobalAttribute] tabindex "tabindex",
translate "translate", // [], // [GlobalAttribute] /// The `target` attribute specifies where to open the linked document.
r#type "type", // [], target "target",
usemap "usemap", // [], /// The `title` attribute provides additional information about an element.
value "value", // [], title "title",
virtualkeyboardpolicy "virtualkeyboardpolicy", // [], // [GlobalAttribute] /// The `translate` attribute specifies whether the content of an element should be translated or not.
width "width", // [], translate "translate",
wrap "wrap", // [], /// 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 // Event Handler Attributes
onabort "onabort", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `onabort` attribute specifies the event handler for the abort event.
onautocomplete "onautocomplete", // [], // [GlobalAttribute] // [EventHandlerAttribute], onabort "onabort",
onautocompleteerror "onautocompleteerror", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `onautocomplete` attribute specifies the event handler for the autocomplete event.
onblur "onblur", // [], // [GlobalAttribute] // [EventHandlerAttribute], onautocomplete "onautocomplete",
oncancel "oncancel", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `onautocompleteerror` attribute specifies the event handler for the autocompleteerror event.
oncanplay "oncanplay", // [], // [GlobalAttribute] // [EventHandlerAttribute], onautocompleteerror "onautocompleteerror",
oncanplaythrough "oncanplaythrough", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `onblur` attribute specifies the event handler for the blur event.
onchange "onchange", // [], // [GlobalAttribute] // [EventHandlerAttribute], onblur "onblur",
onclick "onclick", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `oncancel` attribute specifies the event handler for the cancel event.
onclose "onclose", // [], // [GlobalAttribute] // [EventHandlerAttribute], oncancel "oncancel",
oncontextmenu "oncontextmenu", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `oncanplay` attribute specifies the event handler for the canplay event.
oncuechange "oncuechange", // [], // [GlobalAttribute] // [EventHandlerAttribute], oncanplay "oncanplay",
ondblclick "ondblclick", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `oncanplaythrough` attribute specifies the event handler for the canplaythrough event.
ondrag "ondrag", // [], // [GlobalAttribute] // [EventHandlerAttribute], oncanplaythrough "oncanplaythrough",
ondragend "ondragend", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `onchange` attribute specifies the event handler for the change event.
ondragenter "ondragenter", // [], // [GlobalAttribute] // [EventHandlerAttribute], onchange "onchange",
ondragleave "ondragleave", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `onclick` attribute specifies the event handler for the click event.
ondragover "ondragover", // [], // [GlobalAttribute] // [EventHandlerAttribute], onclick "onclick",
ondragstart "ondragstart", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `onclose` attribute specifies the event handler for the close event.
ondrop "ondrop", // [], // [GlobalAttribute] // [EventHandlerAttribute], onclose "onclose",
ondurationchange "ondurationchange", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `oncontextmenu` attribute specifies the event handler for the contextmenu event.
onemptied "onemptied", // [], // [GlobalAttribute] // [EventHandlerAttribute], oncontextmenu "oncontextmenu",
onended "onended", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `oncuechange` attribute specifies the event handler for the cuechange event.
onerror "onerror", // [], // [GlobalAttribute] // [EventHandlerAttribute], oncuechange "oncuechange",
onfocus "onfocus", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `ondblclick` attribute specifies the event handler for the double click event.
oninput "oninput", // [], // [GlobalAttribute] // [EventHandlerAttribute], ondblclick "ondblclick",
oninvalid "oninvalid", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `ondrag` attribute specifies the event handler for the drag event.
onkeydown "onkeydown", // [], // [GlobalAttribute] // [EventHandlerAttribute], ondrag "ondrag",
onkeypress "onkeypress", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `ondragend` attribute specifies the event handler for the dragend event.
onkeyup "onkeyup", // [], // [GlobalAttribute] // [EventHandlerAttribute], ondragend "ondragend",
onload "onload", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `ondragenter` attribute specifies the event handler for the dragenter event.
onloadeddata "onloadeddata", // [], // [GlobalAttribute] // [EventHandlerAttribute], ondragenter "ondragenter",
onloadedmetadata "onloadedmetadata", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `ondragleave` attribute specifies the event handler for the dragleave event.
onloadstart "onloadstart", // [], // [GlobalAttribute] // [EventHandlerAttribute], ondragleave "ondragleave",
onmousedown "onmousedown", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `ondragover` attribute specifies the event handler for the dragover event.
onmouseenter "onmouseenter", // [], // [GlobalAttribute] // [EventHandlerAttribute], ondragover "ondragover",
onmouseleave "onmouseleave", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `ondragstart` attribute specifies the event handler for the dragstart event.
onmousemove "onmousemove", // [], // [GlobalAttribute] // [EventHandlerAttribute], ondragstart "ondragstart",
onmouseout "onmouseout", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `ondrop` attribute specifies the event handler for the drop event.
onmouseover "onmouseover", // [], // [GlobalAttribute] // [EventHandlerAttribute], ondrop "ondrop",
onmouseup "onmouseup", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `ondurationchange` attribute specifies the event handler for the durationchange event.
onmousewheel "onmousewheel", // [], // [GlobalAttribute] // [EventHandlerAttribute], ondurationchange "ondurationchange",
onpause "onpause", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `onemptied` attribute specifies the event handler for the emptied event.
onplay "onplay", // [], // [GlobalAttribute] // [EventHandlerAttribute], onemptied "onemptied",
onplaying "onplaying", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `onended` attribute specifies the event handler for the ended event.
onprogress "onprogress", // [], // [GlobalAttribute] // [EventHandlerAttribute], onended "onended",
onratechange "onratechange", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `onerror` attribute specifies the event handler for the error event.
onreset "onreset", // [], // [GlobalAttribute] // [EventHandlerAttribute], onerror "onerror",
onresize "onresize", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `onfocus` attribute specifies the event handler for the focus event.
onscroll "onscroll", // [], // [GlobalAttribute] // [EventHandlerAttribute], onfocus "onfocus",
onseeked "onseeked", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `onformdata` attribute specifies the event handler for the formdata event.
onseeking "onseeking", // [], // [GlobalAttribute] // [EventHandlerAttribute], onformdata "onformdata",
onselect "onselect", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `oninput` attribute specifies the event handler for the input event.
onshow "onshow", // [], // [GlobalAttribute] // [EventHandlerAttribute], oninput "oninput",
onsort "onsort", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `oninvalid` attribute specifies the event handler for the invalid event.
onstalled "onstalled", // [], // [GlobalAttribute] // [EventHandlerAttribute], oninvalid "oninvalid",
onsubmit "onsubmit", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `onkeydown` attribute specifies the event handler for the keydown event.
onsuspend "onsuspend", // [], // [GlobalAttribute] // [EventHandlerAttribute], onkeydown "onkeydown",
ontimeupdate "ontimeupdate", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `onkeypress` attribute specifies the event handler for the keypress event.
ontoggle "ontoggle", // [], // [GlobalAttribute] // [EventHandlerAttribute], onkeypress "onkeypress",
onvolumechange "onvolumechange", // [], // [GlobalAttribute] // [EventHandlerAttribute], /// The `onkeyup` attribute specifies the event handler for the keyup event.
onwaiting "onwaiting", // [], // [GlobalAttribute] // [EventHandlerAttribute], 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", accent "accent",
/// The `accentunder` attribute specifies whether the element should be treated as an accent under the base element.
accentunder "accentunder", accentunder "accentunder",
/// The `columnalign` attribute specifies the alignment of columns.
columnalign "columnalign", columnalign "columnalign",
/// The `columnlines` attribute specifies the presence of lines between columns.
columnlines "columnlines", columnlines "columnlines",
/// The `columnspacing` attribute specifies the spacing between columns.
columnspacing "columnspacing", columnspacing "columnspacing",
/// The `columnspan` attribute specifies the number of columns the element should span.
columnspan "columnspan", columnspan "columnspan",
/// The `depth` attribute specifies the depth of the element.
depth "depth", depth "depth",
/// The `display` attribute specifies the display style of the element.
display "display", display "display",
/// The `displaystyle` attribute specifies whether the element is displayed in display style.
displaystyle "displaystyle", displaystyle "displaystyle",
/// The `fence` attribute specifies whether the element should act as a fence.
fence "fence", fence "fence",
/// The `frame` attribute specifies the type of frame for the element.
frame "frame", frame "frame",
/// The `framespacing` attribute specifies the spacing around frames.
framespacing "framespacing", framespacing "framespacing",
/// The `linethickness` attribute specifies the thickness of lines.
linethickness "linethickness", linethickness "linethickness",
/// The `lspace` attribute specifies the space on the left side of the element.
lspace "lspace", lspace "lspace",
/// The `mathbackground` attribute specifies the background color of the element.
mathbackground "mathbackground", mathbackground "mathbackground",
/// The `mathcolor` attribute specifies the color of the element.
mathcolor "mathcolor", mathcolor "mathcolor",
/// The `mathsize` attribute specifies the size of the element.
mathsize "mathsize", mathsize "mathsize",
/// The `mathvariant` attribute specifies the mathematical variant of the element.
mathvariant "mathvariant", mathvariant "mathvariant",
/// The `maxsize` attribute specifies the maximum size of the element.
maxsize "maxsize", maxsize "maxsize",
/// The `minsize` attribute specifies the minimum size of the element.
minsize "minsize", minsize "minsize",
/// The `movablelimits` attribute specifies whether the limits of the element are movable.
movablelimits "movablelimits", movablelimits "movablelimits",
/// The `notation` attribute specifies the type of notation for the element.
notation "notation", notation "notation",
/// The `rowalign` attribute specifies the alignment of rows.
rowalign "rowalign", rowalign "rowalign",
/// The `rowlines` attribute specifies the presence of lines between rows.
rowlines "rowlines", rowlines "rowlines",
/// The `rowspacing` attribute specifies the spacing between rows.
rowspacing "rowspacing", rowspacing "rowspacing",
/// The `rspace` attribute specifies the space on the right side of the element.
rspace "rspace", rspace "rspace",
/// The `scriptlevel` attribute specifies the script level of the element.
scriptlevel "scriptlevel", scriptlevel "scriptlevel",
/// The `separator` attribute specifies whether the element is a separator.
separator "separator", separator "separator",
/// The `stretchy` attribute specifies whether the element is stretchy.
stretchy "stretchy", stretchy "stretchy",
/// The `symmetric` attribute specifies whether the element is symmetric.
symmetric "symmetric", symmetric "symmetric",
/// The `voffset` attribute specifies the vertical offset of the element.
voffset "voffset", voffset "voffset",
/// The `xmlns` attribute specifies the XML namespace of the element.
xmlns "xmlns", xmlns "xmlns",
} }

View File

@ -1,6 +1,10 @@
/// A type-erased `AnyAttribute`.
pub mod any_attribute; pub mod any_attribute;
/// Types for ARIA attributes.
pub mod aria; pub mod aria;
/// Types for custom attributes.
pub mod custom; pub mod custom;
/// Traits to define global attribute methods on all HTML elements.
pub mod global; pub mod global;
mod key; mod key;
mod value; mod value;
@ -12,16 +16,29 @@ pub use key::*;
use std::{fmt::Debug, future::Future, marker::PhantomData}; use std::{fmt::Debug, future::Future, marker::PhantomData};
pub use value::*; pub use value::*;
/// Defines an attribute: anything that can modify an element.
pub trait Attribute<R: Renderer>: NextAttribute<R> + Send { pub trait Attribute<R: Renderer>: NextAttribute<R> + Send {
/// The minimum length of this attribute in HTML.
const MIN_LENGTH: usize; const MIN_LENGTH: usize;
/// The state that should be retained between building and rebuilding.
type State; type State;
/// The type once all async data have loaded.
type AsyncOutput: Attribute<R>; type AsyncOutput: Attribute<R>;
/// An equivalent to this attribute that can be cloned to be shared across elements.
type Cloneable: Attribute<R> + Clone; 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; type CloneableOwned: Attribute<R> + Clone + 'static;
/// An approximation of the actual length of this attribute in HTML.
fn html_len(&self) -> usize; 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( fn to_html(
self, self,
buf: &mut String, buf: &mut String,
@ -30,24 +47,39 @@ pub trait Attribute<R: Renderer>: NextAttribute<R> + Send {
inner_html: &mut String, 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; 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; fn build(self, el: &R::Element) -> Self::State;
/// Applies a new value for the attribute.
fn rebuild(self, state: &mut Self::State); fn rebuild(self, state: &mut Self::State);
/// Converts this attribute into an equivalent that can be cloned.
fn into_cloneable(self) -> Self::Cloneable; 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; 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); 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; 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> { pub trait NextAttribute<R: Renderer> {
/// The type of the new, combined attribute.
type Output<NewAttr: Attribute<R>>: Attribute<R>; type Output<NewAttr: Attribute<R>>: Attribute<R>;
/// Adds a new attribute.
fn add_any_attr<NewAttr: Attribute<R>>( fn add_any_attr<NewAttr: Attribute<R>>(
self, self,
new_attr: NewAttr, new_attr: NewAttr,
@ -112,6 +144,7 @@ where
} }
} }
/// An attribute with a key and value.
#[derive(Debug)] #[derive(Debug)]
pub struct Attr<K, V, R>(pub K, pub V, pub PhantomData<R>) pub struct Attr<K, V, R>(pub K, pub V, pub PhantomData<R>)
where where

View File

@ -11,8 +11,12 @@ use std::{
sync::Arc, sync::Arc,
}; };
/// A possible value for an HTML attribute.
pub trait AttributeValue<R: Renderer>: Send { pub trait AttributeValue<R: Renderer>: Send {
/// The state that should be retained between building and rebuilding.
type State; type State;
/// The type once all async data have loaded.
type AsyncOutput: AttributeValue<R>; type AsyncOutput: AttributeValue<R>;
/// A version of the value that can be cloned. This can be the same type, or a /// 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. /// cloneable type has worse performance than the cloneable type, so they are separate.
type CloneableOwned: AttributeValue<R> + Clone + 'static; type CloneableOwned: AttributeValue<R> + Clone + 'static;
/// An approximation of the actual length of this attribute in HTML.
fn html_len(&self) -> usize; fn html_len(&self) -> usize;
/// Renders the attribute value to HTML.
fn to_html(self, key: &str, buf: &mut String); 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); 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>( fn hydrate<const FROM_SERVER: bool>(
self, self,
key: &str, key: &str,
el: &R::Element, el: &R::Element,
) -> Self::State; ) -> Self::State;
/// Adds this attribute to the element during client-side rendering.
fn build(self, el: &R::Element, key: &str) -> Self::State; 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); 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; 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; 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); 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; 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}; use std::{future::Future, marker::PhantomData, sync::Arc};
/// Adds a CSS class.
#[inline(always)] #[inline(always)]
pub fn class<C, R>(class: C) -> Class<C, R> pub fn class<C, R>(class: C) -> Class<C, R>
where where
@ -17,6 +18,7 @@ where
} }
} }
/// A CSS class.
#[derive(Debug)] #[derive(Debug)]
pub struct Class<C, R> { pub struct Class<C, R> {
class: C, class: C,
@ -133,34 +135,54 @@ where
} }
} }
/// A possible value for a CSS class.
pub trait IntoClass<R: DomRenderer>: Send { pub trait IntoClass<R: DomRenderer>: Send {
/// The HTML that should be included in a `<template>`.
const TEMPLATE: &'static str = ""; const TEMPLATE: &'static str = "";
/// The minimum length of the HTML.
const MIN_LENGTH: usize = Self::TEMPLATE.len(); const MIN_LENGTH: usize = Self::TEMPLATE.len();
/// The type after all async data have resolved.
type AsyncOutput: IntoClass<R>; type AsyncOutput: IntoClass<R>;
/// The view state retained between building and rebuilding.
type State; type State;
/// An equivalent value that can be cloned.
type Cloneable: IntoClass<R> + Clone; type Cloneable: IntoClass<R> + Clone;
/// An equivalent value that can be cloned and is `'static`.
type CloneableOwned: IntoClass<R> + Clone + 'static; type CloneableOwned: IntoClass<R> + Clone + 'static;
/// The estimated length of the HTML.
fn html_len(&self) -> usize; fn html_len(&self) -> usize;
/// Renders the class to HTML.
fn to_html(self, class: &mut String); fn to_html(self, class: &mut String);
/// Renders the class to HTML for a `<template>`.
#[allow(unused)] // it's used with `nightly` feature #[allow(unused)] // it's used with `nightly` feature
fn to_template(class: &mut String) {} 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; 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; fn build(self, el: &R::Element) -> Self::State;
/// Updates the value.
fn rebuild(self, state: &mut Self::State); fn rebuild(self, state: &mut Self::State);
/// Converts this to a cloneable type.
fn into_cloneable(self) -> Self::Cloneable; fn into_cloneable(self) -> Self::Cloneable;
/// Converts this to a cloneable, owned type.
fn into_cloneable_owned(self) -> Self::CloneableOwned; 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); 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; fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;
} }

View File

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

View File

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

View File

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

View File

@ -6,6 +6,14 @@ use crate::{
}; };
use std::{future::Future, marker::PhantomData, sync::Arc}; 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)] #[inline(always)]
pub fn inner_html<T, R>(value: T) -> InnerHtml<T, R> pub fn inner_html<T, R>(value: T) -> InnerHtml<T, R>
where where
@ -18,6 +26,7 @@ where
} }
} }
/// Sets the inner HTML of an element.
#[derive(Debug)] #[derive(Debug)]
pub struct InnerHtml<T, R> { pub struct InnerHtml<T, R> {
value: T, value: T,
@ -118,12 +127,21 @@ where
} }
} }
/// Sets the inner HTML of an element.
pub trait InnerHtmlAttribute<T, Rndr> pub trait InnerHtmlAttribute<T, Rndr>
where where
T: InnerHtmlValue<Rndr>, T: InnerHtmlValue<Rndr>,
Rndr: DomRenderer, Rndr: DomRenderer,
Self: Sized + AddAnyAttr<Rndr>, 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( fn inner_html(
self, self,
value: T, value: T,
@ -149,30 +167,48 @@ where
} }
} }
/// A possible value for [`InnerHtml`].
pub trait InnerHtmlValue<R: DomRenderer>: Send { pub trait InnerHtmlValue<R: DomRenderer>: Send {
/// The type after all async data have resolved.
type AsyncOutput: InnerHtmlValue<R>; type AsyncOutput: InnerHtmlValue<R>;
/// The view state retained between building and rebuilding.
type State; type State;
/// An equivalent value that can be cloned.
type Cloneable: InnerHtmlValue<R> + Clone; type Cloneable: InnerHtmlValue<R> + Clone;
/// An equivalent value that can be cloned and is `'static`.
type CloneableOwned: InnerHtmlValue<R> + Clone + 'static; type CloneableOwned: InnerHtmlValue<R> + Clone + 'static;
/// The estimated length of the HTML.
fn html_len(&self) -> usize; fn html_len(&self) -> usize;
/// Renders the class to HTML.
fn to_html(self, buf: &mut String); fn to_html(self, buf: &mut String);
/// Renders the class to HTML for a `<template>`.
fn to_template(buf: &mut String); 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; 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; fn build(self, el: &R::Element) -> Self::State;
/// Updates the value.
fn rebuild(self, state: &mut Self::State); fn rebuild(self, state: &mut Self::State);
/// Converts this to a cloneable type.
fn into_cloneable(self) -> Self::Cloneable; fn into_cloneable(self) -> Self::Cloneable;
/// Converts this to a cloneable, owned type.
fn into_cloneable_owned(self) -> Self::CloneableOwned; 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); 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; fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;
} }

View File

@ -25,6 +25,7 @@ pub use element_ext::*;
pub use elements::*; pub use elements::*;
pub use inner_html::*; pub use inner_html::*;
/// The typed representation of an HTML element.
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct HtmlElement<E, At, Ch, Rndr> { pub struct HtmlElement<E, At, Ch, Rndr> {
pub(crate) tag: E, 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> impl<E, At, Ch, NewChild, Rndr> ElementChild<Rndr, NewChild>
for HtmlElement<E, At, Ch, Rndr> for HtmlElement<E, At, Ch, Rndr>
where where
@ -136,34 +119,48 @@ where
} }
} }
/// Adds a child to the element.
pub trait ElementChild<Rndr, NewChild> pub trait ElementChild<Rndr, NewChild>
where where
NewChild: Render<Rndr>, NewChild: Render<Rndr>,
Rndr: Renderer, Rndr: Renderer,
{ {
/// The type of the element, with the child added.
type Output; type Output;
/// Adds a child to an element.
fn child(self, child: NewChild) -> Self::Output; fn child(self, child: NewChild) -> Self::Output;
} }
/// An HTML element.
pub trait ElementType: Send { pub trait ElementType: Send {
/// The underlying native widget type that this represents. /// The underlying native widget type that this represents.
type Output; type Output;
/// The element's tag.
const TAG: &'static str; const TAG: &'static str;
/// Whether the element is self-closing.
const SELF_CLOSING: bool; 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; const ESCAPE_CHILDREN: bool;
/// The element's tag.
fn tag(&self) -> &str; fn tag(&self) -> &str;
} }
/// Denotes that the type that implements this has a particular HTML element type.
pub trait HasElementType { pub trait HasElementType {
/// The element type.
type ElementType; type ElementType;
} }
pub trait ElementWithChildren {} pub(crate) trait ElementWithChildren {}
/// Creates an element.
pub trait CreateElement<R: Renderer> { pub trait CreateElement<R: Renderer> {
/// Creates an element.
fn create_element(&self) -> R::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 pub fn attributes_to_html<At, R>(attr: At, buf: &mut String) -> String
where where
At: Attribute<R>, At: Attribute<R>,
@ -423,10 +421,11 @@ where
inner_html inner_html
} }
/// The retained view state for an HTML element.
pub struct ElementState<At, Ch, R: Renderer> { pub struct ElementState<At, Ch, R: Renderer> {
pub el: R::Element, pub(crate) el: R::Element,
pub attrs: At, pub(crate) attrs: At,
pub children: Option<Ch>, pub(crate) children: Option<Ch>,
rndr: PhantomData<R>, rndr: PhantomData<R>,
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,11 +5,9 @@ use crate::{
renderer::DomRenderer, renderer::DomRenderer,
view::{Position, ToTemplate}, 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. /// Returns an [`Attribute`] that will add to an element's CSS styles.
///
/// This can take a plain string value, which will be assigned to the `style`
#[inline(always)] #[inline(always)]
pub fn style<S, R>(style: S) -> Style<S, R> pub fn style<S, R>(style: S) -> Style<S, R>
where where
@ -22,6 +20,7 @@ where
} }
} }
/// An [`Attribute`] that will add to an element's CSS styles.
#[derive(Debug)] #[derive(Debug)]
pub struct Style<S, R> { pub struct Style<S, R> {
style: S, style: S,
@ -140,42 +139,43 @@ where
/// Any type that can be added to the `style` attribute or set as a style in /// 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. /// the [`CssStyleDeclaration`]. This could be a plain string, or a property name-value pair.
pub trait IntoStyle<R: DomRenderer>: Send { pub trait IntoStyle<R: DomRenderer>: Send {
/// The type after all async data have resolved.
type AsyncOutput: IntoStyle<R>; type AsyncOutput: IntoStyle<R>;
/// The view state retained between building and rebuilding.
type State; type State;
/// An equivalent value that can be cloned.
type Cloneable: IntoStyle<R> + Clone; type Cloneable: IntoStyle<R> + Clone;
/// An equivalent value that can be cloned and is `'static`.
type CloneableOwned: IntoStyle<R> + Clone + 'static; type CloneableOwned: IntoStyle<R> + Clone + 'static;
/// Renders the style to HTML.
fn to_html(self, style: &mut String); 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; 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; fn build(self, el: &R::Element) -> Self::State;
/// Updates the value.
fn rebuild(self, state: &mut Self::State); fn rebuild(self, state: &mut Self::State);
/// Converts this to a cloneable type.
fn into_cloneable(self) -> Self::Cloneable; fn into_cloneable(self) -> Self::Cloneable;
/// Converts this to a cloneable, owned type.
fn into_cloneable_owned(self) -> Self::CloneableOwned; 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); 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; 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 impl<'a, R> IntoStyle<R> for &'a str
where where
R: DomRenderer, R: DomRenderer,

View File

@ -4,6 +4,11 @@ use crate::{
}; };
use std::{cell::RefCell, rc::Rc}; 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)] #[derive(Debug)]
pub struct Cursor<R: Renderer>(Rc<RefCell<R::Node>>); pub struct Cursor<R: Renderer>(Rc<RefCell<R::Node>>);
@ -19,14 +24,19 @@ where
R::Element: AsRef<R::Node>, R::Element: AsRef<R::Node>,
{ {
/// Creates a new cursor starting at the root element.
pub fn new(root: R::Element) -> Self { pub fn new(root: R::Element) -> Self {
Self(Rc::new(RefCell::new(root.as_ref().clone()))) 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 { pub fn current(&self) -> R::Node {
self.0.borrow().clone() 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) { pub fn child(&self) {
//crate::log("advancing to next child of "); //crate::log("advancing to next child of ");
//R::log_node(&self.current()); //R::log_node(&self.current());
@ -39,6 +49,9 @@ where
//R::log_node(&self.current()); //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) { pub fn sibling(&self) {
//crate::log("advancing to next sibling of "); //crate::log("advancing to next sibling of ");
//R::log_node(&self.current()); //R::log_node(&self.current());
@ -51,6 +64,9 @@ where
//R::log_node(&self.current()); //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) { pub fn parent(&self) {
let mut inner = self.0.borrow_mut(); let mut inner = self.0.borrow_mut();
if let Some(node) = R::get_parent(&*inner) { 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) { pub fn set(&self, node: R::Node) {
*self.0.borrow_mut() = node; *self.0.borrow_mut() = node;
} }
/// Advances to the next placeholder node.
pub fn next_placeholder(&self, position: &PositionState) -> R::Placeholder { pub fn next_placeholder(&self, position: &PositionState) -> R::Placeholder {
//crate::dom::log("looking for placeholder after"); //crate::dom::log("looking for placeholder after");
//R::log_node(&self.current()); //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 #![allow(incomplete_features)] // yolo
#![cfg_attr(feature = "nightly", feature(adt_const_params))] #![cfg_attr(feature = "nightly", feature(adt_const_params))]
#![deny(missing_docs)]
/// Commonly-used traits.
pub mod prelude { pub mod prelude {
#[cfg(feature = "reactive_graph")]
pub use crate::reactive_graph::FutureViewExt;
pub use crate::{ pub use crate::{
html::{ html::{
attribute::{ attribute::{
@ -29,30 +34,38 @@ pub mod prelude {
use wasm_bindgen::JsValue; use wasm_bindgen::JsValue;
use web_sys::Node; use web_sys::Node;
/// Helpers for interacting with the DOM.
pub mod dom; pub mod dom;
/// Types for building a statically-typed HTML view tree.
pub mod html; pub mod html;
/// Supports adding interactivity to HTML.
pub mod hydration; pub mod hydration;
/// Types for MathML.
pub mod mathml; pub mod mathml;
/// Defines various backends that can render views.
pub mod renderer; pub mod renderer;
/// Rendering views to HTML.
pub mod ssr; pub mod ssr;
/// Types for SVG.
pub mod svg; pub mod svg;
/// Core logic for manipulating views.
pub mod view; pub mod view;
pub use either_of as either; pub use either_of as either;
#[cfg(feature = "islands")] #[cfg(feature = "islands")]
#[doc(hidden)]
pub use wasm_bindgen; pub use wasm_bindgen;
#[cfg(feature = "islands")] #[cfg(feature = "islands")]
#[doc(hidden)]
pub use web_sys; pub use web_sys;
/// View implementations for the `oco_ref` crate (cheaply-cloned string types).
#[cfg(feature = "oco")] #[cfg(feature = "oco")]
pub mod oco; pub mod oco;
/// View implementations for the `reactive_graph` crate.
#[cfg(feature = "reactive_graph")] #[cfg(feature = "reactive_graph")]
pub mod reactive_graph; pub mod reactive_graph;
pub fn log(text: &str) {
web_sys::console::log_1(&JsValue::from_str(text));
}
pub(crate) trait UnwrapOrDebug { pub(crate) trait UnwrapOrDebug {
type Output; type Output;
@ -118,6 +131,7 @@ impl<T> UnwrapOrDebug for Result<T, JsValue> {
} }
} }
#[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! or_debug { macro_rules! or_debug {
($action:expr, $el:expr, $label:literal) => { ($action:expr, $el:expr, $label:literal) => {
@ -129,6 +143,7 @@ macro_rules! or_debug {
}; };
} }
#[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! ok_or_debug { macro_rules! ok_or_debug {
($action:expr, $el:expr, $label:literal) => { ($action:expr, $el:expr, $label:literal) => {

View File

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

View File

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

View File

@ -20,6 +20,7 @@ use std::{
mod class; mod class;
mod guards; mod guards;
mod inner_html; mod inner_html;
/// Provides a reactive [`NodeRef`](node_ref::NodeRef) type.
pub mod node_ref; pub mod node_ref;
mod owned; mod owned;
mod property; mod property;
@ -78,6 +79,8 @@ where
old.unmount(); old.unmount();
} }
} }
/// Retained view state for a [`RenderEffect`].
pub struct RenderEffectState<T: 'static>(Option<RenderEffect<T>>); pub struct RenderEffectState<T: 'static>(Option<RenderEffect<T>>);
impl<T> From<RenderEffect<T>> for RenderEffectState<T> { impl<T> From<RenderEffect<T>> for RenderEffectState<T> {
@ -359,7 +362,9 @@ where
fn to_html(self, _key: &str, _buf: &mut String) { fn to_html(self, _key: &str, _buf: &mut String) {
#[cfg(feature = "tracing")] #[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) {} fn to_template(_key: &str, _buf: &mut String) {}
@ -411,12 +416,12 @@ where
fn into_cloneable(self) -> Self::Cloneable { fn into_cloneable(self) -> Self::Cloneable {
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
tracing::warn!("Suspended attributes cannot be spread"); tracing::error!("Suspended attributes cannot be spread");
} }
fn into_cloneable_owned(self) -> Self::CloneableOwned { fn into_cloneable_owned(self) -> Self::CloneableOwned {
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
tracing::warn!("Suspended attributes cannot be spread"); tracing::error!("Suspended attributes cannot be spread");
} }
fn dry_resolve(&mut self) {} 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>>; pub type SharedReactiveFunction<T> = Arc<Mutex<dyn FnMut() -> T + Send>>;
/// A reactive view function.
pub trait ReactiveFunction: Send + 'static { pub trait ReactiveFunction: Send + 'static {
/// The return type of the function.
type Output; type Output;
/// Call the function.
fn invoke(&mut self) -> Self::Output; 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>>; 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 send_wrapper::SendWrapper;
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
/// A reactive reference to a DOM node that can be used with the `node_ref` attribute.
#[derive(Debug)] #[derive(Debug)]
pub struct NodeRef<E>(RwSignal<Option<SendWrapper<E::Output>>>) pub struct NodeRef<E>(RwSignal<Option<SendWrapper<E::Output>>>)
where where
@ -20,6 +21,7 @@ where
E: ElementType, E: ElementType,
E::Output: 'static, E::Output: 'static,
{ {
/// Creates a new node reference.
#[track_caller] #[track_caller]
pub fn new() -> Self { pub fn new() -> Self {
Self(RwSignal::new(None)) Self(RwSignal::new(None))

View File

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

View File

@ -19,25 +19,7 @@ use reactive_graph::{
}; };
use std::{cell::RefCell, fmt::Debug, future::Future, pin::Pin, rc::Rc}; use std::{cell::RefCell, fmt::Debug, future::Future, pin::Pin, rc::Rc};
pub trait FutureViewExt: Sized { /// A suspended `Future`, which can be used in the view.
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 })
};
}*/
#[derive(Clone)] #[derive(Clone)]
pub struct Suspend<Fut>(pub Fut); 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> pub struct SuspendState<T, Rndr>
where where
T: Render<Rndr>, T: Render<Rndr>,

View File

@ -14,6 +14,7 @@ use web_sys::{
Event, HtmlElement, HtmlTemplateElement, Node, Text, Event, HtmlElement, HtmlTemplateElement, Node, Text,
}; };
/// A [`Renderer`] that uses `web-sys` to manipulate DOM elements in the browser.
#[derive(Debug)] #[derive(Debug)]
pub struct Dom; pub struct Dom;
@ -37,7 +38,12 @@ impl Renderer for Dom {
} }
fn create_placeholder() -> Self::Placeholder { 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"))] #[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 std::{borrow::Cow, cell::RefCell, collections::HashMap, rc::Rc};
use wasm_bindgen::JsValue; 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)] #[derive(Debug)]
pub struct MockDom; pub struct MockDom;
new_key_type! { 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)] #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Node(NodeId); pub struct Node(NodeId);
/// A mock element.
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Element(Node); pub struct Element(Node);
/// A mock text node.
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Text(Node); pub struct Text(Node);
/// A mock comment node.
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Placeholder(Node); 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 { pub fn node_eq(a: impl AsRef<Node>, b: impl AsRef<Node>) -> bool {
a.as_ref() == b.as_ref() a.as_ref() == b.as_ref()
} }
@ -79,6 +89,7 @@ impl From<Placeholder> for Node {
} }
impl Element { impl Element {
/// Outputs an HTML form of the element, for testing and debugging purposes.
pub fn to_debug_html(&self) -> String { pub fn to_debug_html(&self) -> String {
let mut buf = String::new(); let mut buf = String::new();
self.debug_html(&mut buf); self.debug_html(&mut buf);
@ -86,9 +97,12 @@ impl Element {
} }
} }
/// The DOM data associated with a particular node.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct NodeData { pub struct NodeData {
parent: Option<NodeId>, /// The node's parent.
pub parent: Option<NodeId>,
/// The node itself.
pub ty: NodeType, pub ty: NodeType,
} }
@ -153,10 +167,12 @@ impl DebugHtml for NodeData {
} }
} }
/// The mock DOM document.
#[derive(Clone)] #[derive(Clone)]
pub struct Document(Rc<RefCell<SlotMap<NodeId, NodeData>>>); pub struct Document(Rc<RefCell<SlotMap<NodeId, NodeData>>>);
impl Document { impl Document {
/// Creates a new document.
pub fn new() -> Self { pub fn new() -> Self {
Document(Default::default()) Document(Default::default())
} }
@ -180,6 +196,7 @@ impl Document {
}) })
} }
/// Resets the document's contents.
pub fn reset(&self) { pub fn reset(&self) {
self.0.borrow_mut().clear(); self.0.borrow_mut().clear();
} }
@ -295,18 +312,26 @@ thread_local! {
static DOCUMENT: Document = Document::new(); static DOCUMENT: Document = Document::new();
} }
/// Returns the global document.
pub fn document() -> Document { pub fn document() -> Document {
DOCUMENT.with(Clone::clone) DOCUMENT.with(Clone::clone)
} }
/// The type of mock DOM node.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum NodeType { pub enum NodeType {
/// A text node.
Text(String), Text(String),
/// An element.
Element { Element {
/// The HTML tag name.
tag: Cow<'static, str>, tag: Cow<'static, str>,
/// The attributes.
attrs: HashMap<String, String>, attrs: HashMap<String, String>,
/// The element's children.
children: Vec<Node>, children: Vec<Node>,
}, },
/// A placeholder.
Placeholder, Placeholder,
} }

View File

@ -5,9 +5,12 @@ use crate::{
use std::{borrow::Cow, fmt::Debug}; use std::{borrow::Cow, fmt::Debug};
use wasm_bindgen::JsValue; use wasm_bindgen::JsValue;
/// A DOM renderer.
pub mod dom; pub mod dom;
#[cfg(feature = "testing")] #[cfg(feature = "testing")]
/// A renderer based on a mock DOM.
pub mod mock_dom; pub mod mock_dom;
/// A DOM renderer optimized for element creation.
#[cfg(feature = "sledgehammer")] #[cfg(feature = "sledgehammer")]
pub mod sledgehammer; pub mod sledgehammer;
@ -38,6 +41,7 @@ pub trait Renderer: Send + Sized + Debug + 'static {
+ Clone + Clone
+ 'static; + 'static;
/// Interns a string slice, if that is available on this platform and useful as an optimization.
fn intern(text: &str) -> &str; fn intern(text: &str) -> &str;
/// Creates a new element node. /// 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. /// Returns the next sibling of the given node, if any.
fn next_sibling(node: &Self::Node) -> Option<Self::Node>; fn next_sibling(node: &Self::Node) -> Option<Self::Node>;
/// Logs the given node in a platform-appropriate way.
fn log_node(node: &Self::Node); fn log_node(node: &Self::Node);
} }
@ -208,7 +213,7 @@ pub trait DomRenderer: Renderer {
fn get_template<V>() -> Self::TemplateElement fn get_template<V>() -> Self::TemplateElement
where where
V: ToTemplate + 'static; V: ToTemplate + 'static;
/// Deeply clones a template.
fn clone_template(tpl: &Self::TemplateElement) -> Self::Element; fn clone_template(tpl: &Self::TemplateElement) -> Self::Element;
} }
@ -222,5 +227,6 @@ pub trait CastFrom<T>
where where
Self: Sized, Self: Sized,
{ {
/// Casts a node from one type to another.
fn cast_from(source: T) -> Option<Self>; 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::{ use crate::{
dom::window, dom::window,
view::{Mountable, ToTemplate}, view::{Mountable, ToTemplate},
@ -374,7 +376,7 @@ impl DomRenderer for Sledgehammer {
el: &Self::Element, el: &Self::Element,
name: &str, name: &str,
cb: Box<dyn FnMut(Self::Event)>, 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(); let cb = wasm_bindgen::closure::Closure::wrap(cb).into_js_value();
CHANNEL.with_borrow_mut(|channel| { CHANNEL.with_borrow_mut(|channel| {
channel.add_listener(el.0 .0, name); channel.add_listener(el.0 .0, name);
@ -383,7 +385,7 @@ impl DomRenderer for Sledgehammer {
}); });
// return the remover // return the remover
Box::new(move |_el| todo!()) RemoveEventHandler(Box::new(move |_el| todo!()))
} }
fn event_target<T>(_ev: &Self::Event) -> T fn event_target<T>(_ev: &Self::Event) -> T
@ -404,7 +406,7 @@ impl DomRenderer for Sledgehammer {
name: Cow<'static, str>, name: Cow<'static, str>,
delegation_key: Cow<'static, str>, delegation_key: Cow<'static, str>,
cb: Box<dyn FnMut(Self::Event)>, cb: Box<dyn FnMut(Self::Event)>,
) -> Box<dyn FnOnce(&Self::Element) + Send> { ) -> RemoveEventHandler<Self::Element> {
let cb = Closure::wrap(cb).into_js_value(); let cb = Closure::wrap(cb).into_js_value();
CHANNEL.with_borrow_mut(|channel| { CHANNEL.with_borrow_mut(|channel| {
channel.set_property(el.0 .0, &delegation_key); channel.set_property(el.0 .0, &delegation_key);
@ -481,7 +483,7 @@ impl DomRenderer for Sledgehammer {
}); });
// return the remover // return the remover
Box::new(move |_el| todo!()) RemoveEventHandler(Box::new(move |_el| todo!()))
} }
fn class_list(el: &Self::Element) -> Self::ClassList { fn class_list(el: &Self::Element) -> Self::ClassList {

View File

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

View File

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

View File

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

View File

@ -14,6 +14,16 @@ use std::{
#[cfg(feature = "ssr")] #[cfg(feature = "ssr")]
use std::{future::Future, pin::Pin}; 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> pub struct AnyView<R>
where where
R: Renderer, R: Renderer,
@ -49,6 +59,7 @@ where
fn(Box<dyn Any>, &Cursor<R>, &PositionState) -> AnyViewState<R>, fn(Box<dyn Any>, &Cursor<R>, &PositionState) -> AnyViewState<R>,
} }
/// Retained view state for [`AnyView`].
pub struct AnyViewState<R> pub struct AnyViewState<R>
where where
R: Renderer, R: Renderer,
@ -77,10 +88,12 @@ where
} }
} }
/// Allows converting some view into [`AnyView`].
pub trait IntoAny<R> pub trait IntoAny<R>
where where
R: Renderer, R: Renderer,
{ {
/// Converts the view into a type-erased [`AnyView`].
fn into_any(self) -> AnyView<R>; 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. /// Stores each value in the view state, overwriting it only if `Some(_)` is provided.
pub struct EitherKeepAlive<A, B> { pub struct EitherKeepAlive<A, B> {
/// The first possibility.
pub a: Option<A>, pub a: Option<A>,
/// The second possibility.
pub b: Option<B>, pub b: Option<B>,
/// If `true`, then `b` will be shown.
pub show_b: bool, pub show_b: bool,
} }
/// Retained view state for [`EitherKeepAlive`].
pub struct EitherKeepAliveState<A, B> { pub struct EitherKeepAliveState<A, B> {
a: Option<A>, a: Option<A>,
b: Option<B>, b: Option<B>,
@ -392,12 +396,15 @@ where
macro_rules! tuples { macro_rules! tuples {
($num:literal => $($ty:ident),*) => { ($num:literal => $($ty:ident),*) => {
paste::paste! { paste::paste! {
#[doc = concat!("Retained view state for ", stringify!([<EitherOf $num>]), ".")]
pub struct [<EitherOf $num State>]<$($ty,)* Rndr> pub struct [<EitherOf $num State>]<$($ty,)* Rndr>
where where
$($ty: Render<Rndr>,)* $($ty: Render<Rndr>,)*
Rndr: Renderer Rndr: Renderer
{ {
/// Which child view state is being displayed.
pub state: [<EitherOf $num>]<$($ty::State,)*>, pub state: [<EitherOf $num>]<$($ty::State,)*>,
/// The renderer.
pub rndr: PhantomData<Rndr> pub rndr: PhantomData<Rndr>
} }

View File

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

View File

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

View File

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

View File

@ -3,18 +3,29 @@ use crate::{hydration::Cursor, renderer::Renderer, ssr::StreamBuilder};
use parking_lot::RwLock; use parking_lot::RwLock;
use std::{cell::RefCell, future::Future, rc::Rc, sync::Arc}; use std::{cell::RefCell, future::Future, rc::Rc, sync::Arc};
/// Add attributes to typed views.
pub mod add_attr; pub mod add_attr;
/// A typed-erased view type.
pub mod any_view; pub mod any_view;
/// Allows choosing between one of several views.
pub mod either; pub mod either;
/// View rendering for `Result<_, _>` types.
pub mod error_boundary; pub mod error_boundary;
/// A type-erased view collection.
pub mod fragment; pub mod fragment;
/// View implementations for several iterable types.
pub mod iterators; pub mod iterators;
/// Keyed list iteration.
pub mod keyed; pub mod keyed;
mod primitives; mod primitives;
/// Optimized types for static strings known at compile time.
#[cfg(feature = "nightly")] #[cfg(feature = "nightly")]
pub mod static_types; pub mod static_types;
/// View implementation for string types.
pub mod strings; pub mod strings;
/// Optimizations for creating views via HTML `<template>` nodes.
pub mod template; pub mod template;
/// View implementations for tuples.
pub mod tuples; pub mod tuples;
/// The `Render` trait allows rendering something as part of the user interface. /// 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); 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 /// The `RenderHtml` trait allows rendering something to HTML, and transforming
/// that HTML into an interactive interface. /// that HTML into an interactive interface.
/// ///
@ -65,11 +65,15 @@ where
/// The type of the view after waiting for all asynchronous data to load. /// The type of the view after waiting for all asynchronous data to load.
type AsyncOutput: RenderHtml<R>; type AsyncOutput: RenderHtml<R>;
/// The minimum length of HTML created when this view is rendered.
const MIN_LENGTH: usize; const MIN_LENGTH: usize;
/// Whether this should actually exist in the DOM, if it is the child of an element. /// Whether this should actually exist in the DOM, if it is the child of an element.
const EXISTS: bool = true; 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); fn dry_resolve(&mut self);
/// Waits for any asynchronous sections of the view to load and returns the output. /// 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. /// Allows data to be added to a static template.
pub trait ToTemplate { pub trait ToTemplate {
/// The HTML content of the static template.
const TEMPLATE: &'static str = ""; const TEMPLATE: &'static str = "";
/// The `class` attribute content known at compile time.
const CLASS: &'static str = ""; const CLASS: &'static str = "";
/// The `style` attribute content known at compile time.
const STYLE: &'static str = ""; const STYLE: &'static str = "";
/// The length of the template.
const LEN: usize = Self::TEMPLATE.as_bytes().len(); const LEN: usize = Self::TEMPLATE.as_bytes().len();
/// Renders a view type to a template. This does not take actual view data, /// 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)] #[derive(Debug, Default, Clone)]
pub struct PositionState(Arc<RwLock<Position>>); pub struct PositionState(Arc<RwLock<Position>>);
impl PositionState { impl PositionState {
/// Creates a new position tracker.
pub fn new(position: Position) -> Self { pub fn new(position: Position) -> Self {
Self(Arc::new(RwLock::new(position))) Self(Arc::new(RwLock::new(position)))
} }
/// Sets the current position.
pub fn set(&self, position: Position) { pub fn set(&self, position: Position) {
*self.0.write() = position; *self.0.write() = position;
} }
/// Gets the current position.
pub fn get(&self) -> Position { pub fn get(&self) -> Position {
*self.0.read() *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 { pub fn deep_clone(&self) -> Self {
let current = self.get(); let current = self.get();
Self(Arc::new(RwLock::new(current))) Self(Arc::new(RwLock::new(current)))
} }
} }
/// The position of this element, relative to others.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
pub enum Position { pub enum Position {
/// This is the current node.
Current, Current,
/// This is the first child of its parent.
#[default] #[default]
FirstChild, FirstChild,
/// This is the next child after another child.
NextChild, NextChild,
/// This is the next child after a text node.
NextChildAfterText, NextChildAfterText,
/// This is the only child of its parent.
OnlyChild, OnlyChild,
/// This is the last child of its parent.
LastChild, 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> pub fn static_attr<K: AttributeKey, const V: &'static str>() -> StaticAttr<K, V>
{ {
StaticAttr { ty: PhantomData } 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)] #[derive(Debug, Clone, Copy)]
pub struct Static<const V: &'static str>; pub struct Static<const V: &'static str>;

View File

@ -13,8 +13,9 @@ no_attrs!(String);
no_attrs!(Arc<str>); no_attrs!(Arc<str>);
no_attrs!(Cow<'a, str>); no_attrs!(Cow<'a, str>);
/// Retained view state for `&str`.
pub struct StrState<'a, R: Renderer> { pub struct StrState<'a, R: Renderer> {
pub node: R::Text, pub(crate) node: R::Text,
str: &'a str, str: &'a str,
} }
@ -142,6 +143,7 @@ where
} }
} }
/// Retained view state for `String`.
pub struct StringState<R: Renderer> { pub struct StringState<R: Renderer> {
node: R::Text, node: R::Text,
str: String, 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> { pub struct RcStrState<R: Renderer> {
node: R::Text, node: R::Text,
str: Rc<str>, 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> { pub struct ArcStrState<R: Renderer> {
node: R::Text, node: R::Text,
str: Arc<str>, 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> { pub struct CowStrState<'a, R: Renderer> {
node: R::Text, node: R::Text,
str: Cow<'a, str>, str: Cow<'a, str>,

View File

@ -7,6 +7,11 @@ use crate::{
}; };
use std::marker::PhantomData; 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> { pub struct ViewTemplate<V, R> {
view: V, view: V,
rndr: PhantomData<R>, rndr: PhantomData<R>,
@ -17,6 +22,7 @@ where
V: Render<R> + ToTemplate + 'static, V: Render<R> + ToTemplate + 'static,
R: DomRenderer, R: DomRenderer,
{ {
/// Creates a new view template.
pub fn new(view: V) -> Self { pub fn new(view: V) -> Self {
Self { Self {
view, view,