mirror of
https://github.com/leptos-rs/leptos.git
synced 2025-12-28 12:31:55 -05:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b97cf3353a | ||
|
|
68c849073c |
18
examples/gtk/Cargo.toml
Normal file
18
examples/gtk/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "gtk"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../leptos" }
|
||||
throw_error = { path = "../../any_error/" }
|
||||
|
||||
# these are used to build the integration
|
||||
gtk = { version = "0.9.0", package = "gtk4" }
|
||||
next_tuple = { path = "../../next_tuple/" }
|
||||
paste = "1.0"
|
||||
|
||||
# we want to support using glib for the reactive runtime event loop
|
||||
any_spawner = { path = "../../any_spawner/", features = ["glib"] }
|
||||
# yes, we want effects to run: this is a "frontend," not a backend
|
||||
reactive_graph = { path = "../../reactive_graph", features = ["effects"] }
|
||||
1
examples/gtk/Makefile.toml
Normal file
1
examples/gtk/Makefile.toml
Normal file
@@ -0,0 +1 @@
|
||||
extend = [{ path = "../cargo-make/main.toml" }]
|
||||
8
examples/gtk/index.html
Normal file
8
examples/gtk/index.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="color-scheme" content="dark">
|
||||
<link rel="css" href="style.css" data-trunk>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
645
examples/gtk/src/leptos_gtk.rs
Normal file
645
examples/gtk/src/leptos_gtk.rs
Normal file
@@ -0,0 +1,645 @@
|
||||
use self::properties::Connect;
|
||||
use gtk::{
|
||||
glib::{
|
||||
object::{IsA, IsClass, ObjectExt},
|
||||
Object, Value,
|
||||
},
|
||||
prelude::{Cast, WidgetExt},
|
||||
Label, Orientation, Widget,
|
||||
};
|
||||
use leptos::{
|
||||
reactive_graph::effect::RenderEffect,
|
||||
tachys::{
|
||||
renderer::{CastFrom, Renderer},
|
||||
view::{Mountable, Render},
|
||||
},
|
||||
};
|
||||
use next_tuple::NextTuple;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LeptosGtk;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Element(pub Widget);
|
||||
|
||||
impl Element {
|
||||
pub fn remove(&self) {
|
||||
self.0.unparent();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Text(pub Element);
|
||||
|
||||
impl<T> From<T> for Element
|
||||
where
|
||||
T: Into<Widget>,
|
||||
{
|
||||
fn from(value: T) -> Self {
|
||||
Element(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Mountable<LeptosGtk> for Element {
|
||||
fn unmount(&mut self) {
|
||||
self.remove()
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &<LeptosGtk as Renderer>::Element,
|
||||
marker: Option<&<LeptosGtk as Renderer>::Node>,
|
||||
) {
|
||||
self.0
|
||||
.insert_before(&parent.0, marker.as_ref().map(|m| &m.0));
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<LeptosGtk>) -> bool {
|
||||
if let Some(parent) = self.0.parent() {
|
||||
child.mount(&Element(parent), Some(self));
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Mountable<LeptosGtk> for Text {
|
||||
fn unmount(&mut self) {
|
||||
self.0.remove()
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &<LeptosGtk as Renderer>::Element,
|
||||
marker: Option<&<LeptosGtk as Renderer>::Node>,
|
||||
) {
|
||||
self.0
|
||||
.0
|
||||
.insert_before(&parent.0, marker.as_ref().map(|m| &m.0));
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<LeptosGtk>) -> bool {
|
||||
self.0.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
impl CastFrom<Element> for Element {
|
||||
fn cast_from(source: Element) -> Option<Self> {
|
||||
Some(source)
|
||||
}
|
||||
}
|
||||
|
||||
impl CastFrom<Element> for Text {
|
||||
fn cast_from(source: Element) -> Option<Self> {
|
||||
source
|
||||
.0
|
||||
.downcast::<Label>()
|
||||
.ok()
|
||||
.map(|n| Text(Element::from(n)))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Element> for Element {
|
||||
fn as_ref(&self) -> &Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Element> for Text {
|
||||
fn as_ref(&self) -> &Element {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Renderer for LeptosGtk {
|
||||
type Node = Element;
|
||||
type Element = Element;
|
||||
type Text = Text;
|
||||
type Placeholder = Element;
|
||||
|
||||
fn intern(text: &str) -> &str {
|
||||
text
|
||||
}
|
||||
|
||||
fn create_text_node(text: &str) -> Self::Text {
|
||||
Text(Element::from(Label::new(Some(text))))
|
||||
}
|
||||
|
||||
fn create_placeholder() -> Self::Placeholder {
|
||||
let label = Label::new(None);
|
||||
label.set_visible(false);
|
||||
Element::from(label)
|
||||
}
|
||||
|
||||
fn set_text(node: &Self::Text, text: &str) {
|
||||
let node_as_text = node.0 .0.downcast_ref::<Label>().unwrap();
|
||||
node_as_text.set_label(text);
|
||||
}
|
||||
|
||||
fn set_attribute(node: &Self::Element, name: &str, value: &str) {
|
||||
node.0.set_property(name, value);
|
||||
}
|
||||
|
||||
fn remove_attribute(node: &Self::Element, name: &str) {
|
||||
node.0.set_property(name, None::<&str>);
|
||||
}
|
||||
|
||||
fn insert_node(
|
||||
parent: &Self::Element,
|
||||
new_child: &Self::Node,
|
||||
marker: Option<&Self::Node>,
|
||||
) {
|
||||
new_child
|
||||
.0
|
||||
.insert_before(&parent.0, marker.as_ref().map(|n| &n.0));
|
||||
}
|
||||
|
||||
fn remove_node(
|
||||
_parent: &Self::Element,
|
||||
_child: &Self::Node,
|
||||
) -> Option<Self::Node> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn remove(_node: &Self::Node) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_parent(node: &Self::Node) -> Option<Self::Node> {
|
||||
node.0.parent().map(Element::from)
|
||||
}
|
||||
|
||||
fn first_child(_node: &Self::Node) -> Option<Self::Node> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn next_sibling(_node: &Self::Node) -> Option<Self::Node> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn log_node(node: &Self::Node) {
|
||||
println!("{node:?}");
|
||||
}
|
||||
|
||||
fn clear_children(_parent: &Self::Element) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root<Chil>(children: Chil) -> (Widget, impl Mountable<LeptosGtk>)
|
||||
where
|
||||
Chil: Render<LeptosGtk>,
|
||||
{
|
||||
let state = r#box()
|
||||
.orientation(Orientation::Vertical)
|
||||
.spacing(12)
|
||||
.child(children)
|
||||
.build();
|
||||
(state.as_widget().clone(), state)
|
||||
}
|
||||
|
||||
pub trait WidgetClass {
|
||||
type Widget: Into<Widget> + IsA<Object> + IsClass;
|
||||
}
|
||||
|
||||
pub struct LGtkWidget<Widg, Props, Chil> {
|
||||
widget: PhantomData<Widg>,
|
||||
properties: Props,
|
||||
children: Chil,
|
||||
}
|
||||
|
||||
impl<Widg, Props, Chil> LGtkWidget<Widg, Props, Chil>
|
||||
where
|
||||
Widg: WidgetClass,
|
||||
Chil: NextTuple,
|
||||
{
|
||||
pub fn child<T>(
|
||||
self,
|
||||
child: T,
|
||||
) -> LGtkWidget<Widg, Props, Chil::Output<T>> {
|
||||
let LGtkWidget {
|
||||
widget,
|
||||
properties,
|
||||
children,
|
||||
} = self;
|
||||
LGtkWidget {
|
||||
widget,
|
||||
properties,
|
||||
children: children.next_tuple(child),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<Widg, Props, Chil> LGtkWidget<Widg, Props, Chil>
|
||||
where
|
||||
Widg: WidgetClass,
|
||||
Props: NextTuple,
|
||||
Chil: Render<LeptosGtk>,
|
||||
{
|
||||
pub fn connect<F>(
|
||||
self,
|
||||
signal_name: &'static str,
|
||||
callback: F,
|
||||
) -> LGtkWidget<Widg, Props::Output<Connect<F>>, Chil>
|
||||
where
|
||||
F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static,
|
||||
{
|
||||
let LGtkWidget {
|
||||
widget,
|
||||
properties,
|
||||
children,
|
||||
} = self;
|
||||
LGtkWidget {
|
||||
widget,
|
||||
properties: properties.next_tuple(Connect {
|
||||
signal_name,
|
||||
callback,
|
||||
}),
|
||||
children,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LGtkWidgetState<Widg, Props, Chil>
|
||||
where
|
||||
Chil: Render<LeptosGtk>,
|
||||
Props: Property,
|
||||
Widg: WidgetClass,
|
||||
{
|
||||
ty: PhantomData<Widg>,
|
||||
widget: Element,
|
||||
properties: Props::State,
|
||||
children: Chil::State,
|
||||
}
|
||||
|
||||
impl<Widg, Props, Chil> LGtkWidgetState<Widg, Props, Chil>
|
||||
where
|
||||
Chil: Render<LeptosGtk>,
|
||||
Props: Property,
|
||||
Widg: WidgetClass,
|
||||
{
|
||||
pub fn as_widget(&self) -> &Widget {
|
||||
&self.widget.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<Widg, Props, Chil> Render<LeptosGtk> for LGtkWidget<Widg, Props, Chil>
|
||||
where
|
||||
Widg: WidgetClass,
|
||||
Props: Property,
|
||||
Chil: Render<LeptosGtk>,
|
||||
{
|
||||
type State = LGtkWidgetState<Widg, Props, Chil>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let widget = Object::new::<Widg::Widget>();
|
||||
let widget = Element::from(widget);
|
||||
let properties = self.properties.build(&widget);
|
||||
let mut children = self.children.build();
|
||||
children.mount(&widget, None);
|
||||
LGtkWidgetState {
|
||||
ty: PhantomData,
|
||||
widget,
|
||||
properties,
|
||||
children,
|
||||
}
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
self.properties
|
||||
.rebuild(&state.widget, &mut state.properties);
|
||||
self.children.rebuild(&mut state.children);
|
||||
}
|
||||
}
|
||||
|
||||
impl<Widg, Props, Chil> Mountable<LeptosGtk>
|
||||
for LGtkWidgetState<Widg, Props, Chil>
|
||||
where
|
||||
Widg: WidgetClass,
|
||||
Props: Property,
|
||||
Chil: Render<LeptosGtk>,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
self.children.unmount();
|
||||
self.widget.remove();
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &<LeptosGtk as Renderer>::Element,
|
||||
marker: Option<&<LeptosGtk as Renderer>::Node>,
|
||||
) {
|
||||
self.children.mount(&self.widget, None);
|
||||
LeptosGtk::insert_node(parent, &self.widget, marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<LeptosGtk>) -> bool {
|
||||
self.widget.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Property {
|
||||
type State;
|
||||
|
||||
fn build(self, element: &Element) -> Self::State;
|
||||
|
||||
fn rebuild(self, element: &Element, state: &mut Self::State);
|
||||
}
|
||||
|
||||
impl<T, F> Property for F
|
||||
where
|
||||
T: Property,
|
||||
T::State: 'static,
|
||||
F: Fn() -> T + 'static,
|
||||
{
|
||||
type State = RenderEffect<T::State>;
|
||||
|
||||
fn build(self, widget: &Element) -> Self::State {
|
||||
let widget = widget.clone();
|
||||
RenderEffect::new(move |prev| {
|
||||
let value = self();
|
||||
if let Some(mut prev) = prev {
|
||||
value.rebuild(&widget, &mut prev);
|
||||
prev
|
||||
} else {
|
||||
value.build(&widget)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn rebuild(self, widget: &Element, state: &mut Self::State) {
|
||||
let prev_value = state.take_value();
|
||||
let widget = widget.to_owned();
|
||||
*state = RenderEffect::new_with_value(
|
||||
move |prev| {
|
||||
let value = self();
|
||||
if let Some(mut state) = prev {
|
||||
value.rebuild(&widget, &mut state);
|
||||
state
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
},
|
||||
prev_value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn button() -> LGtkWidget<gtk::Button, (), ()> {
|
||||
LGtkWidget {
|
||||
widget: PhantomData,
|
||||
properties: (),
|
||||
children: (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn r#box() -> LGtkWidget<gtk::Box, (), ()> {
|
||||
LGtkWidget {
|
||||
widget: PhantomData,
|
||||
properties: (),
|
||||
children: (),
|
||||
}
|
||||
}
|
||||
|
||||
mod widgets {
|
||||
use super::WidgetClass;
|
||||
|
||||
impl WidgetClass for gtk::Button {
|
||||
type Widget = Self;
|
||||
}
|
||||
|
||||
impl WidgetClass for gtk::Box {
|
||||
type Widget = Self;
|
||||
}
|
||||
}
|
||||
|
||||
pub mod properties {
|
||||
#![allow(dead_code)]
|
||||
|
||||
use super::{Element, LGtkWidget, LeptosGtk, Property, WidgetClass};
|
||||
use gtk::glib::{object::ObjectExt, Value};
|
||||
use leptos::tachys::{renderer::Renderer, view::Render};
|
||||
use next_tuple::NextTuple;
|
||||
|
||||
pub struct Connect<F>
|
||||
where
|
||||
F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static,
|
||||
{
|
||||
pub signal_name: &'static str,
|
||||
pub callback: F,
|
||||
}
|
||||
|
||||
impl<F> Property for Connect<F>
|
||||
where
|
||||
F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static,
|
||||
{
|
||||
type State = ();
|
||||
|
||||
fn build(self, element: &Element) -> Self::State {
|
||||
element.0.connect(self.signal_name, false, self.callback);
|
||||
}
|
||||
|
||||
fn rebuild(self, _element: &Element, _state: &mut Self::State) {
|
||||
// TODO we want to *remove* the previous listener, and reconnect with this new one
|
||||
}
|
||||
}
|
||||
|
||||
/* examples for macro */
|
||||
pub struct Orientation {
|
||||
value: gtk::Orientation,
|
||||
}
|
||||
|
||||
pub struct OrientationState {
|
||||
value: gtk::Orientation,
|
||||
}
|
||||
|
||||
impl Property for Orientation {
|
||||
type State = OrientationState;
|
||||
|
||||
fn build(self, element: &Element) -> Self::State {
|
||||
element.0.set_property("orientation", self.value);
|
||||
OrientationState { value: self.value }
|
||||
}
|
||||
|
||||
fn rebuild(self, element: &Element, state: &mut Self::State) {
|
||||
if self.value != state.value {
|
||||
element.0.set_property("orientation", self.value);
|
||||
state.value = self.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Widg, Props, Chil> LGtkWidget<Widg, Props, Chil>
|
||||
where
|
||||
Widg: WidgetClass,
|
||||
Props: NextTuple,
|
||||
Chil: Render<LeptosGtk>,
|
||||
{
|
||||
pub fn orientation(
|
||||
self,
|
||||
value: impl Into<gtk::Orientation>,
|
||||
) -> LGtkWidget<Widg, Props::Output<Orientation>, Chil> {
|
||||
let LGtkWidget {
|
||||
widget,
|
||||
properties,
|
||||
children,
|
||||
} = self;
|
||||
LGtkWidget {
|
||||
widget,
|
||||
properties: properties.next_tuple(Orientation {
|
||||
value: value.into(),
|
||||
}),
|
||||
children,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Spacing {
|
||||
value: i32,
|
||||
}
|
||||
|
||||
pub struct SpacingState {
|
||||
value: i32,
|
||||
}
|
||||
|
||||
impl Property for Spacing {
|
||||
type State = SpacingState;
|
||||
|
||||
fn build(self, element: &Element) -> Self::State {
|
||||
element.0.set_property("spacing", self.value);
|
||||
SpacingState { value: self.value }
|
||||
}
|
||||
|
||||
fn rebuild(self, element: &Element, state: &mut Self::State) {
|
||||
if self.value != state.value {
|
||||
element.0.set_property("spacing", self.value);
|
||||
state.value = self.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Widg, Props, Chil> LGtkWidget<Widg, Props, Chil>
|
||||
where
|
||||
Widg: WidgetClass,
|
||||
Props: NextTuple,
|
||||
Chil: Render<LeptosGtk>,
|
||||
{
|
||||
pub fn spacing(
|
||||
self,
|
||||
value: impl Into<i32>,
|
||||
) -> LGtkWidget<Widg, Props::Output<Spacing>, Chil> {
|
||||
let LGtkWidget {
|
||||
widget,
|
||||
properties,
|
||||
children,
|
||||
} = self;
|
||||
LGtkWidget {
|
||||
widget,
|
||||
properties: properties.next_tuple(Spacing {
|
||||
value: value.into(),
|
||||
}),
|
||||
children,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* end examples for properties macro */
|
||||
#[derive(Debug)]
|
||||
pub struct Label {
|
||||
value: String,
|
||||
}
|
||||
|
||||
impl Label {
|
||||
pub fn new(value: impl Into<String>) -> Self {
|
||||
Self {
|
||||
value: value.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LabelState {
|
||||
value: String,
|
||||
}
|
||||
|
||||
impl Property for Label {
|
||||
type State = LabelState;
|
||||
|
||||
fn build(self, element: &Element) -> Self::State {
|
||||
LeptosGtk::set_attribute(element, "label", &self.value);
|
||||
LabelState { value: self.value }
|
||||
}
|
||||
|
||||
fn rebuild(self, element: &Element, state: &mut Self::State) {
|
||||
if self.value != state.value {
|
||||
LeptosGtk::set_attribute(element, "label", &self.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Property for () {
|
||||
type State = ();
|
||||
|
||||
fn build(self, _element: &Element) -> Self::State {}
|
||||
|
||||
fn rebuild(self, _element: &Element, _state: &mut Self::State) {}
|
||||
}
|
||||
|
||||
macro_rules! tuples {
|
||||
($($ty:ident),* $(,)?) => {
|
||||
impl<$($ty,)*> Property for ($($ty,)*)
|
||||
where $($ty: Property,)*
|
||||
{
|
||||
type State = ($($ty::State,)*);
|
||||
|
||||
fn build(self, element: &Element) -> Self::State {
|
||||
#[allow(non_snake_case)]
|
||||
let ($($ty,)*) = self;
|
||||
($($ty.build(element),)*)
|
||||
}
|
||||
|
||||
fn rebuild(self, element: &Element, state: &mut Self::State) {
|
||||
paste::paste! {
|
||||
#[allow(non_snake_case)]
|
||||
let ($($ty,)*) = self;
|
||||
#[allow(non_snake_case)]
|
||||
let ($([<state_ $ty:lower>],)*) = state;
|
||||
$($ty.rebuild(element, [<state_ $ty:lower>]));*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tuples!(A);
|
||||
tuples!(A, B);
|
||||
tuples!(A, B, C);
|
||||
tuples!(A, B, C, D);
|
||||
tuples!(A, B, C, D, E);
|
||||
tuples!(A, B, C, D, E, F);
|
||||
tuples!(A, B, C, D, E, F, G);
|
||||
tuples!(A, B, C, D, E, F, G, H);
|
||||
tuples!(A, B, C, D, E, F, G, H, I);
|
||||
tuples!(A, B, C, D, E, F, G, H, I, J);
|
||||
tuples!(A, B, C, D, E, F, G, H, I, J, K);
|
||||
tuples!(A, B, C, D, E, F, G, H, I, J, K, L);
|
||||
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M);
|
||||
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
|
||||
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
|
||||
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
|
||||
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);
|
||||
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);
|
||||
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
|
||||
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);
|
||||
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U);
|
||||
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V);
|
||||
tuples!(
|
||||
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W
|
||||
);
|
||||
tuples!(
|
||||
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X
|
||||
);
|
||||
tuples!(
|
||||
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X,
|
||||
Y
|
||||
);
|
||||
}
|
||||
107
examples/gtk/src/main.rs
Normal file
107
examples/gtk/src/main.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
use any_spawner::Executor;
|
||||
use gtk::{prelude::*, Application, ApplicationWindow, Orientation};
|
||||
use leptos::prelude::*;
|
||||
use leptos_gtk::LeptosGtk;
|
||||
use std::{mem, thread, time::Duration};
|
||||
mod leptos_gtk;
|
||||
|
||||
const APP_ID: &str = "dev.leptos.Counter";
|
||||
|
||||
// Basic GTK app setup from https://gtk-rs.org/gtk4-rs/stable/latest/book/hello_world.html
|
||||
fn main() {
|
||||
// use the glib event loop to power the reactive system
|
||||
_ = Executor::init_glib();
|
||||
let app = Application::builder().application_id(APP_ID).build();
|
||||
|
||||
app.connect_startup(|_| load_css());
|
||||
|
||||
app.connect_activate(|app| {
|
||||
// Connect to "activate" signal of `app`
|
||||
let owner = Owner::new();
|
||||
let view = owner.with(ui);
|
||||
let (root, state) = leptos_gtk::root(view);
|
||||
|
||||
let window = ApplicationWindow::builder()
|
||||
.application(app)
|
||||
.title("TachyGTK")
|
||||
.child(&root)
|
||||
.build();
|
||||
// Present window
|
||||
window.present();
|
||||
mem::forget((owner, state));
|
||||
});
|
||||
|
||||
app.run();
|
||||
}
|
||||
|
||||
fn ui() -> impl Render<LeptosGtk> {
|
||||
let value = RwSignal::new(0);
|
||||
let rows = RwSignal::new(vec![1, 2, 3, 4, 5]);
|
||||
|
||||
Effect::new(move |_| {
|
||||
println!("value = {}", value.get());
|
||||
});
|
||||
|
||||
// just an example of multithreaded reactivity
|
||||
thread::spawn(move || loop {
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
value.update(|n| *n += 1);
|
||||
});
|
||||
|
||||
vstack((
|
||||
hstack((
|
||||
button("-1", move || {
|
||||
println!("clicked -1");
|
||||
value.update(|n| *n -= 1);
|
||||
}),
|
||||
move || value.get().to_string(),
|
||||
button("+1", move || value.update(|n| *n += 1)),
|
||||
)),
|
||||
button("Swap", move || {
|
||||
rows.update(|items| {
|
||||
items.swap(1, 3);
|
||||
})
|
||||
}),
|
||||
hstack(rows),
|
||||
))
|
||||
}
|
||||
|
||||
fn button(
|
||||
label: impl Render<LeptosGtk>,
|
||||
callback: impl Fn() + Send + Sync + 'static,
|
||||
) -> impl Render<LeptosGtk> {
|
||||
leptos_gtk::button()
|
||||
.child(label)
|
||||
.connect("clicked", move |_| {
|
||||
callback();
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
fn vstack(children: impl Render<LeptosGtk>) -> impl Render<LeptosGtk> {
|
||||
leptos_gtk::r#box()
|
||||
.orientation(Orientation::Vertical)
|
||||
.spacing(12)
|
||||
.child(children)
|
||||
}
|
||||
|
||||
fn hstack(children: impl Render<LeptosGtk>) -> impl Render<LeptosGtk> {
|
||||
leptos_gtk::r#box()
|
||||
.orientation(Orientation::Horizontal)
|
||||
.spacing(12)
|
||||
.child(children)
|
||||
}
|
||||
|
||||
fn load_css() {
|
||||
use gtk::{gdk::Display, CssProvider};
|
||||
|
||||
let provider = CssProvider::new();
|
||||
provider.load_from_path("style.css");
|
||||
|
||||
// Add the provider to the default screen
|
||||
gtk::style_context_add_provider_for_display(
|
||||
&Display::default().expect("Could not connect to a display."),
|
||||
&provider,
|
||||
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
|
||||
);
|
||||
}
|
||||
0
examples/gtk/style.css
Normal file
0
examples/gtk/style.css
Normal file
@@ -65,7 +65,7 @@ pub fn RouterExample() -> impl IntoView {
|
||||
// You can define other routes in their own component.
|
||||
// Routes implement the MatchNestedRoutes
|
||||
#[component]
|
||||
pub fn ContactRoutes() -> impl MatchNestedRoutes + Clone {
|
||||
pub fn ContactRoutes() -> impl MatchNestedRoutes<Dom> + Clone {
|
||||
view! {
|
||||
<ParentRoute path=path!("") view=ContactList>
|
||||
<Route path=path!("/") view=|| "Select a contact."/>
|
||||
|
||||
@@ -3,10 +3,13 @@ use std::{
|
||||
fmt::{self, Debug},
|
||||
sync::Arc,
|
||||
};
|
||||
use tachys::view::{
|
||||
any_view::{AnyView, IntoAny},
|
||||
fragment::{Fragment, IntoFragment},
|
||||
RenderHtml,
|
||||
use tachys::{
|
||||
renderer::dom::Dom,
|
||||
view::{
|
||||
any_view::{AnyView, IntoAny},
|
||||
fragment::{Fragment, IntoFragment},
|
||||
RenderHtml,
|
||||
},
|
||||
};
|
||||
|
||||
/// The most common type for the `children` property on components,
|
||||
@@ -14,31 +17,31 @@ use tachys::view::{
|
||||
///
|
||||
/// This does not support iterating over individual nodes within the children.
|
||||
/// To iterate over children, use [`ChildrenFragment`].
|
||||
pub type Children = Box<dyn FnOnce() -> AnyView + Send>;
|
||||
pub type Children = Box<dyn FnOnce() -> AnyView<Dom> + Send>;
|
||||
|
||||
/// A type for the `children` property on components that can be called only once,
|
||||
/// and provides a collection of all the children passed to this component.
|
||||
pub type ChildrenFragment = Box<dyn FnOnce() -> Fragment + Send>;
|
||||
pub type ChildrenFragment = Box<dyn FnOnce() -> Fragment<Dom> + Send>;
|
||||
|
||||
/// A type for the `children` property on components that can be called
|
||||
/// more than once.
|
||||
pub type ChildrenFn = Arc<dyn Fn() -> AnyView + Send + Sync>;
|
||||
pub type ChildrenFn = Arc<dyn Fn() -> AnyView<Dom> + Send + Sync>;
|
||||
|
||||
/// A type for the `children` property on components that can be called more than once,
|
||||
/// and provides a collection of all the children passed to this component.
|
||||
pub type ChildrenFragmentFn = Arc<dyn Fn() -> Fragment + Send>;
|
||||
pub type ChildrenFragmentFn = Arc<dyn Fn() -> Fragment<Dom> + Send>;
|
||||
|
||||
/// A type for the `children` property on components that can be called
|
||||
/// more than once, but may mutate the children.
|
||||
pub type ChildrenFnMut = Box<dyn FnMut() -> AnyView + Send>;
|
||||
pub type ChildrenFnMut = Box<dyn FnMut() -> AnyView<Dom> + Send>;
|
||||
|
||||
/// A type for the `children` property on components that can be called more than once,
|
||||
/// but may mutate the children, and provides a collection of all the children
|
||||
/// passed to this component.
|
||||
pub type ChildrenFragmentMut = Box<dyn FnMut() -> Fragment + Send>;
|
||||
pub type ChildrenFragmentMut = Box<dyn FnMut() -> Fragment<Dom> + Send>;
|
||||
|
||||
// This is to still support components that accept `Box<dyn Fn() -> AnyView>` as a children.
|
||||
type BoxedChildrenFn = Box<dyn Fn() -> AnyView + Send>;
|
||||
type BoxedChildrenFn = Box<dyn Fn() -> AnyView<Dom> + Send>;
|
||||
|
||||
/// This trait can be used when constructing a component that takes children without needing
|
||||
/// to know exactly what children type the component expects. This is used internally by the
|
||||
@@ -94,7 +97,7 @@ pub trait ToChildren<F> {
|
||||
impl<F, C> ToChildren<F> for Children
|
||||
where
|
||||
F: FnOnce() -> C + Send + 'static,
|
||||
C: RenderHtml + Send + 'static,
|
||||
C: RenderHtml<Dom> + Send + 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn to_children(f: F) -> Self {
|
||||
@@ -105,7 +108,7 @@ where
|
||||
impl<F, C> ToChildren<F> for ChildrenFn
|
||||
where
|
||||
F: Fn() -> C + Send + Sync + 'static,
|
||||
C: RenderHtml + Send + 'static,
|
||||
C: RenderHtml<Dom> + Send + 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn to_children(f: F) -> Self {
|
||||
@@ -116,7 +119,7 @@ where
|
||||
impl<F, C> ToChildren<F> for ChildrenFnMut
|
||||
where
|
||||
F: Fn() -> C + Send + 'static,
|
||||
C: RenderHtml + Send + 'static,
|
||||
C: RenderHtml<Dom> + Send + 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn to_children(f: F) -> Self {
|
||||
@@ -127,7 +130,7 @@ where
|
||||
impl<F, C> ToChildren<F> for BoxedChildrenFn
|
||||
where
|
||||
F: Fn() -> C + Send + 'static,
|
||||
C: RenderHtml + Send + 'static,
|
||||
C: RenderHtml<Dom> + Send + 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn to_children(f: F) -> Self {
|
||||
@@ -138,7 +141,7 @@ where
|
||||
impl<F, C> ToChildren<F> for ChildrenFragment
|
||||
where
|
||||
F: FnOnce() -> C + Send + 'static,
|
||||
C: IntoFragment,
|
||||
C: IntoFragment<Dom>,
|
||||
{
|
||||
#[inline]
|
||||
fn to_children(f: F) -> Self {
|
||||
@@ -149,7 +152,7 @@ where
|
||||
impl<F, C> ToChildren<F> for ChildrenFragmentFn
|
||||
where
|
||||
F: Fn() -> C + Send + 'static,
|
||||
C: IntoFragment,
|
||||
C: IntoFragment<Dom>,
|
||||
{
|
||||
#[inline]
|
||||
fn to_children(f: F) -> Self {
|
||||
@@ -160,7 +163,7 @@ where
|
||||
impl<F, C> ToChildren<F> for ChildrenFragmentMut
|
||||
where
|
||||
F: FnMut() -> C + Send + 'static,
|
||||
C: IntoFragment,
|
||||
C: IntoFragment<Dom>,
|
||||
{
|
||||
#[inline]
|
||||
fn to_children(mut f: F) -> Self {
|
||||
@@ -171,7 +174,7 @@ where
|
||||
/// New-type wrapper for a function that returns a view with `From` and `Default` traits implemented
|
||||
/// to enable optional props in for example `<Show>` and `<Suspense>`.
|
||||
#[derive(Clone)]
|
||||
pub struct ViewFn(Arc<dyn Fn() -> AnyView + Send + Sync + 'static>);
|
||||
pub struct ViewFn(Arc<dyn Fn() -> AnyView<Dom> + Send + Sync + 'static>);
|
||||
|
||||
impl Default for ViewFn {
|
||||
fn default() -> Self {
|
||||
@@ -182,7 +185,7 @@ impl Default for ViewFn {
|
||||
impl<F, C> From<F> for ViewFn
|
||||
where
|
||||
F: Fn() -> C + Send + Sync + 'static,
|
||||
C: RenderHtml + Send + 'static,
|
||||
C: RenderHtml<Dom> + Send + 'static,
|
||||
{
|
||||
fn from(value: F) -> Self {
|
||||
Self(Arc::new(move || value().into_any()))
|
||||
@@ -191,14 +194,14 @@ where
|
||||
|
||||
impl ViewFn {
|
||||
/// Execute the wrapped function
|
||||
pub fn run(&self) -> AnyView {
|
||||
pub fn run(&self) -> AnyView<Dom> {
|
||||
(self.0)()
|
||||
}
|
||||
}
|
||||
|
||||
/// New-type wrapper for a function, which will only be called once and returns a view with `From` and
|
||||
/// `Default` traits implemented to enable optional props in for example `<Show>` and `<Suspense>`.
|
||||
pub struct ViewFnOnce(Box<dyn FnOnce() -> AnyView + Send + 'static>);
|
||||
pub struct ViewFnOnce(Box<dyn FnOnce() -> AnyView<Dom> + Send + 'static>);
|
||||
|
||||
impl Default for ViewFnOnce {
|
||||
fn default() -> Self {
|
||||
@@ -209,7 +212,7 @@ impl Default for ViewFnOnce {
|
||||
impl<F, C> From<F> for ViewFnOnce
|
||||
where
|
||||
F: FnOnce() -> C + Send + 'static,
|
||||
C: RenderHtml + Send + 'static,
|
||||
C: RenderHtml<Dom> + Send + 'static,
|
||||
{
|
||||
fn from(value: F) -> Self {
|
||||
Self(Box::new(move || value().into_any()))
|
||||
@@ -218,7 +221,7 @@ where
|
||||
|
||||
impl ViewFnOnce {
|
||||
/// Execute the wrapped function
|
||||
pub fn run(self) -> AnyView {
|
||||
pub fn run(self) -> AnyView<Dom> {
|
||||
(self.0)()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,12 @@ use reactive_graph::{
|
||||
traits::{Get, Update, With, WithUntracked},
|
||||
};
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::{fmt::Debug, sync::Arc};
|
||||
use std::{fmt::Debug, marker::PhantomData, sync::Arc};
|
||||
use tachys::{
|
||||
html::attribute::Attribute,
|
||||
hydration::Cursor,
|
||||
reactive_graph::OwnedView,
|
||||
renderer::Renderer,
|
||||
ssr::StreamBuilder,
|
||||
view::{
|
||||
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
|
||||
@@ -111,18 +112,20 @@ where
|
||||
children,
|
||||
errors,
|
||||
fallback,
|
||||
rndr: PhantomData,
|
||||
},
|
||||
owner,
|
||||
)
|
||||
}
|
||||
|
||||
struct ErrorBoundaryView<Chil, FalFn> {
|
||||
struct ErrorBoundaryView<Chil, FalFn, Rndr> {
|
||||
hook: Arc<dyn ErrorHook>,
|
||||
boundary_id: SerializedDataId,
|
||||
errors_empty: ArcMemo<bool>,
|
||||
children: Chil,
|
||||
fallback: FalFn,
|
||||
errors: ArcRwSignal<Errors>,
|
||||
rndr: PhantomData<Rndr>,
|
||||
}
|
||||
|
||||
struct ErrorBoundaryViewState<Chil, Fal> {
|
||||
@@ -131,10 +134,11 @@ struct ErrorBoundaryViewState<Chil, Fal> {
|
||||
fallback: Option<Fal>,
|
||||
}
|
||||
|
||||
impl<Chil, Fal> Mountable for ErrorBoundaryViewState<Chil, Fal>
|
||||
impl<Chil, Fal, Rndr> Mountable<Rndr> for ErrorBoundaryViewState<Chil, Fal>
|
||||
where
|
||||
Chil: Mountable,
|
||||
Fal: Mountable,
|
||||
Chil: Mountable<Rndr>,
|
||||
Fal: Mountable<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
if let Some(fallback) = &mut self.fallback {
|
||||
@@ -144,11 +148,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &tachys::renderer::types::Element,
|
||||
marker: Option<&tachys::renderer::types::Node>,
|
||||
) {
|
||||
fn mount(&mut self, parent: &Rndr::Element, marker: Option<&Rndr::Node>) {
|
||||
if let Some(fallback) = &mut self.fallback {
|
||||
fallback.mount(parent, marker);
|
||||
} else {
|
||||
@@ -156,7 +156,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<Rndr>) -> bool {
|
||||
if let Some(fallback) = &self.fallback {
|
||||
fallback.insert_before_this(child)
|
||||
} else {
|
||||
@@ -165,11 +165,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Chil, FalFn, Fal> Render for ErrorBoundaryView<Chil, FalFn>
|
||||
impl<Chil, FalFn, Fal, Rndr> Render<Rndr>
|
||||
for ErrorBoundaryView<Chil, FalFn, Rndr>
|
||||
where
|
||||
Chil: Render + 'static,
|
||||
Chil: Render<Rndr> + 'static,
|
||||
FalFn: FnMut(ArcRwSignal<Errors>) -> Fal + Send + 'static,
|
||||
Fal: Render + 'static,
|
||||
Fal: Render<Rndr> + 'static,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type State = RenderEffect<ErrorBoundaryViewState<Chil::State, Fal::State>>;
|
||||
|
||||
@@ -226,21 +228,26 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Chil, FalFn, Fal> AddAnyAttr for ErrorBoundaryView<Chil, FalFn>
|
||||
impl<Chil, FalFn, Fal, Rndr> AddAnyAttr<Rndr>
|
||||
for ErrorBoundaryView<Chil, FalFn, Rndr>
|
||||
where
|
||||
Chil: RenderHtml + 'static,
|
||||
Chil: RenderHtml<Rndr> + 'static,
|
||||
FalFn: FnMut(ArcRwSignal<Errors>) -> Fal + Send + 'static,
|
||||
Fal: RenderHtml + Send + 'static,
|
||||
Fal: RenderHtml<Rndr> + Send + 'static,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> =
|
||||
ErrorBoundaryView<Chil::Output<SomeNewAttr::CloneableOwned>, FalFn>;
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> = ErrorBoundaryView<
|
||||
Chil::Output<SomeNewAttr::CloneableOwned>,
|
||||
FalFn,
|
||||
Rndr,
|
||||
>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
let ErrorBoundaryView {
|
||||
hook,
|
||||
@@ -249,6 +256,7 @@ where
|
||||
children,
|
||||
fallback,
|
||||
errors,
|
||||
rndr,
|
||||
} = self;
|
||||
ErrorBoundaryView {
|
||||
hook,
|
||||
@@ -257,17 +265,20 @@ where
|
||||
children: children.add_any_attr(attr.into_cloneable_owned()),
|
||||
fallback,
|
||||
errors,
|
||||
rndr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Chil, FalFn, Fal> RenderHtml for ErrorBoundaryView<Chil, FalFn>
|
||||
impl<Chil, FalFn, Fal, Rndr> RenderHtml<Rndr>
|
||||
for ErrorBoundaryView<Chil, FalFn, Rndr>
|
||||
where
|
||||
Chil: RenderHtml + Send + 'static,
|
||||
Chil: RenderHtml<Rndr> + Send + 'static,
|
||||
FalFn: FnMut(ArcRwSignal<Errors>) -> Fal + Send + 'static,
|
||||
Fal: RenderHtml + Send + 'static,
|
||||
Fal: RenderHtml<Rndr> + Send + 'static,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type AsyncOutput = ErrorBoundaryView<Chil::AsyncOutput, FalFn>;
|
||||
type AsyncOutput = ErrorBoundaryView<Chil::AsyncOutput, FalFn, Rndr>;
|
||||
|
||||
const MIN_LENGTH: usize = Chil::MIN_LENGTH;
|
||||
|
||||
@@ -292,6 +303,7 @@ where
|
||||
children: children.resolve().await,
|
||||
fallback,
|
||||
errors,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,7 +377,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
mut self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let mut children = Some(self.children);
|
||||
|
||||
@@ -157,7 +157,7 @@ where
|
||||
};
|
||||
move || keyed(each(), key.clone(), children.clone())
|
||||
}
|
||||
/*
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::prelude::*;
|
||||
@@ -168,7 +168,7 @@ mod tests {
|
||||
fn creates_list() {
|
||||
Owner::new().with(|| {
|
||||
let values = RwSignal::new(vec![1, 2, 3, 4, 5]);
|
||||
let list: View<HtmlElement<_, _, _>> = view! {
|
||||
let list: View<HtmlElement<_, _, _, Dom>> = view! {
|
||||
<ol>
|
||||
<For each=move || values.get() key=|i| *i let:i>
|
||||
<li>{i}</li>
|
||||
@@ -187,7 +187,7 @@ mod tests {
|
||||
fn creates_list_enumerate() {
|
||||
Owner::new().with(|| {
|
||||
let values = RwSignal::new(vec![1, 2, 3, 4, 5]);
|
||||
let list: View<HtmlElement<_, _, _>> = view! {
|
||||
let list: View<HtmlElement<_, _, _, Dom>> = view! {
|
||||
<ol>
|
||||
<ForEnumerate each=move || values.get() key=|i| *i let(index, i)>
|
||||
<li>{move || index.get()}"-"{i}</li>
|
||||
@@ -200,7 +200,7 @@ mod tests {
|
||||
<!>-<!>4</li><li>4<!>-<!>5</li><!></ol>"
|
||||
);
|
||||
|
||||
let list: View<HtmlElement<_, _, _>> = view! {
|
||||
let list: View<HtmlElement<_, _, _, Dom>> = view! {
|
||||
<ol>
|
||||
<ForEnumerate each=move || values.get() key=|i| *i let(index, i)>
|
||||
<li>{move || index.get()}"-"{i}</li>
|
||||
@@ -216,4 +216,3 @@ mod tests {
|
||||
});
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
(function (root, pkg_path, output_name, wasm_output_name) {
|
||||
import(`${root}/${pkg_path}/${output_name}.js`)
|
||||
.then(mod => {
|
||||
mod.default(`${root}/${pkg_path}/${wasm_output_name}.wasm`).then(() => {
|
||||
mod.default(`/${pkg_path}/${wasm_output_name}.wasm`).then(() => {
|
||||
mod.hydrate();
|
||||
});
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -2,6 +2,7 @@ use std::borrow::Cow;
|
||||
use tachys::{
|
||||
html::attribute::Attribute,
|
||||
hydration::Cursor,
|
||||
renderer::{dom::Dom, Renderer},
|
||||
ssr::StreamBuilder,
|
||||
view::{
|
||||
add_attr::AddAnyAttr, Position, PositionState, Render, RenderHtml,
|
||||
@@ -49,14 +50,14 @@ impl<T> View<T> {
|
||||
|
||||
pub trait IntoView
|
||||
where
|
||||
Self: Sized + Render + RenderHtml + Send,
|
||||
Self: Sized + Render<Dom> + RenderHtml<Dom> + Send,
|
||||
{
|
||||
fn into_view(self) -> View<Self>;
|
||||
}
|
||||
|
||||
impl<T> IntoView for T
|
||||
where
|
||||
T: Sized + Render + RenderHtml + Send, //+ AddAnyAttr,
|
||||
T: Sized + Render<Dom> + RenderHtml<Dom> + Send, //+ AddAnyAttr<Dom>,
|
||||
{
|
||||
fn into_view(self) -> View<Self> {
|
||||
View {
|
||||
@@ -67,7 +68,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Render> Render for View<T> {
|
||||
impl<T: Render<Rndr>, Rndr: Renderer> Render<Rndr> for View<T> {
|
||||
type State = T::State;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
@@ -79,10 +80,10 @@ impl<T: Render> Render for View<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RenderHtml> RenderHtml for View<T> {
|
||||
impl<T: RenderHtml<Rndr>, Rndr: Renderer> RenderHtml<Rndr> for View<T> {
|
||||
type AsyncOutput = T::AsyncOutput;
|
||||
|
||||
const MIN_LENGTH: usize = <T as RenderHtml>::MIN_LENGTH;
|
||||
const MIN_LENGTH: usize = <T as RenderHtml<Rndr>>::MIN_LENGTH;
|
||||
|
||||
async fn resolve(self) -> Self::AsyncOutput {
|
||||
self.inner.resolve().await
|
||||
@@ -146,7 +147,7 @@ impl<T: RenderHtml> RenderHtml for View<T> {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
self.inner.hydrate::<FROM_SERVER>(cursor, position)
|
||||
@@ -165,15 +166,18 @@ impl<T: ToTemplate> ToTemplate for View<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AddAnyAttr> AddAnyAttr for View<T> {
|
||||
type Output<SomeNewAttr: Attribute> = View<T::Output<SomeNewAttr>>;
|
||||
impl<T: AddAnyAttr<Rndr>, Rndr> AddAnyAttr<Rndr> for View<T>
|
||||
where
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> = View<T::Output<SomeNewAttr>>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
let View {
|
||||
inner,
|
||||
|
||||
@@ -5,8 +5,10 @@ use any_spawner::Executor;
|
||||
use reactive_graph::owner::Owner;
|
||||
#[cfg(debug_assertions)]
|
||||
use std::cell::Cell;
|
||||
use std::marker::PhantomData;
|
||||
use tachys::{
|
||||
dom::body,
|
||||
renderer::{dom::Dom, Renderer},
|
||||
view::{Mountable, Render},
|
||||
};
|
||||
#[cfg(feature = "hydrate")]
|
||||
@@ -36,7 +38,10 @@ thread_local! {
|
||||
|
||||
#[cfg(feature = "hydrate")]
|
||||
/// Runs the provided closure and mounts the result to the provided element.
|
||||
pub fn hydrate_from<F, N>(parent: HtmlElement, f: F) -> UnmountHandle<N::State>
|
||||
pub fn hydrate_from<F, N>(
|
||||
parent: HtmlElement,
|
||||
f: F,
|
||||
) -> UnmountHandle<N::State, Dom>
|
||||
where
|
||||
F: FnOnce() -> N + 'static,
|
||||
N: IntoView,
|
||||
@@ -80,7 +85,11 @@ where
|
||||
|
||||
// returns a handle that owns the owner
|
||||
// when this is dropped, it will clean up the reactive system and unmount the view
|
||||
UnmountHandle { owner, mountable }
|
||||
UnmountHandle {
|
||||
owner,
|
||||
mountable,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs the provided closure and mounts the result to the `<body>`.
|
||||
@@ -94,7 +103,7 @@ where
|
||||
}
|
||||
|
||||
/// Runs the provided closure and mounts the result to the provided element.
|
||||
pub fn mount_to<F, N>(parent: HtmlElement, f: F) -> UnmountHandle<N::State>
|
||||
pub fn mount_to<F, N>(parent: HtmlElement, f: F) -> UnmountHandle<N::State, Dom>
|
||||
where
|
||||
F: FnOnce() -> N + 'static,
|
||||
N: IntoView,
|
||||
@@ -131,17 +140,22 @@ where
|
||||
|
||||
// returns a handle that owns the owner
|
||||
// when this is dropped, it will clean up the reactive system and unmount the view
|
||||
UnmountHandle { owner, mountable }
|
||||
UnmountHandle {
|
||||
owner,
|
||||
mountable,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs the provided closure and mounts the result to the provided element.
|
||||
pub fn mount_to_renderer<F, N>(
|
||||
parent: &tachys::renderer::types::Element,
|
||||
pub fn mount_to_renderer<F, N, R>(
|
||||
parent: &R::Element,
|
||||
f: F,
|
||||
) -> UnmountHandle<N::State>
|
||||
) -> UnmountHandle<N::State, R>
|
||||
where
|
||||
F: FnOnce() -> N + 'static,
|
||||
N: Render,
|
||||
N: Render<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
// use wasm-bindgen-futures to drive the reactive system
|
||||
// we ignore the return value because an Err here just means the wasm-bindgen executor is
|
||||
@@ -159,7 +173,11 @@ where
|
||||
|
||||
// returns a handle that owns the owner
|
||||
// when this is dropped, it will clean up the reactive system and unmount the view
|
||||
UnmountHandle { owner, mountable }
|
||||
UnmountHandle {
|
||||
owner,
|
||||
mountable,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Hydrates any islands that are currently present on the page.
|
||||
@@ -193,18 +211,21 @@ pub fn hydrate_islands() {
|
||||
reactive system. You should either call `.forget()` to keep the \
|
||||
view permanently mounted, or store the `UnmountHandle` somewhere \
|
||||
and drop it when you'd like to unmount the view."]
|
||||
pub struct UnmountHandle<M>
|
||||
pub struct UnmountHandle<M, R>
|
||||
where
|
||||
M: Mountable,
|
||||
M: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
#[allow(dead_code)]
|
||||
owner: Owner,
|
||||
mountable: M,
|
||||
rndr: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<M> UnmountHandle<M>
|
||||
impl<M, R> UnmountHandle<M, R>
|
||||
where
|
||||
M: Mountable,
|
||||
M: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
/// Leaks the handle, preventing the reactive system from being cleaned up and the view from
|
||||
/// being unmounted. This should always be called when [`mount_to`] is used for the root of an
|
||||
@@ -214,9 +235,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<M> Drop for UnmountHandle<M>
|
||||
impl<M, R> Drop for UnmountHandle<M, R>
|
||||
where
|
||||
M: Mountable,
|
||||
M: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
self.mountable.unmount();
|
||||
|
||||
@@ -6,7 +6,7 @@ use base64::{
|
||||
};
|
||||
use rand::{thread_rng, RngCore};
|
||||
use std::{fmt::Display, ops::Deref, sync::Arc};
|
||||
use tachys::html::attribute::AttributeValue;
|
||||
use tachys::{html::attribute::AttributeValue, renderer::Renderer};
|
||||
|
||||
/// A cryptographic nonce ("number used once") which can be
|
||||
/// used by Content Security Policy to determine whether or not a given
|
||||
@@ -65,9 +65,12 @@ impl Display for Nonce {
|
||||
}
|
||||
}
|
||||
|
||||
impl AttributeValue for Nonce {
|
||||
impl<R> AttributeValue<R> for Nonce
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = <Arc<str> as AttributeValue>::State;
|
||||
type State = <Arc<str> as AttributeValue<R>>::State;
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
@@ -76,7 +79,7 @@ impl AttributeValue for Nonce {
|
||||
}
|
||||
|
||||
fn to_html(self, key: &str, buf: &mut String) {
|
||||
<Arc<str> as AttributeValue>::to_html(self.0, key, buf)
|
||||
<Arc<str> as AttributeValue<R>>::to_html(self.0, key, buf)
|
||||
}
|
||||
|
||||
fn to_template(_key: &str, _buf: &mut String) {}
|
||||
@@ -84,21 +87,17 @@ impl AttributeValue for Nonce {
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
key: &str,
|
||||
el: &tachys::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
<Arc<str> as AttributeValue>::hydrate::<FROM_SERVER>(self.0, key, el)
|
||||
<Arc<str> as AttributeValue<R>>::hydrate::<FROM_SERVER>(self.0, key, el)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &tachys::renderer::types::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
<Arc<str> as AttributeValue>::build(self.0, el, key)
|
||||
fn build(self, el: &<R as Renderer>::Element, key: &str) -> Self::State {
|
||||
<Arc<str> as AttributeValue<R>>::build(self.0, el, key)
|
||||
}
|
||||
|
||||
fn rebuild(self, key: &str, state: &mut Self::State) {
|
||||
<Arc<str> as AttributeValue>::rebuild(self.0, key, state)
|
||||
<Arc<str> as AttributeValue<R>>::rebuild(self.0, key, state)
|
||||
}
|
||||
|
||||
fn into_cloneable(self) -> Self::Cloneable {
|
||||
|
||||
@@ -21,6 +21,7 @@ use tachys::{
|
||||
html::attribute::Attribute,
|
||||
hydration::Cursor,
|
||||
reactive_graph::{OwnedView, OwnedViewState},
|
||||
renderer::Renderer,
|
||||
ssr::StreamBuilder,
|
||||
view::{
|
||||
add_attr::AddAnyAttr,
|
||||
@@ -134,14 +135,15 @@ pub(crate) struct SuspenseBoundary<const TRANSITION: bool, Fal, Chil> {
|
||||
pub children: Chil,
|
||||
}
|
||||
|
||||
impl<const TRANSITION: bool, Fal, Chil> Render
|
||||
impl<const TRANSITION: bool, Fal, Chil, Rndr> Render<Rndr>
|
||||
for SuspenseBoundary<TRANSITION, Fal, Chil>
|
||||
where
|
||||
Fal: Render + Send + 'static,
|
||||
Chil: Render + Send + 'static,
|
||||
Fal: Render<Rndr> + Send + 'static,
|
||||
Chil: Render<Rndr> + Send + 'static,
|
||||
Rndr: Renderer + 'static,
|
||||
{
|
||||
type State = RenderEffect<
|
||||
OwnedViewState<EitherKeepAliveState<Chil::State, Fal::State>>,
|
||||
OwnedViewState<EitherKeepAliveState<Chil::State, Fal::State>, Rndr>,
|
||||
>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
@@ -185,24 +187,25 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<const TRANSITION: bool, Fal, Chil> AddAnyAttr
|
||||
impl<const TRANSITION: bool, Fal, Chil, Rndr> AddAnyAttr<Rndr>
|
||||
for SuspenseBoundary<TRANSITION, Fal, Chil>
|
||||
where
|
||||
Fal: RenderHtml + Send + 'static,
|
||||
Chil: RenderHtml + Send + 'static,
|
||||
Fal: RenderHtml<Rndr> + Send + 'static,
|
||||
Chil: RenderHtml<Rndr> + Send + 'static,
|
||||
Rndr: Renderer + 'static,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> = SuspenseBoundary<
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> = SuspenseBoundary<
|
||||
TRANSITION,
|
||||
Fal,
|
||||
Chil::Output<SomeNewAttr::CloneableOwned>,
|
||||
>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
let attr = attr.into_cloneable_owned();
|
||||
let SuspenseBoundary {
|
||||
@@ -220,11 +223,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<const TRANSITION: bool, Fal, Chil> RenderHtml
|
||||
impl<const TRANSITION: bool, Fal, Chil, Rndr> RenderHtml<Rndr>
|
||||
for SuspenseBoundary<TRANSITION, Fal, Chil>
|
||||
where
|
||||
Fal: RenderHtml + Send + 'static,
|
||||
Chil: RenderHtml + Send + 'static,
|
||||
Fal: RenderHtml<Rndr> + Send + 'static,
|
||||
Chil: RenderHtml<Rndr> + Send + 'static,
|
||||
Rndr: Renderer + 'static,
|
||||
{
|
||||
// i.e., if this is the child of another Suspense during SSR, don't wait for it: it will handle
|
||||
// itself
|
||||
@@ -401,7 +405,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let cursor = cursor.to_owned();
|
||||
@@ -451,9 +455,10 @@ impl<T> Unsuspend<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Render for Unsuspend<T>
|
||||
impl<T, Rndr> Render<Rndr> for Unsuspend<T>
|
||||
where
|
||||
T: Render,
|
||||
T: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type State = T::State;
|
||||
|
||||
@@ -466,28 +471,30 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AddAnyAttr for Unsuspend<T>
|
||||
impl<T, Rndr> AddAnyAttr<Rndr> for Unsuspend<T>
|
||||
where
|
||||
T: AddAnyAttr + 'static,
|
||||
T: AddAnyAttr<Rndr> + 'static,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> =
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> =
|
||||
Unsuspend<T::Output<SomeNewAttr::CloneableOwned>>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
let attr = attr.into_cloneable_owned();
|
||||
Unsuspend::new(move || (self.0)().add_any_attr(attr))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RenderHtml for Unsuspend<T>
|
||||
impl<T, Rndr> RenderHtml<Rndr> for Unsuspend<T>
|
||||
where
|
||||
T: RenderHtml + 'static,
|
||||
T: RenderHtml<Rndr> + 'static,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
@@ -528,7 +535,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
(self.0)().hydrate::<FROM_SERVER>(cursor, position)
|
||||
|
||||
@@ -5,7 +5,7 @@ fn simple_ssr_test() {
|
||||
use leptos::prelude::*;
|
||||
|
||||
let (value, set_value) = signal(0);
|
||||
let rendered: View<HtmlElement<_, _, _>> = view! {
|
||||
let rendered: View<HtmlElement<_, _, _, Dom>> = view! {
|
||||
<div>
|
||||
<button on:click=move |_| set_value.update(|value| *value -= 1)>"-1"</button>
|
||||
<span>"Value: " {move || value.get().to_string()} "!"</span>
|
||||
@@ -36,7 +36,7 @@ fn ssr_test_with_components() {
|
||||
}
|
||||
}
|
||||
|
||||
let rendered: View<HtmlElement<_, _, _>> = view! {
|
||||
let rendered: View<HtmlElement<_, _, _, Dom>> = view! {
|
||||
<div class="counters">
|
||||
<Counter initial_value=1/>
|
||||
<Counter initial_value=2/>
|
||||
@@ -66,7 +66,7 @@ fn ssr_test_with_snake_case_components() {
|
||||
</div>
|
||||
}
|
||||
}
|
||||
let rendered: View<HtmlElement<_, _, _>> = view! {
|
||||
let rendered: View<HtmlElement<_, _, _, Dom>> = view! {
|
||||
<div class="counters">
|
||||
<SnakeCaseCounter initial_value=1/>
|
||||
<SnakeCaseCounter initial_value=2/>
|
||||
@@ -86,7 +86,7 @@ fn test_classes() {
|
||||
use leptos::prelude::*;
|
||||
|
||||
let (value, _set_value) = signal(5);
|
||||
let rendered: View<HtmlElement<_, _, _>> = view! {
|
||||
let rendered: View<HtmlElement<_, _, _, Dom>> = view! {
|
||||
<div
|
||||
class="my big"
|
||||
class:a=move || { value.get() > 10 }
|
||||
@@ -104,7 +104,7 @@ fn ssr_with_styles() {
|
||||
|
||||
let (_, set_value) = signal(0);
|
||||
let styles = "myclass";
|
||||
let rendered: View<HtmlElement<_, _, _>> = view! { class=styles,
|
||||
let rendered: View<HtmlElement<_, _, _, Dom>> = view! { class=styles,
|
||||
<div>
|
||||
<button class="btn" on:click=move |_| set_value.update(|value| *value -= 1)>
|
||||
"-1"
|
||||
@@ -124,7 +124,7 @@ fn ssr_option() {
|
||||
use leptos::prelude::*;
|
||||
|
||||
let (_, _) = signal(0);
|
||||
let rendered: View<HtmlElement<_, _, _>> = view! { <option></option> };
|
||||
let rendered: View<HtmlElement<_, _, _, Dom>> = view! { <option></option> };
|
||||
|
||||
assert_eq!(rendered.to_html(), "<option></option>");
|
||||
}
|
||||
|
||||
@@ -188,18 +188,20 @@ mod view_implementations {
|
||||
html::attribute::Attribute,
|
||||
hydration::Cursor,
|
||||
reactive_graph::{RenderEffectState, Suspend, SuspendState},
|
||||
renderer::Renderer,
|
||||
ssr::StreamBuilder,
|
||||
view::{
|
||||
add_attr::AddAnyAttr, Position, PositionState, Render, RenderHtml,
|
||||
},
|
||||
};
|
||||
|
||||
impl<T, Ser> Render for Resource<T, Ser>
|
||||
impl<T, R, Ser> Render<R> for Resource<T, Ser>
|
||||
where
|
||||
T: Render + Send + Sync + Clone,
|
||||
T: Render<R> + Send + Sync + Clone,
|
||||
Ser: Send + 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
type State = RenderEffectState<SuspendState<T>>;
|
||||
type State = RenderEffectState<SuspendState<T, R>>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
(move || Suspend::new(async move { self.await })).build()
|
||||
@@ -210,18 +212,19 @@ mod view_implementations {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Ser> AddAnyAttr for Resource<T, Ser>
|
||||
impl<T, R, Ser> AddAnyAttr<R> for Resource<T, Ser>
|
||||
where
|
||||
T: RenderHtml + Send + Sync + Clone,
|
||||
T: RenderHtml<R> + Send + Sync + Clone,
|
||||
Ser: Send + 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> = Box<
|
||||
type Output<SomeNewAttr: Attribute<R>> = Box<
|
||||
dyn FnMut() -> Suspend<
|
||||
Pin<
|
||||
Box<
|
||||
dyn Future<
|
||||
Output = <T as AddAnyAttr>::Output<
|
||||
<SomeNewAttr::CloneableOwned as Attribute>::CloneableOwned,
|
||||
Output = <T as AddAnyAttr<R>>::Output<
|
||||
<SomeNewAttr::CloneableOwned as Attribute<R>>::CloneableOwned,
|
||||
>,
|
||||
> + Send,
|
||||
>,
|
||||
@@ -229,21 +232,22 @@ mod view_implementations {
|
||||
> + Send,
|
||||
>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<R>,
|
||||
{
|
||||
(move || Suspend::new(async move { self.await })).add_any_attr(attr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Ser> RenderHtml for Resource<T, Ser>
|
||||
impl<T, R, Ser> RenderHtml<R> for Resource<T, Ser>
|
||||
where
|
||||
T: RenderHtml + Send + Sync + Clone,
|
||||
T: RenderHtml<R> + Send + Sync + Clone,
|
||||
Ser: Send + 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Option<T>;
|
||||
|
||||
@@ -292,7 +296,7 @@ mod view_implementations {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
(move || Suspend::new(async move { self.await }))
|
||||
|
||||
@@ -7,6 +7,7 @@ use leptos::{
|
||||
dom::document,
|
||||
html::attribute::Attribute,
|
||||
hydration::Cursor,
|
||||
renderer::{dom::Dom, Renderer},
|
||||
view::{
|
||||
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
|
||||
RenderHtml,
|
||||
@@ -55,14 +56,14 @@ struct BodyView<At> {
|
||||
|
||||
struct BodyViewState<At>
|
||||
where
|
||||
At: Attribute,
|
||||
At: Attribute<Dom>,
|
||||
{
|
||||
attributes: At::State,
|
||||
}
|
||||
|
||||
impl<At> Render for BodyView<At>
|
||||
impl<At> Render<Dom> for BodyView<At>
|
||||
where
|
||||
At: Attribute,
|
||||
At: Attribute<Dom>,
|
||||
{
|
||||
type State = BodyViewState<At>;
|
||||
|
||||
@@ -78,19 +79,19 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<At> AddAnyAttr for BodyView<At>
|
||||
impl<At> AddAnyAttr<Dom> for BodyView<At>
|
||||
where
|
||||
At: Attribute,
|
||||
At: Attribute<Dom>,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> =
|
||||
BodyView<<At as NextAttribute>::Output<SomeNewAttr>>;
|
||||
type Output<SomeNewAttr: Attribute<Dom>> =
|
||||
BodyView<<At as NextAttribute<Dom>>::Output<SomeNewAttr>>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Dom>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Dom>,
|
||||
{
|
||||
BodyView {
|
||||
attributes: self.attributes.add_any_attr(attr),
|
||||
@@ -98,9 +99,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<At> RenderHtml for BodyView<At>
|
||||
impl<At> RenderHtml<Dom> for BodyView<At>
|
||||
where
|
||||
At: Attribute,
|
||||
At: Attribute<Dom>,
|
||||
{
|
||||
type AsyncOutput = BodyView<At::AsyncOutput>;
|
||||
|
||||
@@ -134,7 +135,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
_cursor: &Cursor,
|
||||
_cursor: &Cursor<Dom>,
|
||||
_position: &PositionState,
|
||||
) -> Self::State {
|
||||
let el = document().body().expect("there to be a <body> element");
|
||||
@@ -144,20 +145,20 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<At> Mountable for BodyViewState<At>
|
||||
impl<At> Mountable<Dom> for BodyViewState<At>
|
||||
where
|
||||
At: Attribute,
|
||||
At: Attribute<Dom>,
|
||||
{
|
||||
fn unmount(&mut self) {}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
_parent: &leptos::tachys::renderer::types::Element,
|
||||
_marker: Option<&leptos::tachys::renderer::types::Node>,
|
||||
_parent: &<Dom as Renderer>::Element,
|
||||
_marker: Option<&<Dom as Renderer>::Node>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, _child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, _child: &mut dyn Mountable<Dom>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use leptos::{
|
||||
dom::document,
|
||||
html::attribute::Attribute,
|
||||
hydration::Cursor,
|
||||
renderer::{dom::Dom, Renderer},
|
||||
view::{
|
||||
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
|
||||
RenderHtml,
|
||||
@@ -52,14 +53,14 @@ struct HtmlView<At> {
|
||||
|
||||
struct HtmlViewState<At>
|
||||
where
|
||||
At: Attribute,
|
||||
At: Attribute<Dom>,
|
||||
{
|
||||
attributes: At::State,
|
||||
}
|
||||
|
||||
impl<At> Render for HtmlView<At>
|
||||
impl<At> Render<Dom> for HtmlView<At>
|
||||
where
|
||||
At: Attribute,
|
||||
At: Attribute<Dom>,
|
||||
{
|
||||
type State = HtmlViewState<At>;
|
||||
|
||||
@@ -78,19 +79,19 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<At> AddAnyAttr for HtmlView<At>
|
||||
impl<At> AddAnyAttr<Dom> for HtmlView<At>
|
||||
where
|
||||
At: Attribute,
|
||||
At: Attribute<Dom>,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> =
|
||||
HtmlView<<At as NextAttribute>::Output<SomeNewAttr>>;
|
||||
type Output<SomeNewAttr: Attribute<Dom>> =
|
||||
HtmlView<<At as NextAttribute<Dom>>::Output<SomeNewAttr>>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Dom>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Dom>,
|
||||
{
|
||||
HtmlView {
|
||||
attributes: self.attributes.add_any_attr(attr),
|
||||
@@ -98,9 +99,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<At> RenderHtml for HtmlView<At>
|
||||
impl<At> RenderHtml<Dom> for HtmlView<At>
|
||||
where
|
||||
At: Attribute,
|
||||
At: Attribute<Dom>,
|
||||
{
|
||||
type AsyncOutput = HtmlView<At::AsyncOutput>;
|
||||
|
||||
@@ -134,7 +135,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
_cursor: &Cursor,
|
||||
_cursor: &Cursor<Dom>,
|
||||
_position: &PositionState,
|
||||
) -> Self::State {
|
||||
let el = document()
|
||||
@@ -147,22 +148,22 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<At> Mountable for HtmlViewState<At>
|
||||
impl<At> Mountable<Dom> for HtmlViewState<At>
|
||||
where
|
||||
At: Attribute,
|
||||
At: Attribute<Dom>,
|
||||
{
|
||||
fn unmount(&mut self) {}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
_parent: &leptos::tachys::renderer::types::Element,
|
||||
_marker: Option<&leptos::tachys::renderer::types::Node>,
|
||||
_parent: &<Dom as Renderer>::Element,
|
||||
_marker: Option<&<Dom as Renderer>::Node>,
|
||||
) {
|
||||
// <Html> only sets attributes
|
||||
// the <html> tag doesn't need to be mounted anywhere, of course
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, _child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, _child: &mut dyn Mountable<Dom>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,9 +57,10 @@ use leptos::{
|
||||
dom::document,
|
||||
html::{
|
||||
attribute::Attribute,
|
||||
element::{ElementType, HtmlElement},
|
||||
element::{CreateElement, ElementType, HtmlElement},
|
||||
},
|
||||
hydration::Cursor,
|
||||
renderer::{dom::Dom, Renderer},
|
||||
view::{
|
||||
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
|
||||
RenderHtml,
|
||||
@@ -105,7 +106,7 @@ pub struct MetaContext {
|
||||
/// Metadata associated with the `<title>` element.
|
||||
pub(crate) title: TitleContext,
|
||||
/// The hydration cursor for the location in the `<head>` for arbitrary tags will be rendered.
|
||||
pub(crate) cursor: Arc<Lazy<SendWrapper<Cursor>>>,
|
||||
pub(crate) cursor: Arc<Lazy<SendWrapper<Cursor<Dom>>>>,
|
||||
}
|
||||
|
||||
impl MetaContext {
|
||||
@@ -122,7 +123,7 @@ const COMMENT_NODE: u16 = 8;
|
||||
|
||||
impl Default for MetaContext {
|
||||
fn default() -> Self {
|
||||
let build_cursor: fn() -> SendWrapper<Cursor> = || {
|
||||
let build_cursor: fn() -> SendWrapper<Cursor<Dom>> = || {
|
||||
let head = document().head().expect("missing <head> element");
|
||||
let mut cursor = None;
|
||||
let mut child = head.first_child();
|
||||
@@ -322,10 +323,10 @@ pub fn use_head() -> MetaContext {
|
||||
}
|
||||
|
||||
pub(crate) fn register<E, At, Ch>(
|
||||
el: HtmlElement<E, At, Ch>,
|
||||
el: HtmlElement<E, At, Ch, Dom>,
|
||||
) -> RegisteredMetaTag<E, At, Ch>
|
||||
where
|
||||
HtmlElement<E, At, Ch>: RenderHtml,
|
||||
HtmlElement<E, At, Ch, Dom>: RenderHtml<Dom>,
|
||||
{
|
||||
#[allow(unused_mut)] // used for `ssr`
|
||||
let mut el = Some(el);
|
||||
@@ -357,19 +358,19 @@ where
|
||||
struct RegisteredMetaTag<E, At, Ch> {
|
||||
// this is `None` if we've already taken it out to render to HTML on the server
|
||||
// we don't render it in place in RenderHtml, so it's fine
|
||||
el: Option<HtmlElement<E, At, Ch>>,
|
||||
el: Option<HtmlElement<E, At, Ch, Dom>>,
|
||||
}
|
||||
|
||||
struct RegisteredMetaTagState<E, At, Ch>
|
||||
where
|
||||
HtmlElement<E, At, Ch>: Render,
|
||||
HtmlElement<E, At, Ch, Dom>: Render<Dom>,
|
||||
{
|
||||
state: <HtmlElement<E, At, Ch> as Render>::State,
|
||||
state: <HtmlElement<E, At, Ch, Dom> as Render<Dom>>::State,
|
||||
}
|
||||
|
||||
impl<E, At, Ch> Drop for RegisteredMetaTagState<E, At, Ch>
|
||||
where
|
||||
HtmlElement<E, At, Ch>: Render,
|
||||
HtmlElement<E, At, Ch, Dom>: Render<Dom>,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
self.state.unmount();
|
||||
@@ -386,11 +387,11 @@ fn document_head() -> HtmlHeadElement {
|
||||
})
|
||||
}
|
||||
|
||||
impl<E, At, Ch> Render for RegisteredMetaTag<E, At, Ch>
|
||||
impl<E, At, Ch> Render<Dom> for RegisteredMetaTag<E, At, Ch>
|
||||
where
|
||||
E: ElementType,
|
||||
At: Attribute,
|
||||
Ch: Render,
|
||||
E: ElementType + CreateElement<Dom>,
|
||||
At: Attribute<Dom>,
|
||||
Ch: Render<Dom>,
|
||||
{
|
||||
type State = RegisteredMetaTagState<E, At, Ch>;
|
||||
|
||||
@@ -404,21 +405,24 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, At, Ch> AddAnyAttr for RegisteredMetaTag<E, At, Ch>
|
||||
impl<E, At, Ch> AddAnyAttr<Dom> for RegisteredMetaTag<E, At, Ch>
|
||||
where
|
||||
E: ElementType + Send,
|
||||
At: Attribute + Send,
|
||||
Ch: RenderHtml + Send,
|
||||
E: ElementType + CreateElement<Dom> + Send,
|
||||
At: Attribute<Dom> + Send,
|
||||
Ch: RenderHtml<Dom> + Send,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> =
|
||||
RegisteredMetaTag<E, <At as NextAttribute>::Output<SomeNewAttr>, Ch>;
|
||||
type Output<SomeNewAttr: Attribute<Dom>> = RegisteredMetaTag<
|
||||
E,
|
||||
<At as NextAttribute<Dom>>::Output<SomeNewAttr>,
|
||||
Ch,
|
||||
>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Dom>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Dom>,
|
||||
{
|
||||
RegisteredMetaTag {
|
||||
el: self.el.map(|inner| inner.add_any_attr(attr)),
|
||||
@@ -426,11 +430,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, At, Ch> RenderHtml for RegisteredMetaTag<E, At, Ch>
|
||||
impl<E, At, Ch> RenderHtml<Dom> for RegisteredMetaTag<E, At, Ch>
|
||||
where
|
||||
E: ElementType,
|
||||
At: Attribute,
|
||||
Ch: RenderHtml + Send,
|
||||
E: ElementType + CreateElement<Dom>,
|
||||
At: Attribute<Dom>,
|
||||
Ch: RenderHtml<Dom> + Send,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
@@ -457,7 +461,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
_cursor: &Cursor,
|
||||
_cursor: &Cursor<Dom>,
|
||||
_position: &PositionState,
|
||||
) -> Self::State {
|
||||
let cursor = use_context::<MetaContext>()
|
||||
@@ -467,18 +471,18 @@ where
|
||||
)
|
||||
.cursor;
|
||||
let state = self.el.unwrap().hydrate::<FROM_SERVER>(
|
||||
&cursor,
|
||||
&*cursor,
|
||||
&PositionState::new(Position::NextChild),
|
||||
);
|
||||
RegisteredMetaTagState { state }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, At, Ch> Mountable for RegisteredMetaTagState<E, At, Ch>
|
||||
impl<E, At, Ch> Mountable<Dom> for RegisteredMetaTagState<E, At, Ch>
|
||||
where
|
||||
E: ElementType,
|
||||
At: Attribute,
|
||||
Ch: Render,
|
||||
E: ElementType + CreateElement<Dom>,
|
||||
At: Attribute<Dom>,
|
||||
Ch: Render<Dom>,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
self.state.unmount();
|
||||
@@ -486,8 +490,8 @@ where
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
_parent: &leptos::tachys::renderer::types::Element,
|
||||
_marker: Option<&leptos::tachys::renderer::types::Node>,
|
||||
_parent: &<Dom as Renderer>::Element,
|
||||
_marker: Option<&<Dom as Renderer>::Node>,
|
||||
) {
|
||||
// we always mount this to the <head>, which is the whole point
|
||||
// but this shouldn't warn about the parent being a regular element or being unused
|
||||
@@ -496,7 +500,7 @@ where
|
||||
self.state.mount(&document_head(), None);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, _child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, _child: &mut dyn Mountable<Dom>) -> bool {
|
||||
// Registered meta tags will be mounted in the <head>, but *seem* to be mounted somewhere
|
||||
// else in the DOM. We should never tell the renderer that we have successfully mounted
|
||||
// something before this, because if e.g., a <Meta/> is the first item in an Either, then
|
||||
@@ -521,7 +525,7 @@ struct MetaTagsView;
|
||||
// rendering HTML for all the tags that will be injected into the `<head>`
|
||||
//
|
||||
// client-side rendering is handled by the individual components
|
||||
impl Render for MetaTagsView {
|
||||
impl Render<Dom> for MetaTagsView {
|
||||
type State = ();
|
||||
|
||||
fn build(self) -> Self::State {}
|
||||
@@ -529,21 +533,21 @@ impl Render for MetaTagsView {
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
}
|
||||
|
||||
impl AddAnyAttr for MetaTagsView {
|
||||
type Output<SomeNewAttr: Attribute> = MetaTagsView;
|
||||
impl AddAnyAttr<Dom> for MetaTagsView {
|
||||
type Output<SomeNewAttr: Attribute<Dom>> = MetaTagsView;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Dom>>(
|
||||
self,
|
||||
_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Dom>,
|
||||
{
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderHtml for MetaTagsView {
|
||||
impl RenderHtml<Dom> for MetaTagsView {
|
||||
type AsyncOutput = Self;
|
||||
|
||||
const MIN_LENGTH: usize = 0;
|
||||
@@ -566,7 +570,7 @@ impl RenderHtml for MetaTagsView {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
_cursor: &Cursor,
|
||||
_cursor: &Cursor<Dom>,
|
||||
_position: &PositionState,
|
||||
) -> Self::State {
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use leptos::{
|
||||
tachys::{
|
||||
dom::document,
|
||||
hydration::Cursor,
|
||||
renderer::{dom::Dom, Renderer},
|
||||
view::{
|
||||
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
|
||||
RenderHtml,
|
||||
@@ -186,7 +187,7 @@ struct TitleViewState {
|
||||
effect: RenderEffect<Oco<'static, str>>,
|
||||
}
|
||||
|
||||
impl Render for TitleView {
|
||||
impl Render<Dom> for TitleView {
|
||||
type State = TitleViewState;
|
||||
|
||||
fn build(mut self) -> Self::State {
|
||||
@@ -218,21 +219,21 @@ impl Render for TitleView {
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAnyAttr for TitleView {
|
||||
type Output<SomeNewAttr: Attribute> = TitleView;
|
||||
impl AddAnyAttr<Dom> for TitleView {
|
||||
type Output<SomeNewAttr: Attribute<Dom>> = TitleView;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Dom>>(
|
||||
self,
|
||||
_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Dom>,
|
||||
{
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderHtml for TitleView {
|
||||
impl RenderHtml<Dom> for TitleView {
|
||||
type AsyncOutput = Self;
|
||||
|
||||
const MIN_LENGTH: usize = 0;
|
||||
@@ -256,7 +257,7 @@ impl RenderHtml for TitleView {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
mut self,
|
||||
_cursor: &Cursor,
|
||||
_cursor: &Cursor<Dom>,
|
||||
_position: &PositionState,
|
||||
) -> Self::State {
|
||||
let el = self.el();
|
||||
@@ -284,19 +285,19 @@ impl RenderHtml for TitleView {
|
||||
}
|
||||
}
|
||||
|
||||
impl Mountable for TitleViewState {
|
||||
impl Mountable<Dom> for TitleViewState {
|
||||
fn unmount(&mut self) {}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
_parent: &leptos::tachys::renderer::types::Element,
|
||||
_marker: Option<&leptos::tachys::renderer::types::Node>,
|
||||
_parent: &<Dom as Renderer>::Element,
|
||||
_marker: Option<&<Dom as Renderer>::Node>,
|
||||
) {
|
||||
// <title> doesn't need to be mounted
|
||||
// TitleView::el() guarantees that there is a <title> in the <head>
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, _child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, _child: &mut dyn Mountable<Dom>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,10 +24,11 @@ use reactive_graph::{
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fmt::{Debug, Display},
|
||||
marker::PhantomData,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
use tachys::view::any_view::AnyView;
|
||||
use tachys::{renderer::dom::Dom, view::any_view::AnyView};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RouteChildren<Children>(Children);
|
||||
@@ -210,7 +211,7 @@ pub fn Routes<Defs, FallbackFn, Fallback>(
|
||||
children: RouteChildren<Defs>,
|
||||
) -> impl IntoView
|
||||
where
|
||||
Defs: MatchNestedRoutes + Clone + Send + 'static,
|
||||
Defs: MatchNestedRoutes<Dom> + Clone + Send + 'static,
|
||||
FallbackFn: FnOnce() -> Fallback + Clone + Send + 'static,
|
||||
Fallback: IntoView + 'static,
|
||||
{
|
||||
@@ -242,6 +243,7 @@ where
|
||||
current_url: current_url.clone(),
|
||||
base: base.clone(),
|
||||
fallback: fallback.clone(),
|
||||
rndr: PhantomData,
|
||||
set_is_routing,
|
||||
}
|
||||
}
|
||||
@@ -253,7 +255,7 @@ pub fn FlatRoutes<Defs, FallbackFn, Fallback>(
|
||||
children: RouteChildren<Defs>,
|
||||
) -> impl IntoView
|
||||
where
|
||||
Defs: MatchNestedRoutes + Clone + Send + 'static,
|
||||
Defs: MatchNestedRoutes<Dom> + Clone + Send + 'static,
|
||||
FallbackFn: FnOnce() -> Fallback + Clone + Send + 'static,
|
||||
Fallback: IntoView + 'static,
|
||||
{
|
||||
@@ -299,9 +301,9 @@ pub fn Route<Segments, View>(
|
||||
path: Segments,
|
||||
view: View,
|
||||
#[prop(optional)] ssr: SsrMode,
|
||||
) -> NestedRoute<Segments, (), (), View>
|
||||
) -> NestedRoute<Segments, (), (), View, Dom>
|
||||
where
|
||||
View: ChooseView,
|
||||
View: ChooseView<Dom>,
|
||||
{
|
||||
NestedRoute::new(path, view).ssr_mode(ssr)
|
||||
}
|
||||
@@ -312,9 +314,9 @@ pub fn ParentRoute<Segments, View, Children>(
|
||||
view: View,
|
||||
children: RouteChildren<Children>,
|
||||
#[prop(optional)] ssr: SsrMode,
|
||||
) -> NestedRoute<Segments, Children, (), View>
|
||||
) -> NestedRoute<Segments, Children, (), View, Dom>
|
||||
where
|
||||
View: ChooseView,
|
||||
View: ChooseView<Dom>,
|
||||
{
|
||||
let children = children.into_inner();
|
||||
NestedRoute::new(path, view).ssr_mode(ssr).child(children)
|
||||
@@ -327,7 +329,7 @@ pub fn ProtectedRoute<Segments, ViewFn, View, C, PathFn, P>(
|
||||
condition: C,
|
||||
redirect_path: PathFn,
|
||||
#[prop(optional)] ssr: SsrMode,
|
||||
) -> NestedRoute<Segments, (), (), impl Fn() -> AnyView + Send + Clone>
|
||||
) -> NestedRoute<Segments, (), (), impl Fn() -> AnyView<Dom> + Send + Clone, Dom>
|
||||
where
|
||||
ViewFn: Fn() -> View + Send + Clone + 'static,
|
||||
View: IntoView + 'static,
|
||||
@@ -370,7 +372,13 @@ pub fn ProtectedParentRoute<Segments, ViewFn, View, C, PathFn, P, Children>(
|
||||
redirect_path: PathFn,
|
||||
children: RouteChildren<Children>,
|
||||
#[prop(optional)] ssr: SsrMode,
|
||||
) -> NestedRoute<Segments, Children, (), impl Fn() -> AnyView + Send + Clone>
|
||||
) -> NestedRoute<
|
||||
Segments,
|
||||
Children,
|
||||
(),
|
||||
impl Fn() -> AnyView<Dom> + Send + Clone,
|
||||
Dom,
|
||||
>
|
||||
where
|
||||
ViewFn: Fn() -> View + Send + Clone + 'static,
|
||||
View: IntoView + 'static,
|
||||
|
||||
@@ -21,6 +21,7 @@ use std::{cell::RefCell, iter, mem, rc::Rc};
|
||||
use tachys::{
|
||||
hydration::Cursor,
|
||||
reactive_graph::OwnedView,
|
||||
renderer::Renderer,
|
||||
ssr::StreamBuilder,
|
||||
view::{
|
||||
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
|
||||
@@ -28,22 +29,23 @@ use tachys::{
|
||||
},
|
||||
};
|
||||
|
||||
pub(crate) struct FlatRoutesView<Loc, Defs, FalFn> {
|
||||
pub(crate) struct FlatRoutesView<Loc, Defs, FalFn, R> {
|
||||
pub current_url: ArcRwSignal<Url>,
|
||||
pub location: Option<Loc>,
|
||||
pub routes: Routes<Defs>,
|
||||
pub routes: Routes<Defs, R>,
|
||||
pub fallback: FalFn,
|
||||
pub outer_owner: Owner,
|
||||
pub set_is_routing: Option<SignalSetter<bool>>,
|
||||
}
|
||||
|
||||
pub struct FlatRoutesViewState<Defs, Fal>
|
||||
pub struct FlatRoutesViewState<Defs, Fal, R>
|
||||
where
|
||||
Defs: MatchNestedRoutes + 'static,
|
||||
Fal: Render + 'static,
|
||||
Defs: MatchNestedRoutes<R> + 'static,
|
||||
Fal: Render<R> + 'static,
|
||||
R: Renderer + 'static
|
||||
{
|
||||
#[allow(clippy::type_complexity)]
|
||||
view: <EitherOf3<(), Fal, OwnedView<<Defs::Match as MatchInterface>::View>> as Render>::State,
|
||||
view: <EitherOf3<(), Fal, <Defs::Match as MatchInterface<R>>::View> as Render<R>>::State,
|
||||
id: Option<RouteMatchId>,
|
||||
owner: Owner,
|
||||
params: ArcRwSignal<ParamsMap>,
|
||||
@@ -52,10 +54,11 @@ where
|
||||
matched: ArcRwSignal<String>
|
||||
}
|
||||
|
||||
impl<Defs, Fal> Mountable for FlatRoutesViewState<Defs, Fal>
|
||||
impl<Defs, Fal, R> Mountable<R> for FlatRoutesViewState<Defs, Fal, R>
|
||||
where
|
||||
Defs: MatchNestedRoutes + 'static,
|
||||
Fal: Render + 'static,
|
||||
Defs: MatchNestedRoutes<R> + 'static,
|
||||
Fal: Render<R> + 'static,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
self.view.unmount();
|
||||
@@ -63,25 +66,26 @@ where
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &leptos::tachys::renderer::types::Element,
|
||||
marker: Option<&leptos::tachys::renderer::types::Node>,
|
||||
parent: &<R as Renderer>::Element,
|
||||
marker: Option<&<R as Renderer>::Node>,
|
||||
) {
|
||||
self.view.mount(parent, marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
self.view.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Loc, Defs, FalFn, Fal> Render for FlatRoutesView<Loc, Defs, FalFn>
|
||||
impl<Loc, Defs, FalFn, Fal, R> Render<R> for FlatRoutesView<Loc, Defs, FalFn, R>
|
||||
where
|
||||
Loc: LocationProvider,
|
||||
Defs: MatchNestedRoutes + 'static,
|
||||
Defs: MatchNestedRoutes<R> + 'static,
|
||||
FalFn: FnOnce() -> Fal + Send,
|
||||
Fal: Render + 'static,
|
||||
Fal: Render<R> + 'static,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type State = Rc<RefCell<FlatRoutesViewState<Defs, Fal>>>;
|
||||
type State = Rc<RefCell<FlatRoutesViewState<Defs, Fal, R>>>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let FlatRoutesView {
|
||||
@@ -147,7 +151,7 @@ where
|
||||
provide_context(params_memo);
|
||||
provide_context(url);
|
||||
provide_context(Matched(ArcMemo::from(matched)));
|
||||
OwnedView::new(view.choose().await)
|
||||
view.choose().await
|
||||
}
|
||||
})
|
||||
}));
|
||||
@@ -292,7 +296,7 @@ where
|
||||
provide_context(Matched(ArcMemo::from(
|
||||
new_matched,
|
||||
)));
|
||||
let view = OwnedView::new(
|
||||
let view =
|
||||
if let Some(set_is_routing) = set_is_routing {
|
||||
set_is_routing.set(true);
|
||||
let value =
|
||||
@@ -302,8 +306,7 @@ where
|
||||
value
|
||||
} else {
|
||||
view.choose().await
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
// only update the route if it's still the current path
|
||||
// i.e., if we've navigated away before this has loaded, do nothing
|
||||
@@ -329,37 +332,41 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Loc, Defs, FalFn, Fal> AddAnyAttr for FlatRoutesView<Loc, Defs, FalFn>
|
||||
impl<Loc, Defs, FalFn, Fal, R> AddAnyAttr<R>
|
||||
for FlatRoutesView<Loc, Defs, FalFn, R>
|
||||
where
|
||||
Loc: LocationProvider + Send,
|
||||
Defs: MatchNestedRoutes + Send + 'static,
|
||||
Defs: MatchNestedRoutes<R> + Send + 'static,
|
||||
FalFn: FnOnce() -> Fal + Send,
|
||||
Fal: RenderHtml + 'static,
|
||||
Fal: RenderHtml<R> + 'static,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type Output<SomeNewAttr: leptos::attr::Attribute> =
|
||||
FlatRoutesView<Loc, Defs, FalFn>;
|
||||
type Output<SomeNewAttr: leptos::attr::Attribute<R>> =
|
||||
FlatRoutesView<Loc, Defs, FalFn, R>;
|
||||
|
||||
fn add_any_attr<NewAttr: leptos::attr::Attribute>(
|
||||
fn add_any_attr<NewAttr: leptos::attr::Attribute<R>>(
|
||||
self,
|
||||
_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<R>,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Loc, Defs, FalFn, Fal> FlatRoutesView<Loc, Defs, FalFn>
|
||||
impl<Loc, Defs, FalFn, Fal, R> FlatRoutesView<Loc, Defs, FalFn, R>
|
||||
where
|
||||
Loc: LocationProvider + Send,
|
||||
Defs: MatchNestedRoutes + Send + 'static,
|
||||
Defs: MatchNestedRoutes<R> + Send + 'static,
|
||||
FalFn: FnOnce() -> Fal + Send,
|
||||
Fal: RenderHtml + 'static,
|
||||
Fal: RenderHtml<R> + 'static,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
fn choose_ssr(
|
||||
self,
|
||||
) -> OwnedView<Either<Fal, <Defs::Match as MatchInterface>::View>> {
|
||||
) -> OwnedView<Either<Fal, <Defs::Match as MatchInterface<R>>::View>, R>
|
||||
{
|
||||
let current_url = self.current_url.read_untracked();
|
||||
let new_match = self.routes.match_route(current_url.path());
|
||||
let owner = self.outer_owner.child();
|
||||
@@ -404,19 +411,21 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Loc, Defs, FalFn, Fal> RenderHtml for FlatRoutesView<Loc, Defs, FalFn>
|
||||
impl<Loc, Defs, FalFn, Fal, R> RenderHtml<R>
|
||||
for FlatRoutesView<Loc, Defs, FalFn, R>
|
||||
where
|
||||
Loc: LocationProvider + Send,
|
||||
Defs: MatchNestedRoutes + Send + 'static,
|
||||
Defs: MatchNestedRoutes<R> + Send + 'static,
|
||||
FalFn: FnOnce() -> Fal + Send,
|
||||
Fal: RenderHtml + 'static,
|
||||
Fal: RenderHtml<R> + 'static,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
const MIN_LENGTH: usize = <Either<
|
||||
Fal,
|
||||
<Defs::Match as MatchInterface>::View,
|
||||
> as RenderHtml>::MIN_LENGTH;
|
||||
<Defs::Match as MatchInterface<R>>::View,
|
||||
> as RenderHtml<R>>::MIN_LENGTH;
|
||||
|
||||
fn dry_resolve(&mut self) {}
|
||||
|
||||
@@ -500,7 +509,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
// this can be mostly the same as the build() implementation, but with hydrate()
|
||||
@@ -573,7 +582,7 @@ where
|
||||
provide_context(params_memo);
|
||||
provide_context(url);
|
||||
provide_context(Matched(ArcMemo::from(matched)));
|
||||
OwnedView::new(view.choose().await)
|
||||
view.choose().await
|
||||
}
|
||||
})
|
||||
}));
|
||||
|
||||
@@ -13,7 +13,7 @@ use std::{
|
||||
future::Future,
|
||||
mem,
|
||||
};
|
||||
use tachys::view::RenderHtml;
|
||||
use tachys::{renderer::Renderer, view::RenderHtml};
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
/// A route that this application can serve.
|
||||
@@ -220,9 +220,12 @@ impl RouteList {
|
||||
static GENERATED: RefCell<Option<RouteList>> = const { RefCell::new(None) };
|
||||
}
|
||||
|
||||
pub fn generate<T>(app: impl FnOnce() -> T) -> Option<Self>
|
||||
pub fn generate<T, Rndr>(app: impl FnOnce() -> T) -> Option<Self>
|
||||
where
|
||||
T: RenderHtml,
|
||||
T: RenderHtml<Rndr>,
|
||||
Rndr: Renderer,
|
||||
Rndr::Node: Clone,
|
||||
Rndr::Element: Clone,
|
||||
{
|
||||
Self::IS_GENERATING.set(true);
|
||||
// run the app once, but throw away the HTML
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
use either_of::*;
|
||||
use std::{future::Future, marker::PhantomData};
|
||||
use tachys::view::{any_view::AnyView, Render};
|
||||
use tachys::{
|
||||
renderer::Renderer,
|
||||
view::{any_view::AnyView, Render},
|
||||
};
|
||||
|
||||
pub trait ChooseView
|
||||
pub trait ChooseView<R>
|
||||
where
|
||||
Self: Send + Clone + 'static,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type Output;
|
||||
|
||||
@@ -13,10 +17,11 @@ where
|
||||
fn preload(&self) -> impl Future<Output = ()>;
|
||||
}
|
||||
|
||||
impl<F, View> ChooseView for F
|
||||
impl<F, View, R> ChooseView<R> for F
|
||||
where
|
||||
F: Fn() -> View + Send + Clone + 'static,
|
||||
View: Render + Send,
|
||||
View: Render<R> + Send,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type Output = View;
|
||||
|
||||
@@ -27,11 +32,12 @@ where
|
||||
async fn preload(&self) {}
|
||||
}
|
||||
|
||||
impl<T> ChooseView for Lazy<T>
|
||||
impl<T, R> ChooseView<R> for Lazy<T>
|
||||
where
|
||||
T: LazyRoute,
|
||||
T: LazyRoute<R>,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type Output = AnyView;
|
||||
type Output = AnyView<R>;
|
||||
|
||||
async fn choose(self) -> Self::Output {
|
||||
T::data().view().await
|
||||
@@ -42,10 +48,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub trait LazyRoute: Send + 'static {
|
||||
pub trait LazyRoute<R>: Send + 'static
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
fn data() -> Self;
|
||||
|
||||
fn view(self) -> impl Future<Output = AnyView>;
|
||||
fn view(self) -> impl Future<Output = AnyView<R>>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -73,7 +82,10 @@ impl<T> Default for Lazy<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl ChooseView for () {
|
||||
impl<R> ChooseView<R> for ()
|
||||
where
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type Output = ();
|
||||
|
||||
async fn choose(self) -> Self::Output {}
|
||||
@@ -81,10 +93,11 @@ impl ChooseView for () {
|
||||
async fn preload(&self) {}
|
||||
}
|
||||
|
||||
impl<A, B> ChooseView for Either<A, B>
|
||||
impl<A, B, Rndr> ChooseView<Rndr> for Either<A, B>
|
||||
where
|
||||
A: ChooseView,
|
||||
B: ChooseView,
|
||||
A: ChooseView<Rndr>,
|
||||
B: ChooseView<Rndr>,
|
||||
Rndr: Renderer + 'static,
|
||||
{
|
||||
type Output = Either<A::Output, B::Output>;
|
||||
|
||||
@@ -105,9 +118,10 @@ where
|
||||
|
||||
macro_rules! tuples {
|
||||
($either:ident => $($ty:ident),*) => {
|
||||
impl<$($ty,)*> ChooseView for $either<$($ty,)*>
|
||||
impl<$($ty,)* Rndr> ChooseView<Rndr> for $either<$($ty,)*>
|
||||
where
|
||||
$($ty: ChooseView,)*
|
||||
$($ty: ChooseView<Rndr>,)*
|
||||
Rndr: Renderer + 'static,
|
||||
{
|
||||
type Output = $either<$($ty::Output,)*>;
|
||||
|
||||
|
||||
@@ -9,17 +9,21 @@ mod vertical;
|
||||
use crate::{static_routes::RegenerationFn, Method, SsrMode};
|
||||
pub use horizontal::*;
|
||||
pub use nested::*;
|
||||
use std::{borrow::Cow, collections::HashSet};
|
||||
use tachys::view::{Render, RenderHtml};
|
||||
use std::{borrow::Cow, collections::HashSet, marker::PhantomData};
|
||||
use tachys::{
|
||||
renderer::Renderer,
|
||||
view::{Render, RenderHtml},
|
||||
};
|
||||
pub use vertical::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Routes<Children> {
|
||||
pub struct Routes<Children, Rndr> {
|
||||
base: Option<Cow<'static, str>>,
|
||||
children: Children,
|
||||
ty: PhantomData<Rndr>,
|
||||
}
|
||||
|
||||
impl<Children> Clone for Routes<Children>
|
||||
impl<Children, Rndr> Clone for Routes<Children, Rndr>
|
||||
where
|
||||
Children: Clone,
|
||||
{
|
||||
@@ -27,15 +31,17 @@ where
|
||||
Self {
|
||||
base: self.base.clone(),
|
||||
children: self.children.clone(),
|
||||
ty: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Children> Routes<Children> {
|
||||
impl<Children, Rndr> Routes<Children, Rndr> {
|
||||
pub fn new(children: Children) -> Self {
|
||||
Self {
|
||||
base: None,
|
||||
children,
|
||||
ty: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,13 +52,15 @@ impl<Children> Routes<Children> {
|
||||
Self {
|
||||
base: Some(base.into()),
|
||||
children,
|
||||
ty: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Children> Routes<Children>
|
||||
impl<Children, Rndr> Routes<Children, Rndr>
|
||||
where
|
||||
Children: MatchNestedRoutes,
|
||||
Rndr: Renderer + 'static,
|
||||
Children: MatchNestedRoutes<Rndr>,
|
||||
{
|
||||
pub fn match_route(&self, path: &str) -> Option<Children::Match> {
|
||||
let path = match &self.base {
|
||||
@@ -93,9 +101,12 @@ where
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct RouteMatchId(pub(crate) u16);
|
||||
|
||||
pub trait MatchInterface {
|
||||
type Child: MatchInterface + MatchParams + 'static;
|
||||
type View: Render + RenderHtml + Send + 'static;
|
||||
pub trait MatchInterface<R>
|
||||
where
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type Child: MatchInterface<R> + MatchParams + 'static;
|
||||
type View: Render<R> + RenderHtml<R> + Send + 'static;
|
||||
|
||||
fn as_id(&self) -> RouteMatchId;
|
||||
|
||||
@@ -103,7 +114,7 @@ pub trait MatchInterface {
|
||||
|
||||
fn into_view_and_child(
|
||||
self,
|
||||
) -> (impl ChooseView<Output = Self::View>, Option<Self::Child>);
|
||||
) -> (impl ChooseView<R, Output = Self::View>, Option<Self::Child>);
|
||||
}
|
||||
|
||||
pub trait MatchParams {
|
||||
@@ -112,10 +123,13 @@ pub trait MatchParams {
|
||||
fn to_params(&self) -> Self::Params;
|
||||
}
|
||||
|
||||
pub trait MatchNestedRoutes {
|
||||
pub trait MatchNestedRoutes<R>
|
||||
where
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type Data;
|
||||
type View;
|
||||
type Match: MatchInterface + MatchParams;
|
||||
type Match: MatchInterface<R> + MatchParams;
|
||||
|
||||
fn match_nested<'a>(
|
||||
&'a self,
|
||||
@@ -143,11 +157,12 @@ mod tests {
|
||||
WildcardSegment,
|
||||
};
|
||||
use either_of::Either;
|
||||
use tachys::renderer::dom::Dom;
|
||||
|
||||
#[test]
|
||||
pub fn matches_single_root_route() {
|
||||
let routes =
|
||||
Routes::<_>::new(NestedRoute::new(StaticSegment("/"), || ()));
|
||||
Routes::<_, Dom>::new(NestedRoute::new(StaticSegment("/"), || ()));
|
||||
let matched = routes.match_route("/");
|
||||
assert!(matched.is_some());
|
||||
// this case seems like it should match, but implementing it interferes with
|
||||
@@ -163,7 +178,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
pub fn matches_nested_route() {
|
||||
let routes: Routes<_> =
|
||||
let routes: Routes<_, Dom> =
|
||||
Routes::new(NestedRoute::new(StaticSegment(""), || "Home").child(
|
||||
NestedRoute::new(
|
||||
(StaticSegment("author"), StaticSegment("contact")),
|
||||
@@ -185,17 +200,17 @@ mod tests {
|
||||
);
|
||||
|
||||
let matched = routes.match_route("/author/contact").unwrap();
|
||||
assert_eq!(MatchInterface::as_matched(&matched), "");
|
||||
let (_, child) = MatchInterface::into_view_and_child(matched);
|
||||
assert_eq!(MatchInterface::<Dom>::as_matched(&matched), "");
|
||||
let (_, child) = MatchInterface::<Dom>::into_view_and_child(matched);
|
||||
assert_eq!(
|
||||
MatchInterface::as_matched(&child.unwrap()),
|
||||
MatchInterface::<Dom>::as_matched(&child.unwrap()),
|
||||
"/author/contact"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn does_not_match_route_unless_full_param_matches() {
|
||||
let routes = Routes::<_>::new((
|
||||
let routes = Routes::<_, Dom>::new((
|
||||
NestedRoute::new(StaticSegment("/property-api"), || ()),
|
||||
NestedRoute::new(StaticSegment("/property"), || ()),
|
||||
));
|
||||
@@ -205,7 +220,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
pub fn does_not_match_incomplete_route() {
|
||||
let routes: Routes<_> =
|
||||
let routes: Routes<_, Dom> =
|
||||
Routes::new(NestedRoute::new(StaticSegment(""), || "Home").child(
|
||||
NestedRoute::new(
|
||||
(StaticSegment("author"), StaticSegment("contact")),
|
||||
@@ -218,7 +233,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
pub fn chooses_between_nested_routes() {
|
||||
let routes: Routes<_> = Routes::new((
|
||||
let routes: Routes<_, Dom> = Routes::new((
|
||||
NestedRoute::new(StaticSegment("/"), || ()).child((
|
||||
NestedRoute::new(StaticSegment(""), || ()),
|
||||
NestedRoute::new(StaticSegment("about"), || ()),
|
||||
@@ -272,7 +287,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
pub fn arbitrary_nested_routes() {
|
||||
let routes: Routes<_> = Routes::new_with_base(
|
||||
let routes: Routes<_, Dom> = Routes::new_with_base(
|
||||
(
|
||||
NestedRoute::new(StaticSegment("/"), || ()).child((
|
||||
NestedRoute::new(StaticSegment("/"), || ()),
|
||||
|
||||
@@ -8,27 +8,32 @@ use either_of::Either;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::HashSet,
|
||||
marker::PhantomData,
|
||||
sync::atomic::{AtomicU16, Ordering},
|
||||
};
|
||||
use tachys::view::{Render, RenderHtml};
|
||||
use tachys::{
|
||||
renderer::Renderer,
|
||||
view::{Render, RenderHtml},
|
||||
};
|
||||
|
||||
mod tuples;
|
||||
|
||||
static ROUTE_ID: AtomicU16 = AtomicU16::new(1);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct NestedRoute<Segments, Children, Data, View> {
|
||||
pub struct NestedRoute<Segments, Children, Data, View, R> {
|
||||
id: u16,
|
||||
segments: Segments,
|
||||
children: Option<Children>,
|
||||
data: Data,
|
||||
view: View,
|
||||
rndr: PhantomData<R>,
|
||||
methods: HashSet<Method>,
|
||||
ssr_mode: SsrMode,
|
||||
}
|
||||
|
||||
impl<Segments, Children, Data, View> Clone
|
||||
for NestedRoute<Segments, Children, Data, View>
|
||||
impl<Segments, Children, Data, View, R> Clone
|
||||
for NestedRoute<Segments, Children, Data, View, R>
|
||||
where
|
||||
Segments: Clone,
|
||||
Children: Clone,
|
||||
@@ -42,16 +47,18 @@ where
|
||||
children: self.children.clone(),
|
||||
data: self.data.clone(),
|
||||
view: self.view.clone(),
|
||||
rndr: PhantomData,
|
||||
methods: self.methods.clone(),
|
||||
ssr_mode: self.ssr_mode.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Segments, View> NestedRoute<Segments, (), (), View> {
|
||||
impl<Segments, View, R> NestedRoute<Segments, (), (), View, R> {
|
||||
pub fn new(path: Segments, view: View) -> Self
|
||||
where
|
||||
View: ChooseView,
|
||||
View: ChooseView<R>,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
Self {
|
||||
id: ROUTE_ID.fetch_add(1, Ordering::Relaxed),
|
||||
@@ -59,22 +66,24 @@ impl<Segments, View> NestedRoute<Segments, (), (), View> {
|
||||
children: None,
|
||||
data: (),
|
||||
view,
|
||||
rndr: PhantomData,
|
||||
methods: [Method::Get].into(),
|
||||
ssr_mode: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Segments, Data, View> NestedRoute<Segments, (), Data, View> {
|
||||
impl<Segments, Data, View, R> NestedRoute<Segments, (), Data, View, R> {
|
||||
pub fn child<Children>(
|
||||
self,
|
||||
child: Children,
|
||||
) -> NestedRoute<Segments, Children, Data, View> {
|
||||
) -> NestedRoute<Segments, Children, Data, View, R> {
|
||||
let Self {
|
||||
id,
|
||||
segments,
|
||||
data,
|
||||
view,
|
||||
rndr,
|
||||
ssr_mode,
|
||||
methods,
|
||||
..
|
||||
@@ -87,6 +96,7 @@ impl<Segments, Data, View> NestedRoute<Segments, (), Data, View> {
|
||||
view,
|
||||
ssr_mode,
|
||||
methods,
|
||||
rndr,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,12 +146,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<ParamsIter, Child, View> MatchInterface
|
||||
impl<ParamsIter, Child, View, Rndr> MatchInterface<Rndr>
|
||||
for NestedMatch<ParamsIter, Child, View>
|
||||
where
|
||||
Child: MatchInterface + MatchParams + 'static,
|
||||
View: ChooseView,
|
||||
View::Output: Render + RenderHtml + Send + 'static,
|
||||
Rndr: Renderer + 'static,
|
||||
Child: MatchInterface<Rndr> + MatchParams + 'static,
|
||||
View: ChooseView<Rndr>,
|
||||
View::Output: Render<Rndr> + RenderHtml<Rndr> + Send + 'static,
|
||||
{
|
||||
type Child = Child;
|
||||
type View = View::Output;
|
||||
@@ -156,24 +167,28 @@ where
|
||||
|
||||
fn into_view_and_child(
|
||||
self,
|
||||
) -> (impl ChooseView<Output = Self::View>, Option<Self::Child>) {
|
||||
) -> (
|
||||
impl ChooseView<Rndr, Output = Self::View>,
|
||||
Option<Self::Child>,
|
||||
) {
|
||||
(self.view_fn, self.child)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Segments, Children, Data, View> MatchNestedRoutes
|
||||
for NestedRoute<Segments, Children, Data, View>
|
||||
impl<Segments, Children, Data, View, Rndr> MatchNestedRoutes<Rndr>
|
||||
for NestedRoute<Segments, Children, Data, View, Rndr>
|
||||
where
|
||||
Self: 'static,
|
||||
Rndr: Renderer + 'static,
|
||||
Segments: PossibleRouteMatch + std::fmt::Debug,
|
||||
<<Segments as PossibleRouteMatch>::ParamsIter as IntoIterator>::IntoIter: Clone,
|
||||
Children: MatchNestedRoutes,
|
||||
<<<Children as MatchNestedRoutes>::Match as MatchParams>::Params as IntoIterator>::IntoIter: Clone,
|
||||
Children: MatchNestedRoutes<Rndr>,
|
||||
<<<Children as MatchNestedRoutes<Rndr>>::Match as MatchParams>::Params as IntoIterator>::IntoIter: Clone,
|
||||
Children::Match: MatchParams,
|
||||
Children: 'static,
|
||||
<Children::Match as MatchParams>::Params: Clone,
|
||||
View: ChooseView + Clone,
|
||||
View::Output: Render + RenderHtml + Send + 'static,
|
||||
View: ChooseView<Rndr> + Clone,
|
||||
View::Output: Render<Rndr> + RenderHtml<Rndr> + Send + 'static,
|
||||
{
|
||||
type Data = Data;
|
||||
type View = View::Output;
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::{ChooseView, GeneratedRouteData, MatchParams};
|
||||
use core::iter;
|
||||
use either_of::*;
|
||||
use std::borrow::Cow;
|
||||
use tachys::renderer::Renderer;
|
||||
|
||||
impl MatchParams for () {
|
||||
type Params = iter::Empty<(Cow<'static, str>, String)>;
|
||||
@@ -12,7 +13,10 @@ impl MatchParams for () {
|
||||
}
|
||||
}
|
||||
|
||||
impl MatchInterface for () {
|
||||
impl<Rndr> MatchInterface<Rndr> for ()
|
||||
where
|
||||
Rndr: Renderer + 'static,
|
||||
{
|
||||
type Child = ();
|
||||
type View = ();
|
||||
|
||||
@@ -26,12 +30,18 @@ impl MatchInterface for () {
|
||||
|
||||
fn into_view_and_child(
|
||||
self,
|
||||
) -> (impl ChooseView<Output = Self::View>, Option<Self::Child>) {
|
||||
) -> (
|
||||
impl ChooseView<Rndr, Output = Self::View>,
|
||||
Option<Self::Child>,
|
||||
) {
|
||||
((), None)
|
||||
}
|
||||
}
|
||||
|
||||
impl MatchNestedRoutes for () {
|
||||
impl<Rndr> MatchNestedRoutes<Rndr> for ()
|
||||
where
|
||||
Rndr: Renderer + 'static,
|
||||
{
|
||||
type Data = ();
|
||||
type View = ();
|
||||
type Match = ();
|
||||
@@ -64,9 +74,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> MatchInterface for (A,)
|
||||
impl<A, Rndr> MatchInterface<Rndr> for (A,)
|
||||
where
|
||||
A: MatchInterface + 'static,
|
||||
A: MatchInterface<Rndr> + 'static,
|
||||
Rndr: Renderer + 'static,
|
||||
{
|
||||
type Child = A::Child;
|
||||
type View = A::View;
|
||||
@@ -81,14 +92,18 @@ where
|
||||
|
||||
fn into_view_and_child(
|
||||
self,
|
||||
) -> (impl ChooseView<Output = Self::View>, Option<Self::Child>) {
|
||||
) -> (
|
||||
impl ChooseView<Rndr, Output = Self::View>,
|
||||
Option<Self::Child>,
|
||||
) {
|
||||
self.0.into_view_and_child()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> MatchNestedRoutes for (A,)
|
||||
impl<A, Rndr> MatchNestedRoutes<Rndr> for (A,)
|
||||
where
|
||||
A: MatchNestedRoutes + 'static,
|
||||
A: MatchNestedRoutes<Rndr> + 'static,
|
||||
Rndr: Renderer + 'static,
|
||||
{
|
||||
type Data = A::Data;
|
||||
type View = A::View;
|
||||
@@ -126,10 +141,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> MatchInterface for Either<A, B>
|
||||
impl<A, B, Rndr> MatchInterface<Rndr> for Either<A, B>
|
||||
where
|
||||
A: MatchInterface,
|
||||
B: MatchInterface,
|
||||
Rndr: Renderer + 'static,
|
||||
A: MatchInterface<Rndr>,
|
||||
B: MatchInterface<Rndr>,
|
||||
{
|
||||
type Child = Either<A::Child, B::Child>;
|
||||
type View = Either<A::View, B::View>;
|
||||
@@ -150,7 +166,10 @@ where
|
||||
|
||||
fn into_view_and_child(
|
||||
self,
|
||||
) -> (impl ChooseView<Output = Self::View>, Option<Self::Child>) {
|
||||
) -> (
|
||||
impl ChooseView<Rndr, Output = Self::View>,
|
||||
Option<Self::Child>,
|
||||
) {
|
||||
match self {
|
||||
Either::Left(i) => {
|
||||
let (view, child) = i.into_view_and_child();
|
||||
@@ -164,10 +183,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> MatchNestedRoutes for (A, B)
|
||||
impl<A, B, Rndr> MatchNestedRoutes<Rndr> for (A, B)
|
||||
where
|
||||
A: MatchNestedRoutes,
|
||||
B: MatchNestedRoutes,
|
||||
A: MatchNestedRoutes<Rndr>,
|
||||
B: MatchNestedRoutes<Rndr>,
|
||||
Rndr: Renderer + 'static,
|
||||
{
|
||||
type Data = (A::Data, B::Data);
|
||||
type View = Either<A::View, B::View>;
|
||||
@@ -231,9 +251,10 @@ macro_rules! tuples {
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($ty,)*> MatchInterface for $either <$($ty,)*>
|
||||
impl<Rndr, $($ty,)*> MatchInterface<Rndr> for $either <$($ty,)*>
|
||||
where
|
||||
$($ty: MatchInterface + 'static),*,
|
||||
Rndr: Renderer + 'static,
|
||||
$($ty: MatchInterface<Rndr> + 'static),*,
|
||||
{
|
||||
type Child = $either<$($ty::Child,)*>;
|
||||
type View = $either<$($ty::View,)*>;
|
||||
@@ -253,7 +274,7 @@ macro_rules! tuples {
|
||||
fn into_view_and_child(
|
||||
self,
|
||||
) -> (
|
||||
impl ChooseView<Output = Self::View>,
|
||||
impl ChooseView<Rndr, Output = Self::View>,
|
||||
Option<Self::Child>,
|
||||
) {
|
||||
match self {
|
||||
@@ -265,9 +286,10 @@ macro_rules! tuples {
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($ty),*> MatchNestedRoutes for ($($ty,)*)
|
||||
impl<Rndr, $($ty),*> MatchNestedRoutes<Rndr> for ($($ty,)*)
|
||||
where
|
||||
$($ty: MatchNestedRoutes + 'static),*,
|
||||
Rndr: Renderer + 'static,
|
||||
$($ty: MatchNestedRoutes<Rndr> + 'static),*,
|
||||
{
|
||||
type Data = ($($ty::Data,)*);
|
||||
type View = $either<$($ty::View,)*>;
|
||||
|
||||
@@ -23,7 +23,9 @@ use std::{
|
||||
cell::RefCell,
|
||||
fmt::Debug,
|
||||
future::Future,
|
||||
iter, mem,
|
||||
iter,
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
pin::Pin,
|
||||
rc::Rc,
|
||||
sync::{Arc, Mutex},
|
||||
@@ -31,6 +33,7 @@ use std::{
|
||||
use tachys::{
|
||||
hydration::Cursor,
|
||||
reactive_graph::{OwnedView, Suspend},
|
||||
renderer::Renderer,
|
||||
ssr::StreamBuilder,
|
||||
view::{
|
||||
add_attr::AddAnyAttr,
|
||||
@@ -40,38 +43,42 @@ use tachys::{
|
||||
},
|
||||
};
|
||||
|
||||
pub(crate) struct NestedRoutesView<Loc, Defs, FalFn> {
|
||||
pub(crate) struct NestedRoutesView<Loc, Defs, FalFn, R> {
|
||||
pub location: Option<Loc>,
|
||||
pub routes: Routes<Defs>,
|
||||
pub routes: Routes<Defs, R>,
|
||||
pub outer_owner: Owner,
|
||||
pub current_url: ArcRwSignal<Url>,
|
||||
pub base: Option<Oco<'static, str>>,
|
||||
pub fallback: FalFn,
|
||||
#[allow(unused)] // TODO
|
||||
pub set_is_routing: Option<SignalSetter<bool>>,
|
||||
pub rndr: PhantomData<R>,
|
||||
}
|
||||
|
||||
pub struct NestedRouteViewState<Fal>
|
||||
pub struct NestedRouteViewState<Fal, R>
|
||||
where
|
||||
Fal: Render,
|
||||
Fal: Render<R>,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
path: String,
|
||||
current_url: ArcRwSignal<Url>,
|
||||
outlets: Vec<RouteContext>,
|
||||
outlets: Vec<RouteContext<R>>,
|
||||
// TODO loading fallback
|
||||
#[allow(clippy::type_complexity)]
|
||||
view: Rc<RefCell<EitherOf3State<(), Fal, AnyView>>>,
|
||||
view: Rc<RefCell<EitherOf3State<(), Fal, AnyView<R>, R>>>,
|
||||
}
|
||||
|
||||
impl<Loc, Defs, FalFn, Fal> Render for NestedRoutesView<Loc, Defs, FalFn>
|
||||
impl<Loc, Defs, FalFn, Fal, R> Render<R>
|
||||
for NestedRoutesView<Loc, Defs, FalFn, R>
|
||||
where
|
||||
Loc: LocationProvider,
|
||||
Defs: MatchNestedRoutes,
|
||||
Defs: MatchNestedRoutes<R>,
|
||||
FalFn: FnOnce() -> Fal,
|
||||
Fal: Render + 'static,
|
||||
Fal: Render<R> + 'static,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
// TODO support fallback while loading
|
||||
type State = NestedRouteViewState<Fal>;
|
||||
type State = NestedRouteViewState<Fal, R>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let NestedRoutesView {
|
||||
@@ -105,7 +112,11 @@ where
|
||||
&outer_owner,
|
||||
);
|
||||
drop(url);
|
||||
outer_owner.with(|| EitherOf3::C(Outlet().into_any()))
|
||||
outer_owner.with(|| {
|
||||
EitherOf3::C(
|
||||
Outlet(OutletProps::builder().build()).into_any(),
|
||||
)
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
@@ -150,7 +161,7 @@ where
|
||||
|
||||
match new_match {
|
||||
None => {
|
||||
EitherOf3::<(), Fal, AnyView>::B((self.fallback)())
|
||||
EitherOf3::<(), Fal, AnyView<R>>::B((self.fallback)())
|
||||
.rebuild(&mut state.view.borrow_mut());
|
||||
state.outlets.clear();
|
||||
}
|
||||
@@ -180,8 +191,10 @@ where
|
||||
// if it was on the fallback, show the view instead
|
||||
if matches!(state.view.borrow().state, EitherOf3::B(_)) {
|
||||
self.outer_owner.with(|| {
|
||||
EitherOf3::<(), Fal, AnyView>::C(Outlet().into_any())
|
||||
.rebuild(&mut *state.view.borrow_mut());
|
||||
EitherOf3::<(), Fal, AnyView<R>>::C(
|
||||
Outlet(OutletProps::builder().build()).into_any(),
|
||||
)
|
||||
.rebuild(&mut *state.view.borrow_mut());
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -193,33 +206,37 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Loc, Defs, Fal, FalFn> AddAnyAttr for NestedRoutesView<Loc, Defs, FalFn>
|
||||
impl<Loc, Defs, Fal, FalFn, R> AddAnyAttr<R>
|
||||
for NestedRoutesView<Loc, Defs, FalFn, R>
|
||||
where
|
||||
Loc: LocationProvider + Send,
|
||||
Defs: MatchNestedRoutes + Send,
|
||||
Defs: MatchNestedRoutes<R> + Send,
|
||||
FalFn: FnOnce() -> Fal + Send,
|
||||
Fal: RenderHtml + 'static,
|
||||
Fal: RenderHtml<R> + 'static,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type Output<SomeNewAttr: leptos::attr::Attribute> =
|
||||
NestedRoutesView<Loc, Defs, FalFn>;
|
||||
type Output<SomeNewAttr: leptos::attr::Attribute<R>> =
|
||||
NestedRoutesView<Loc, Defs, FalFn, R>;
|
||||
|
||||
fn add_any_attr<NewAttr: leptos::attr::Attribute>(
|
||||
fn add_any_attr<NewAttr: leptos::attr::Attribute<R>>(
|
||||
self,
|
||||
_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<R>,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Loc, Defs, FalFn, Fal> RenderHtml for NestedRoutesView<Loc, Defs, FalFn>
|
||||
impl<Loc, Defs, FalFn, Fal, R> RenderHtml<R>
|
||||
for NestedRoutesView<Loc, Defs, FalFn, R>
|
||||
where
|
||||
Loc: LocationProvider + Send,
|
||||
Defs: MatchNestedRoutes + Send,
|
||||
Defs: MatchNestedRoutes<R> + Send,
|
||||
FalFn: FnOnce() -> Fal + Send,
|
||||
Fal: RenderHtml + 'static,
|
||||
Fal: RenderHtml<R> + 'static,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
@@ -313,7 +330,11 @@ where
|
||||
.now_or_never()
|
||||
.expect("async routes not supported in SSR");
|
||||
|
||||
outer_owner.with(|| Either::Right(Outlet().into_any()))
|
||||
outer_owner.with(|| {
|
||||
Either::Right(
|
||||
Outlet(OutletProps::builder().build()).into_any(),
|
||||
)
|
||||
})
|
||||
}
|
||||
};
|
||||
view.to_html_with_buf(buf, position, escape, mark_branches);
|
||||
@@ -360,7 +381,11 @@ where
|
||||
.now_or_never()
|
||||
.expect("async routes not supported in SSR");
|
||||
|
||||
outer_owner.with(|| Either::Right(Outlet().into_any()))
|
||||
outer_owner.with(|| {
|
||||
Either::Right(
|
||||
Outlet(OutletProps::builder().build()).into_any(),
|
||||
)
|
||||
})
|
||||
}
|
||||
};
|
||||
view.to_html_async_with_buf::<OUT_OF_ORDER>(
|
||||
@@ -373,7 +398,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let NestedRoutesView {
|
||||
@@ -411,7 +436,11 @@ where
|
||||
join_all(mem::take(&mut loaders))
|
||||
.now_or_never()
|
||||
.expect("async routes not supported in SSR");
|
||||
outer_owner.with(|| EitherOf3::C(Outlet().into_any()))
|
||||
outer_owner.with(|| {
|
||||
EitherOf3::C(
|
||||
Outlet(OutletProps::builder().build()).into_any(),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
.hydrate::<FROM_SERVER>(cursor, position),
|
||||
@@ -426,11 +455,15 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
type OutletViewFn = Box<
|
||||
dyn Fn() -> Suspend<Pin<Box<dyn Future<Output = AnyView> + Send>>> + Send,
|
||||
type OutletViewFn<R> = Box<
|
||||
dyn Fn() -> Suspend<Pin<Box<dyn Future<Output = AnyView<R>> + Send>>>
|
||||
+ Send,
|
||||
>;
|
||||
|
||||
pub(crate) struct RouteContext {
|
||||
pub(crate) struct RouteContext<R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
id: RouteMatchId,
|
||||
trigger: ArcTrigger,
|
||||
url: ArcRwSignal<Url>,
|
||||
@@ -438,10 +471,10 @@ pub(crate) struct RouteContext {
|
||||
owner: Owner,
|
||||
pub matched: ArcRwSignal<String>,
|
||||
base: Option<Oco<'static, str>>,
|
||||
view_fn: Arc<Mutex<OutletViewFn>>,
|
||||
view_fn: Arc<Mutex<OutletViewFn<R>>>,
|
||||
}
|
||||
|
||||
impl Debug for RouteContext {
|
||||
impl<R: Renderer> Debug for RouteContext<R> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RouteContext")
|
||||
.field("id", &self.id)
|
||||
@@ -455,13 +488,19 @@ impl Debug for RouteContext {
|
||||
}
|
||||
}
|
||||
|
||||
impl RouteContext {
|
||||
impl<R> RouteContext<R>
|
||||
where
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
fn provide_contexts(&self) {
|
||||
provide_context(self.clone());
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for RouteContext {
|
||||
impl<R> Clone for RouteContext<R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
url: self.url.clone(),
|
||||
@@ -476,13 +515,16 @@ impl Clone for RouteContext {
|
||||
}
|
||||
}
|
||||
|
||||
trait AddNestedRoute {
|
||||
trait AddNestedRoute<R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
fn build_nested_route(
|
||||
self,
|
||||
url: &Url,
|
||||
base: Option<Oco<'static, str>>,
|
||||
loaders: &mut Vec<Pin<Box<dyn Future<Output = ArcTrigger>>>>,
|
||||
outlets: &mut Vec<RouteContext>,
|
||||
outlets: &mut Vec<RouteContext<R>>,
|
||||
parent: &Owner,
|
||||
);
|
||||
|
||||
@@ -492,21 +534,22 @@ trait AddNestedRoute {
|
||||
base: Option<Oco<'static, str>>,
|
||||
items: &mut usize,
|
||||
loaders: &mut Vec<Pin<Box<dyn Future<Output = ArcTrigger>>>>,
|
||||
outlets: &mut Vec<RouteContext>,
|
||||
outlets: &mut Vec<RouteContext<R>>,
|
||||
parent: &Owner,
|
||||
);
|
||||
}
|
||||
|
||||
impl<Match> AddNestedRoute for Match
|
||||
impl<Match, R> AddNestedRoute<R> for Match
|
||||
where
|
||||
Match: MatchInterface + MatchParams,
|
||||
Match: MatchInterface<R> + MatchParams,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
fn build_nested_route(
|
||||
self,
|
||||
url: &Url,
|
||||
base: Option<Oco<'static, str>>,
|
||||
loaders: &mut Vec<Pin<Box<dyn Future<Output = ArcTrigger>>>>,
|
||||
outlets: &mut Vec<RouteContext>,
|
||||
outlets: &mut Vec<RouteContext<R>>,
|
||||
parent: &Owner,
|
||||
) {
|
||||
let orig_url = url;
|
||||
@@ -607,7 +650,7 @@ where
|
||||
OwnedView::new(view).into_any()
|
||||
})
|
||||
as Pin<
|
||||
Box<dyn Future<Output = AnyView> + Send>,
|
||||
Box<dyn Future<Output = AnyView<R>> + Send>,
|
||||
>)
|
||||
})
|
||||
});
|
||||
@@ -635,7 +678,7 @@ where
|
||||
base: Option<Oco<'static, str>>,
|
||||
items: &mut usize,
|
||||
loaders: &mut Vec<Pin<Box<dyn Future<Output = ArcTrigger>>>>,
|
||||
outlets: &mut Vec<RouteContext>,
|
||||
outlets: &mut Vec<RouteContext<R>>,
|
||||
parent: &Owner,
|
||||
) {
|
||||
let (parent_params, parent_matches): (Vec<_>, Vec<_>) = outlets
|
||||
@@ -790,33 +833,32 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Fal> Mountable for NestedRouteViewState<Fal>
|
||||
impl<Fal, R> Mountable<R> for NestedRouteViewState<Fal, R>
|
||||
where
|
||||
Fal: Render,
|
||||
Fal: Render<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
self.view.unmount();
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &leptos::tachys::renderer::types::Element,
|
||||
marker: Option<&leptos::tachys::renderer::types::Node>,
|
||||
) {
|
||||
fn mount(&mut self, parent: &R::Element, marker: Option<&R::Node>) {
|
||||
self.view.mount(parent, marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
self.view.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Outlet() -> impl RenderHtml
|
||||
pub fn Outlet<R>(#[prop(optional)] rndr: PhantomData<R>) -> impl RenderHtml<R>
|
||||
where
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
_ = rndr;
|
||||
move || {
|
||||
let ctx = use_context::<RouteContext>()
|
||||
let ctx = use_context::<RouteContext<R>>()
|
||||
.expect("<Outlet/> used without RouteContext being provided.");
|
||||
let RouteContext {
|
||||
trigger, view_fn, ..
|
||||
|
||||
@@ -26,7 +26,7 @@ pub fn ReactiveRouter<Rndr, Loc, DefFn, Defs, FallbackFn, Fallback>(
|
||||
mut location: Loc,
|
||||
routes: DefFn,
|
||||
fallback: FallbackFn,
|
||||
) -> impl RenderHtml
|
||||
) -> impl RenderHtml<Rndr>
|
||||
where
|
||||
DefFn: Fn() -> Defs + 'static,
|
||||
Defs: 'static,
|
||||
@@ -35,9 +35,10 @@ where
|
||||
Rndr::Element: Clone,
|
||||
Rndr::Node: Clone,
|
||||
FallbackFn: Fn() -> Fallback + Clone + 'static,
|
||||
Fallback: Render + 'static,
|
||||
Fallback: Render<Rndr> + 'static,
|
||||
Router<Rndr, Loc, Defs, FallbackFn>: FallbackOrView,
|
||||
<Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output: RenderHtml,
|
||||
<Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output:
|
||||
RenderHtml<Rndr>,
|
||||
{
|
||||
// create a reactive URL signal that will drive the router view
|
||||
let url = ArcRwSignal::new(location.try_to_url().unwrap_or_default());
|
||||
@@ -74,7 +75,7 @@ where
|
||||
fal: PhantomData<Fallback>,
|
||||
}
|
||||
|
||||
impl<Rndr, Loc, Defs, FallbackFn, Fallback> Render
|
||||
impl<Rndr, Loc, Defs, FallbackFn, Fallback> Render<Rndr>
|
||||
for ReactiveRouterInner<Rndr, Loc, Defs, FallbackFn, Fallback>
|
||||
where
|
||||
Loc: Location,
|
||||
@@ -82,9 +83,10 @@ where
|
||||
Rndr::Element: Clone,
|
||||
Rndr::Node: Clone,
|
||||
FallbackFn: Fn() -> Fallback,
|
||||
Fallback: Render,
|
||||
Fallback: Render<Rndr>,
|
||||
Router<Rndr, Loc, Defs, FallbackFn>: FallbackOrView,
|
||||
<Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output: Render,
|
||||
<Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output:
|
||||
Render<Rndr>,
|
||||
{
|
||||
type State =
|
||||
ReactiveRouterInnerState<Rndr, Loc, Defs, FallbackFn, Fallback>;
|
||||
@@ -110,7 +112,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Rndr, Loc, Defs, FallbackFn, Fallback> RenderHtml
|
||||
impl<Rndr, Loc, Defs, FallbackFn, Fallback> RenderHtml<Rndr>
|
||||
for ReactiveRouterInner<Rndr, Loc, Defs, FallbackFn, Fallback>
|
||||
where
|
||||
Loc: Location,
|
||||
@@ -118,18 +120,18 @@ where
|
||||
Rndr::Element: Clone,
|
||||
Rndr::Node: Clone,
|
||||
FallbackFn: Fn() -> Fallback,
|
||||
Fallback: Render,
|
||||
Fallback: Render<Rndr>,
|
||||
Router<Rndr, Loc, Defs, FallbackFn>: FallbackOrView,
|
||||
<Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output: RenderHtml,
|
||||
<Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output:
|
||||
RenderHtml<Rndr>,
|
||||
{
|
||||
const MIN_LENGTH: usize = <<Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output as RenderHtml>::MIN_LENGTH;
|
||||
const MIN_LENGTH: usize = <<Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output as RenderHtml<Rndr>>::MIN_LENGTH;
|
||||
|
||||
fn to_html_with_buf(
|
||||
self,
|
||||
buf: &mut String,
|
||||
position: &mut Position,
|
||||
escape: bool,
|
||||
mark_branches: bool,
|
||||
escape: bool, mark_branches: bool
|
||||
) {
|
||||
// if this is being run on the server for the first time, generating all possible routes
|
||||
if RouteList::is_generating() {
|
||||
@@ -154,8 +156,7 @@ where
|
||||
self,
|
||||
buf: &mut StreamBuilder,
|
||||
position: &mut Position,
|
||||
escape: bool,
|
||||
mark_branches: bool,
|
||||
escape: bool, mark_branches: bool
|
||||
) where
|
||||
Self: Sized,
|
||||
{
|
||||
@@ -167,7 +168,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let (prev_id, inner) = self.inner.fallback_or_view();
|
||||
@@ -185,20 +186,21 @@ where
|
||||
struct ReactiveRouterInnerState<Rndr, Loc, Defs, FallbackFn, Fallback>
|
||||
where
|
||||
Router<Rndr, Loc, Defs, FallbackFn>: FallbackOrView,
|
||||
<Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output: Render,
|
||||
<Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
owner: Owner,
|
||||
prev_id: &'static str,
|
||||
inner: <<Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output as Render>::State,
|
||||
inner: <<Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output as Render<Rndr>>::State,
|
||||
fal: PhantomData<Fallback>,
|
||||
}
|
||||
|
||||
impl<Rndr, Loc, Defs, FallbackFn, Fallback> Mountable
|
||||
impl<Rndr, Loc, Defs, FallbackFn, Fallback> Mountable<Rndr>
|
||||
for ReactiveRouterInnerState<Rndr, Loc, Defs, FallbackFn, Fallback>
|
||||
where
|
||||
Router<Rndr, Loc, Defs, FallbackFn>: FallbackOrView,
|
||||
<Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output: Render,
|
||||
<Router<Rndr, Loc, Defs, FallbackFn> as FallbackOrView>::Output:
|
||||
Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
@@ -207,13 +209,13 @@ where
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &leptos::tachys::renderer::types::Element,
|
||||
marker: Option<&leptos::tachys::renderer::types::Node>,
|
||||
parent: &<Rndr as Renderer>::Element,
|
||||
marker: Option<&<Rndr as Renderer>::Node>,
|
||||
) {
|
||||
self.inner.mount(parent, marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<Rndr>) -> bool {
|
||||
self.inner.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
@@ -246,12 +248,12 @@ impl ReactiveMatchedRoute {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reactive_route<ViewFn, View>(
|
||||
pub fn reactive_route<ViewFn, View, Rndr>(
|
||||
view_fn: ViewFn,
|
||||
) -> impl Fn(MatchedRoute) -> ReactiveRoute<ViewFn, View>
|
||||
) -> impl Fn(MatchedRoute) -> ReactiveRoute<ViewFn, View, Rndr>
|
||||
where
|
||||
ViewFn: Fn(&ReactiveMatchedRoute) -> View + Clone,
|
||||
View: Render,
|
||||
View: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
move |matched| ReactiveRoute {
|
||||
@@ -261,21 +263,21 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReactiveRoute<ViewFn, View>
|
||||
pub struct ReactiveRoute<ViewFn, View, Rndr>
|
||||
where
|
||||
ViewFn: Fn(&ReactiveMatchedRoute) -> View,
|
||||
View: Render,
|
||||
View: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
view_fn: ViewFn,
|
||||
matched: MatchedRoute,
|
||||
ty: PhantomData,
|
||||
ty: PhantomData<Rndr>,
|
||||
}
|
||||
|
||||
impl<ViewFn, View> Render for ReactiveRoute<ViewFn, View>
|
||||
impl<ViewFn, View, Rndr> Render<Rndr> for ReactiveRoute<ViewFn, View, Rndr>
|
||||
where
|
||||
ViewFn: Fn(&ReactiveMatchedRoute) -> View,
|
||||
View: Render,
|
||||
View: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type State = ReactiveRouteState<View::State>;
|
||||
@@ -308,10 +310,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<ViewFn, View> RenderHtml for ReactiveRoute<ViewFn, View>
|
||||
impl<ViewFn, View, Rndr> RenderHtml<Rndr> for ReactiveRoute<ViewFn, View, Rndr>
|
||||
where
|
||||
ViewFn: Fn(&ReactiveMatchedRoute) -> View,
|
||||
View: RenderHtml,
|
||||
View: RenderHtml<Rndr>,
|
||||
Rndr: Renderer,
|
||||
Rndr::Node: Clone,
|
||||
Rndr::Element: Clone,
|
||||
@@ -322,8 +324,7 @@ where
|
||||
self,
|
||||
buf: &mut String,
|
||||
position: &mut Position,
|
||||
escape: bool,
|
||||
mark_branches: bool,
|
||||
escape: bool, mark_branches: bool
|
||||
) {
|
||||
let MatchedRoute {
|
||||
search_params,
|
||||
@@ -344,8 +345,7 @@ where
|
||||
self,
|
||||
buf: &mut StreamBuilder,
|
||||
position: &mut Position,
|
||||
escape: bool,
|
||||
mark_branches: bool,
|
||||
escape: bool, mark_branches: bool
|
||||
) where
|
||||
Self: Sized,
|
||||
{
|
||||
@@ -367,7 +367,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let MatchedRoute {
|
||||
@@ -401,9 +401,10 @@ impl<State> Drop for ReactiveRouteState<State> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Mountable for ReactiveRouteState<T>
|
||||
impl<T, R> Mountable<R> for ReactiveRouteState<T>
|
||||
where
|
||||
T: Mountable,
|
||||
T: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
self.view_state.unmount();
|
||||
@@ -417,7 +418,7 @@ where
|
||||
self.view_state.mount(parent, marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
self.view_state.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ use tachys::{
|
||||
pub struct Router<Rndr, Loc, Children, FallbackFn> {
|
||||
base: Option<Cow<'static, str>>,
|
||||
location: PhantomData<Loc>,
|
||||
pub routes: Routes<Children>,
|
||||
pub routes: Routes<Children, Rndr>,
|
||||
fallback: FallbackFn,
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ where
|
||||
FallbackFn: Fn() -> Fallback,
|
||||
{
|
||||
pub fn new(
|
||||
routes: Routes<Children>,
|
||||
routes: Routes<Children, Rndr>,
|
||||
fallback: FallbackFn,
|
||||
) -> Router<Rndr, Loc, Children, FallbackFn> {
|
||||
Self {
|
||||
@@ -73,7 +73,7 @@ where
|
||||
|
||||
pub fn new_with_base(
|
||||
base: impl Into<Cow<'static, str>>,
|
||||
routes: Routes<Children>,
|
||||
routes: Routes<Children, Rndr>,
|
||||
fallback: FallbackFn,
|
||||
) -> Router<Rndr, Loc, Children, FallbackFn> {
|
||||
Self {
|
||||
@@ -98,28 +98,28 @@ where
|
||||
|
||||
pub struct
|
||||
where
|
||||
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
pub params: ArcMemo<Params>,
|
||||
pub outlet: Outlet,
|
||||
pub outlet: Outlet<R>,
|
||||
}
|
||||
|
||||
impl<Rndr, Loc, FallbackFn, Fallback, Children> Render
|
||||
impl<Rndr, Loc, FallbackFn, Fallback, Children> Render<Rndr>
|
||||
for Router<Rndr, Loc, Children, FallbackFn>
|
||||
where
|
||||
Loc: Location,
|
||||
FallbackFn: Fn() -> Fallback + 'static,
|
||||
Fallback: Render,
|
||||
Children: MatchNestedRoutes + 'static,
|
||||
Fallback: Render<Rndr>,
|
||||
Children: MatchNestedRoutes<Rndr> + 'static,
|
||||
Fallback::State: 'static,
|
||||
Rndr: Renderer + 'static,
|
||||
Children::Match: std::fmt::Debug,
|
||||
<Children::Match as MatchInterface>::Child: std::fmt::Debug,
|
||||
<Children::Match as MatchInterface<Rndr>>::Child: std::fmt::Debug,
|
||||
{
|
||||
type State = RenderEffect<
|
||||
EitherState<
|
||||
<NestedRouteView<Children::Match> as Render>::State,
|
||||
<Fallback as Render>::State,
|
||||
<NestedRouteView<Children::Match, Rndr> as Render<Rndr>>::State,
|
||||
<Fallback as Render<Rndr>>::State,
|
||||
Rndr,
|
||||
>,
|
||||
>;
|
||||
@@ -158,7 +158,7 @@ where
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Either::<NestedRouteView<Children::Match>, _>::Right(
|
||||
Either::<NestedRouteView<Children::Match, Rndr>, _>::Right(
|
||||
(self.fallback)(),
|
||||
)
|
||||
.rebuild(&mut prev);
|
||||
@@ -180,21 +180,21 @@ where
|
||||
fn rebuild(self, state: &mut Self::State) {}
|
||||
}
|
||||
|
||||
impl<Rndr, Loc, FallbackFn, Fallback, Children> RenderHtml
|
||||
impl<Rndr, Loc, FallbackFn, Fallback, Children> RenderHtml<Rndr>
|
||||
for Router<Rndr, Loc, Children, FallbackFn>
|
||||
where
|
||||
Loc: Location + Send,
|
||||
FallbackFn: Fn() -> Fallback + Send + 'static,
|
||||
Fallback: RenderHtml,
|
||||
Children: MatchNestedRoutes + Send + 'static,
|
||||
Children::View: RenderHtml,
|
||||
/*View: Render + IntoAny + 'static,
|
||||
Fallback: RenderHtml<Rndr>,
|
||||
Children: MatchNestedRoutes<Rndr> + Send + 'static,
|
||||
Children::View: RenderHtml<Rndr>,
|
||||
/*View: Render<Rndr> + IntoAny<Rndr> + 'static,
|
||||
View::State: 'static,*/
|
||||
Fallback: RenderHtml,
|
||||
Fallback: RenderHtml<Rndr>,
|
||||
Fallback::State: 'static,
|
||||
Rndr: Renderer + 'static,
|
||||
Children::Match: std::fmt::Debug,
|
||||
<Children::Match as MatchInterface>::Child: std::fmt::Debug,
|
||||
<Children::Match as MatchInterface<Rndr>>::Child: std::fmt::Debug,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
@@ -294,7 +294,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let location = Loc::new().unwrap(); // TODO
|
||||
@@ -332,7 +332,7 @@ where
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Either::<NestedRouteView<Children::Match>, _>::Right(
|
||||
Either::<NestedRouteView<Children::Match, Rndr>, _>::Right(
|
||||
(self.fallback)(),
|
||||
)
|
||||
.rebuild(&mut prev);
|
||||
@@ -359,20 +359,20 @@ where
|
||||
|
||||
pub struct NestedRouteView<Matcher, R>
|
||||
where
|
||||
Matcher: MatchInterface,
|
||||
|
||||
Matcher: MatchInterface<R>,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
id: RouteMatchId,
|
||||
owner: Owner,
|
||||
params: ArcRwSignal<Params>,
|
||||
outlets: VecDeque<Outlet>,
|
||||
outlets: VecDeque<Outlet<R>>,
|
||||
view: Matcher::View,
|
||||
ty: PhantomData<(Matcher, R)>,
|
||||
}
|
||||
|
||||
impl<Matcher> NestedRouteView<Matcher>
|
||||
impl<Matcher, Rndr> NestedRouteView<Matcher, Rndr>
|
||||
where
|
||||
Matcher: MatchInterface + MatchParams,
|
||||
Matcher: MatchInterface<Rndr> + MatchParams,
|
||||
Matcher::Child: 'static,
|
||||
Matcher::View: 'static,
|
||||
Rndr: Renderer + 'static,
|
||||
@@ -414,7 +414,7 @@ where
|
||||
pub fn new_hydrate(
|
||||
outer_owner: &Owner,
|
||||
route_match: Matcher,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self {
|
||||
// keep track of all outlets, for diffing
|
||||
@@ -459,26 +459,26 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NestedRouteState<Matcher>
|
||||
pub struct NestedRouteState<Matcher, Rndr>
|
||||
where
|
||||
Matcher: MatchInterface,
|
||||
Matcher: MatchInterface<Rndr>,
|
||||
Rndr: Renderer + 'static,
|
||||
{
|
||||
id: RouteMatchId,
|
||||
owner: Owner,
|
||||
params: ArcRwSignal<Params>,
|
||||
view: <Matcher::View as Render>::State,
|
||||
outlets: VecDeque<Outlet>,
|
||||
view: <Matcher::View as Render<Rndr>>::State,
|
||||
outlets: VecDeque<Outlet<Rndr>>,
|
||||
}
|
||||
|
||||
fn get_inner_view<Match, R>(
|
||||
outlets: &mut VecDeque<Outlet>,
|
||||
outlets: &mut VecDeque<Outlet<R>>,
|
||||
parent: &Owner,
|
||||
route_match: Match,
|
||||
) -> Outlet
|
||||
) -> Outlet<R>
|
||||
where
|
||||
Match: MatchInterface + MatchParams,
|
||||
|
||||
Match: MatchInterface<R> + MatchParams,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
let owner = parent.child();
|
||||
let id = route_match.as_id();
|
||||
@@ -503,7 +503,7 @@ where
|
||||
})
|
||||
.into_any(),
|
||||
))
|
||||
}) as Box<dyn FnOnce() -> RwLock<Option<AnyView>>>
|
||||
}) as Box<dyn FnOnce() -> RwLock<Option<AnyView<R>>>>
|
||||
}));
|
||||
let inner = Arc::new(RwLock::new(OutletStateInner {
|
||||
html_len: {
|
||||
@@ -525,15 +525,15 @@ where
|
||||
}
|
||||
|
||||
fn get_inner_view_hydrate<Match, R>(
|
||||
outlets: &mut VecDeque<Outlet>,
|
||||
outlets: &mut VecDeque<Outlet<R>>,
|
||||
parent: &Owner,
|
||||
route_match: Match,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Outlet
|
||||
) -> Outlet<R>
|
||||
where
|
||||
Match: MatchInterface + MatchParams,
|
||||
|
||||
Match: MatchInterface<R> + MatchParams,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
let owner = parent.child();
|
||||
let id = route_match.as_id();
|
||||
@@ -558,7 +558,7 @@ where
|
||||
})
|
||||
.into_any(),
|
||||
))
|
||||
}) as Box<dyn FnOnce() -> RwLock<Option<AnyView>>>
|
||||
}) as Box<dyn FnOnce() -> RwLock<Option<AnyView<R>>>>
|
||||
}));
|
||||
let inner = Arc::new(RwLock::new(OutletStateInner {
|
||||
html_len: Box::new({
|
||||
@@ -585,18 +585,18 @@ where
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Outlet
|
||||
pub struct Outlet<R>
|
||||
where
|
||||
R: Renderer + Send + 'static,
|
||||
{
|
||||
id: RouteMatchId,
|
||||
owner: Owner,
|
||||
params: ArcRwSignal<Params>,
|
||||
rndr: PhantomData,
|
||||
inner: OutletInner,
|
||||
rndr: PhantomData<R>,
|
||||
inner: OutletInner<R>,
|
||||
}
|
||||
|
||||
pub enum OutletInner where R: Renderer + 'static {
|
||||
pub enum OutletInner<R> where R: Renderer + 'static {
|
||||
Server {
|
||||
html_len: Box<dyn Fn() -> usize + Send + Sync>,
|
||||
view: Box<
|
||||
@@ -608,30 +608,30 @@ pub enum OutletInner where R: Renderer + 'static {
|
||||
html_len: Box<dyn Fn() -> usize + Send + Sync>,
|
||||
view: Arc<
|
||||
Lazy<
|
||||
RwLock<Option<AnyView>>,
|
||||
Box<dyn FnOnce() -> RwLock<Option<AnyView>> + Send + Sync>,
|
||||
RwLock<Option<AnyView<R>>>,
|
||||
Box<dyn FnOnce() -> RwLock<Option<AnyView<R>>> + Send + Sync>,
|
||||
>,
|
||||
>,
|
||||
state: Lazy<
|
||||
SendWrapper<AnyViewState>,
|
||||
Box<dyn FnOnce() -> SendWrapper<AnyViewState> + Send + Sync>,
|
||||
SendWrapper<AnyViewState<R>>,
|
||||
Box<dyn FnOnce() -> SendWrapper<AnyViewState<R>> + Send + Sync>,
|
||||
>,
|
||||
*/
|
||||
|
||||
impl<R: Renderer> Debug for OutletInner {
|
||||
impl<R: Renderer> Debug for OutletInner<R> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("OutletInner").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for OutletInner
|
||||
impl<R> Default for OutletInner<R>
|
||||
where
|
||||
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
fn default() -> Self {
|
||||
let view =
|
||||
Arc::new(Lazy::new(Box::new(|| RwLock::new(Some(().into_any())))
|
||||
as Box<dyn FnOnce() -> RwLock<Option<AnyView>>>));
|
||||
as Box<dyn FnOnce() -> RwLock<Option<AnyView<R>>>>));
|
||||
Self {
|
||||
html_len: Box::new(|| 0),
|
||||
view,
|
||||
@@ -640,9 +640,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Outlet
|
||||
impl<R> Clone for Outlet<R>
|
||||
where
|
||||
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
@@ -655,9 +655,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Outlet
|
||||
impl<R> Default for Outlet<R>
|
||||
where
|
||||
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
@@ -669,11 +669,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for Outlet
|
||||
impl<R> Render<R> for Outlet<R>
|
||||
where
|
||||
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type State = Outlet;
|
||||
type State = Outlet<R>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
self
|
||||
@@ -684,9 +684,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderHtml for Outlet
|
||||
impl<R> RenderHtml<R> for Outlet<R>
|
||||
where
|
||||
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
@@ -725,7 +725,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
todo!()
|
||||
@@ -743,38 +743,38 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/*pub struct OutletStateInner
|
||||
/*pub struct OutletStateInner<R>
|
||||
where
|
||||
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
html_len: Box<dyn Fn() -> usize + Send + Sync>,
|
||||
view: Arc<
|
||||
Lazy<
|
||||
RwLock<Option<AnyView>>,
|
||||
Box<dyn FnOnce() -> RwLock<Option<AnyView>> + Send + Sync>,
|
||||
RwLock<Option<AnyView<R>>>,
|
||||
Box<dyn FnOnce() -> RwLock<Option<AnyView<R>>> + Send + Sync>,
|
||||
>,
|
||||
>,
|
||||
state: Lazy<
|
||||
SendWrapper<AnyViewState>,
|
||||
Box<dyn FnOnce() -> SendWrapper<AnyViewState> + Send + Sync>,
|
||||
SendWrapper<AnyViewState<R>>,
|
||||
Box<dyn FnOnce() -> SendWrapper<AnyViewState<R>> + Send + Sync>,
|
||||
>,
|
||||
}
|
||||
|
||||
|
||||
impl<R: Renderer> Debug for OutletStateInner {
|
||||
impl<R: Renderer> Debug for OutletStateInner<R> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("OutletStateInner").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for OutletStateInner
|
||||
impl<R> Default for OutletStateInner<R>
|
||||
where
|
||||
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
fn default() -> Self {
|
||||
let view =
|
||||
Arc::new(Lazy::new(Box::new(|| RwLock::new(Some(().into_any())))
|
||||
as Box<dyn FnOnce() -> RwLock<Option<AnyView>>>));
|
||||
as Box<dyn FnOnce() -> RwLock<Option<AnyView<R>>>>));
|
||||
Self {
|
||||
html_len: Box::new(|| 0),
|
||||
view,
|
||||
@@ -784,9 +784,9 @@ where
|
||||
}
|
||||
*/
|
||||
|
||||
impl Mountable for Outlet
|
||||
impl<R> Mountable<R> for Outlet<R>
|
||||
where
|
||||
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
todo!()
|
||||
@@ -802,7 +802,7 @@ where
|
||||
}
|
||||
|
||||
fn insert_before_this(&self,
|
||||
child: &mut dyn Mountable,
|
||||
child: &mut dyn Mountable<R>,
|
||||
) -> bool {
|
||||
/*self.inner
|
||||
.write()
|
||||
@@ -818,8 +818,8 @@ fn rebuild_nested<Match, R>(
|
||||
prev: &mut NestedRouteState<Match, R>,
|
||||
new_match: Match,
|
||||
) where
|
||||
Match: MatchInterface + MatchParams + std::fmt::Debug,
|
||||
|
||||
Match: MatchInterface<R> + MatchParams + std::fmt::Debug,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
let mut items = 0;
|
||||
let NestedRouteState {
|
||||
@@ -846,11 +846,11 @@ fn rebuild_nested<Match, R>(
|
||||
|
||||
fn rebuild_inner<Match, R>(
|
||||
items: &mut usize,
|
||||
outlets: &mut VecDeque<Outlet>,
|
||||
outlets: &mut VecDeque<Outlet<R>>,
|
||||
route_match: Match,
|
||||
) where
|
||||
Match: MatchInterface + MatchParams,
|
||||
|
||||
Match: MatchInterface<R> + MatchParams,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
*items += 1;
|
||||
|
||||
@@ -910,11 +910,11 @@ fn rebuild_inner<Match, R>(
|
||||
}
|
||||
}
|
||||
|
||||
impl<Matcher, R> Render for NestedRouteView<Matcher, R>
|
||||
impl<Matcher, R> Render<R> for NestedRouteView<Matcher, R>
|
||||
where
|
||||
Matcher: MatchInterface,
|
||||
Matcher: MatchInterface<R>,
|
||||
Matcher::View: Sized + 'static,
|
||||
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type State = NestedRouteState<Matcher, R>;
|
||||
|
||||
@@ -953,11 +953,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Matcher, R> RenderHtml for NestedRouteView<Matcher, R>
|
||||
impl<Matcher, R> RenderHtml<R> for NestedRouteView<Matcher, R>
|
||||
where
|
||||
Matcher: MatchInterface + Send,
|
||||
Matcher: MatchInterface<R> + Send,
|
||||
Matcher::View: Sized + 'static,
|
||||
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
@@ -988,7 +988,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let NestedRouteView {
|
||||
@@ -1009,10 +1009,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Matcher, R> Mountable for NestedRouteState<Matcher, R>
|
||||
impl<Matcher, R> Mountable<R> for NestedRouteState<Matcher, R>
|
||||
where
|
||||
Matcher: MatchInterface,
|
||||
|
||||
Matcher: MatchInterface<R>,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
self.view.unmount();
|
||||
@@ -1023,43 +1023,43 @@ where
|
||||
}
|
||||
|
||||
fn insert_before_this(&self,
|
||||
child: &mut dyn Mountable,
|
||||
child: &mut dyn Mountable<R>,
|
||||
) -> bool {
|
||||
self.view.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Rndr, Loc, FallbackFn, Fallback, Children, View> AddAnyAttr
|
||||
impl<Rndr, Loc, FallbackFn, Fallback, Children, View> AddAnyAttr<Rndr>
|
||||
for Router<Rndr, Loc, Children, FallbackFn>
|
||||
where
|
||||
Loc: Location,
|
||||
FallbackFn: Fn() -> Fallback,
|
||||
Fallback: Render,
|
||||
Children: MatchNestedRoutes,
|
||||
<<Children as MatchNestedRoutes>::Match as MatchInterface<
|
||||
Fallback: Render<Rndr>,
|
||||
Children: MatchNestedRoutes<Rndr>,
|
||||
<<Children as MatchNestedRoutes<Rndr>>::Match as MatchInterface<
|
||||
Rndr,
|
||||
>>::View: ChooseView<Rndr, Output = View>,
|
||||
Rndr: Renderer + 'static,
|
||||
Router<Rndr, Loc, Children, FallbackFn>: RenderHtml,
|
||||
Router<Rndr, Loc, Children, FallbackFn>: RenderHtml<Rndr>,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> = Self;
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> = Self;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
self
|
||||
}
|
||||
|
||||
fn add_any_attr_by_ref<NewAttr: Attribute>(
|
||||
fn add_any_attr_by_ref<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: &NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
self
|
||||
}
|
||||
@@ -1069,7 +1069,7 @@ where
|
||||
pub struct FlatRouter<Rndr, Loc, Children, FallbackFn> {
|
||||
base: Option<Cow<'static, str>>,
|
||||
location: PhantomData<Loc>,
|
||||
pub routes: Routes<Children>,
|
||||
pub routes: Routes<Children, Rndr>,
|
||||
fallback: FallbackFn,
|
||||
}
|
||||
|
||||
@@ -1081,7 +1081,7 @@ where
|
||||
FallbackFn: Fn() -> Fallback,
|
||||
{
|
||||
pub fn new(
|
||||
routes: Routes<Children>,
|
||||
routes: Routes<Children, Rndr>,
|
||||
fallback: FallbackFn,
|
||||
) -> FlatRouter<Rndr, Loc, Children, FallbackFn> {
|
||||
Self {
|
||||
@@ -1094,7 +1094,7 @@ where
|
||||
|
||||
pub fn new_with_base(
|
||||
base: impl Into<Cow<'static, str>>,
|
||||
routes: Routes<Children>,
|
||||
routes: Routes<Children, Rndr>,
|
||||
fallback: FallbackFn,
|
||||
) -> FlatRouter<Rndr, Loc, Children, FallbackFn> {
|
||||
Self {
|
||||
@@ -1105,23 +1105,23 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<Rndr, Loc, FallbackFn, Fallback, Children> Render
|
||||
impl<Rndr, Loc, FallbackFn, Fallback, Children> Render<Rndr>
|
||||
for FlatRouter<Rndr, Loc, Children, FallbackFn>
|
||||
where
|
||||
Loc: Location,
|
||||
FallbackFn: Fn() -> Fallback + 'static,
|
||||
Fallback: Render,
|
||||
Children: MatchNestedRoutes + 'static,
|
||||
Fallback: Render<Rndr>,
|
||||
Children: MatchNestedRoutes<Rndr> + 'static,
|
||||
Fallback::State: 'static,
|
||||
Rndr: Renderer + 'static,
|
||||
{
|
||||
type State =
|
||||
RenderEffect<
|
||||
EitherState<
|
||||
<<Children::Match as MatchInterface>::View as Render<
|
||||
<<Children::Match as MatchInterface<Rndr>>::View as Render<
|
||||
Rndr,
|
||||
>>::State,
|
||||
<Fallback as Render>::State,
|
||||
<Fallback as Render<Rndr>>::State,
|
||||
Rndr,
|
||||
>,
|
||||
>;
|
||||
@@ -1170,7 +1170,7 @@ where
|
||||
let view = outer_owner.with(|| view.choose());
|
||||
Either::Left::<_, Fallback>(view).rebuild(&mut prev);
|
||||
} else {
|
||||
Either::<<Children::Match as MatchInterface>::View, _>::Right((self.fallback)()).rebuild(&mut prev);
|
||||
Either::<<Children::Match as MatchInterface<Rndr>>::View, _>::Right((self.fallback)()).rebuild(&mut prev);
|
||||
}
|
||||
prev
|
||||
} else {
|
||||
@@ -1209,20 +1209,20 @@ where
|
||||
fn rebuild(self, state: &mut Self::State) {}
|
||||
}
|
||||
|
||||
impl<Rndr, Loc, FallbackFn, Fallback, Children> RenderHtml
|
||||
impl<Rndr, Loc, FallbackFn, Fallback, Children> RenderHtml<Rndr>
|
||||
for FlatRouter<Rndr, Loc, Children, FallbackFn>
|
||||
where
|
||||
Loc: Location + Send,
|
||||
FallbackFn: Fn() -> Fallback + Send + 'static,
|
||||
Fallback: RenderHtml,
|
||||
Children: MatchNestedRoutes + Send + 'static,
|
||||
Fallback: RenderHtml<Rndr>,
|
||||
Children: MatchNestedRoutes<Rndr> + Send + 'static,
|
||||
Fallback::State: 'static,
|
||||
Rndr: Renderer + 'static,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
const MIN_LENGTH: usize =
|
||||
<Children::Match as MatchInterface>::View::MIN_LENGTH;
|
||||
<Children::Match as MatchInterface<Rndr>>::View::MIN_LENGTH;
|
||||
|
||||
async fn resolve(self) -> Self::AsyncOutput {
|
||||
self
|
||||
@@ -1357,7 +1357,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let location = Loc::new().unwrap(); // TODO
|
||||
@@ -1405,7 +1405,7 @@ where
|
||||
let view = outer_owner.with(|| view.choose());
|
||||
Either::Left::<_, Fallback>(view).rebuild(&mut prev);
|
||||
} else {
|
||||
Either::<<Children::Match as MatchInterface>::View, _>::Right((self.fallback)()).rebuild(&mut prev);
|
||||
Either::<<Children::Match as MatchInterface<Rndr>>::View, _>::Right((self.fallback)()).rebuild(&mut prev);
|
||||
}
|
||||
prev
|
||||
} else {
|
||||
|
||||
@@ -1,68 +1,78 @@
|
||||
use super::{Attribute, NextAttribute};
|
||||
use crate::renderer::Renderer;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
fmt::Debug,
|
||||
marker::PhantomData,
|
||||
};
|
||||
#[cfg(feature = "ssr")]
|
||||
use std::{future::Future, pin::Pin};
|
||||
|
||||
/// A type-erased container for any [`Attribute`].
|
||||
pub struct AnyAttribute {
|
||||
pub struct AnyAttribute<R: Renderer> {
|
||||
type_id: TypeId,
|
||||
html_len: usize,
|
||||
value: Box<dyn Any + Send>,
|
||||
#[cfg(feature = "ssr")]
|
||||
to_html:
|
||||
fn(Box<dyn Any>, &mut String, &mut String, &mut String, &mut String),
|
||||
build: fn(
|
||||
Box<dyn Any>,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> AnyAttributeState,
|
||||
rebuild: fn(TypeId, Box<dyn Any>, &mut AnyAttributeState),
|
||||
build: fn(Box<dyn Any>, el: &R::Element) -> AnyAttributeState<R>,
|
||||
rebuild: fn(TypeId, Box<dyn Any>, &mut AnyAttributeState<R>),
|
||||
#[cfg(feature = "hydrate")]
|
||||
hydrate_from_server:
|
||||
fn(Box<dyn Any>, &crate::renderer::types::Element) -> AnyAttributeState,
|
||||
hydrate_from_server: fn(Box<dyn Any>, &R::Element) -> AnyAttributeState<R>,
|
||||
#[cfg(feature = "hydrate")]
|
||||
hydrate_from_template:
|
||||
fn(Box<dyn Any>, &crate::renderer::types::Element) -> AnyAttributeState,
|
||||
fn(Box<dyn Any>, &R::Element) -> AnyAttributeState<R>,
|
||||
#[cfg(feature = "ssr")]
|
||||
#[allow(clippy::type_complexity)]
|
||||
resolve:
|
||||
fn(Box<dyn Any>) -> Pin<Box<dyn Future<Output = AnyAttribute> + Send>>,
|
||||
resolve: fn(
|
||||
Box<dyn Any>,
|
||||
) -> Pin<Box<dyn Future<Output = AnyAttribute<R>> + Send>>,
|
||||
#[cfg(feature = "ssr")]
|
||||
dry_resolve: fn(&mut Box<dyn Any + Send>),
|
||||
}
|
||||
|
||||
impl Debug for AnyAttribute {
|
||||
impl<R> Debug for AnyAttribute<R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("AnyAttribute").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
/// View state for [`AnyAttribute`].
|
||||
pub struct AnyAttributeState {
|
||||
pub struct AnyAttributeState<R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type_id: TypeId,
|
||||
state: Box<dyn Any>,
|
||||
el: crate::renderer::types::Element,
|
||||
el: R::Element,
|
||||
rndr: PhantomData<R>,
|
||||
}
|
||||
|
||||
/// Converts an [`Attribute`] into [`AnyAttribute`].
|
||||
pub trait IntoAnyAttribute {
|
||||
pub trait IntoAnyAttribute<R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
/// Wraps the given attribute.
|
||||
fn into_any_attr(self) -> AnyAttribute;
|
||||
fn into_any_attr(self) -> AnyAttribute<R>;
|
||||
}
|
||||
|
||||
impl<T> IntoAnyAttribute for T
|
||||
impl<T, R> IntoAnyAttribute<R> for T
|
||||
where
|
||||
Self: Send,
|
||||
T: Attribute + 'static,
|
||||
T: Attribute<R> + 'static,
|
||||
T::State: 'static,
|
||||
crate::renderer::types::Element: Clone,
|
||||
R: Renderer + 'static,
|
||||
R::Element: Clone,
|
||||
{
|
||||
// inlining allows the compiler to remove the unused functions
|
||||
// i.e., doesn't ship HTML-generating code that isn't used
|
||||
#[inline(always)]
|
||||
fn into_any_attr(self) -> AnyAttribute {
|
||||
fn into_any_attr(self) -> AnyAttribute<R> {
|
||||
let html_len = self.html_len();
|
||||
|
||||
let value = Box::new(self) as Box<dyn Any + Send>;
|
||||
@@ -78,8 +88,7 @@ where
|
||||
.expect("AnyAttribute::to_html could not be downcast");
|
||||
value.to_html(buf, class, style, inner_html);
|
||||
};
|
||||
let build = |value: Box<dyn Any>,
|
||||
el: &crate::renderer::types::Element| {
|
||||
let build = |value: Box<dyn Any>, el: &R::Element| {
|
||||
let value = value
|
||||
.downcast::<T>()
|
||||
.expect("AnyAttribute::build couldn't downcast");
|
||||
@@ -89,39 +98,40 @@ where
|
||||
type_id: TypeId::of::<T>(),
|
||||
state,
|
||||
el: el.clone(),
|
||||
rndr: PhantomData,
|
||||
}
|
||||
};
|
||||
#[cfg(feature = "hydrate")]
|
||||
let hydrate_from_server =
|
||||
|value: Box<dyn Any>, el: &crate::renderer::types::Element| {
|
||||
let value = value.downcast::<T>().expect(
|
||||
"AnyAttribute::hydrate_from_server couldn't downcast",
|
||||
);
|
||||
let state = Box::new(value.hydrate::<true>(el));
|
||||
let hydrate_from_server = |value: Box<dyn Any>, el: &R::Element| {
|
||||
let value = value
|
||||
.downcast::<T>()
|
||||
.expect("AnyAttribute::hydrate_from_server couldn't downcast");
|
||||
let state = Box::new(value.hydrate::<true>(el));
|
||||
|
||||
AnyAttributeState {
|
||||
type_id: TypeId::of::<T>(),
|
||||
state,
|
||||
el: el.clone(),
|
||||
}
|
||||
};
|
||||
AnyAttributeState {
|
||||
type_id: TypeId::of::<T>(),
|
||||
state,
|
||||
el: el.clone(),
|
||||
rndr: PhantomData,
|
||||
}
|
||||
};
|
||||
#[cfg(feature = "hydrate")]
|
||||
let hydrate_from_template =
|
||||
|value: Box<dyn Any>, el: &crate::renderer::types::Element| {
|
||||
let value = value.downcast::<T>().expect(
|
||||
"AnyAttribute::hydrate_from_server couldn't downcast",
|
||||
);
|
||||
let state = Box::new(value.hydrate::<true>(el));
|
||||
let hydrate_from_template = |value: Box<dyn Any>, el: &R::Element| {
|
||||
let value = value
|
||||
.downcast::<T>()
|
||||
.expect("AnyAttribute::hydrate_from_server couldn't downcast");
|
||||
let state = Box::new(value.hydrate::<true>(el));
|
||||
|
||||
AnyAttributeState {
|
||||
type_id: TypeId::of::<T>(),
|
||||
state,
|
||||
el: el.clone(),
|
||||
}
|
||||
};
|
||||
AnyAttributeState {
|
||||
type_id: TypeId::of::<T>(),
|
||||
state,
|
||||
el: el.clone(),
|
||||
rndr: PhantomData,
|
||||
}
|
||||
};
|
||||
let rebuild = |new_type_id: TypeId,
|
||||
value: Box<dyn Any>,
|
||||
state: &mut AnyAttributeState| {
|
||||
state: &mut AnyAttributeState<R>| {
|
||||
let value = value
|
||||
.downcast::<T>()
|
||||
.expect("AnyAttribute::rebuild couldn't downcast value");
|
||||
@@ -150,7 +160,7 @@ where
|
||||
.downcast::<T>()
|
||||
.expect("AnyView::resolve could not be downcast");
|
||||
Box::pin(async move { value.resolve().await.into_any_attr() })
|
||||
as Pin<Box<dyn Future<Output = AnyAttribute> + Send>>
|
||||
as Pin<Box<dyn Future<Output = AnyAttribute<R>> + Send>>
|
||||
};
|
||||
AnyAttribute {
|
||||
type_id: TypeId::of::<T>(),
|
||||
@@ -172,10 +182,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl NextAttribute for AnyAttribute {
|
||||
type Output<NewAttr: Attribute> = (Self, NewAttr);
|
||||
impl<R> NextAttribute<R> for AnyAttribute<R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type Output<NewAttr: Attribute<R>> = (Self, NewAttr);
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
new_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr> {
|
||||
@@ -183,11 +196,14 @@ impl NextAttribute for AnyAttribute {
|
||||
}
|
||||
}
|
||||
|
||||
impl Attribute for AnyAttribute {
|
||||
impl<R> Attribute<R> for AnyAttribute<R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
const MIN_LENGTH: usize = 0;
|
||||
|
||||
type AsyncOutput = AnyAttribute;
|
||||
type State = AnyAttributeState;
|
||||
type AsyncOutput = AnyAttribute<R>;
|
||||
type State = AnyAttributeState<R>;
|
||||
type Cloneable = ();
|
||||
type CloneableOwned = ();
|
||||
|
||||
@@ -216,7 +232,7 @@ impl Attribute for AnyAttribute {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
#[cfg(feature = "hydrate")]
|
||||
if FROM_SERVER {
|
||||
@@ -234,7 +250,7 @@ impl Attribute for AnyAttribute {
|
||||
}
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &<R as Renderer>::Element) -> Self::State {
|
||||
(self.build)(self.value, el)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
use crate::{
|
||||
html::{
|
||||
attribute::{Attr, *},
|
||||
element::{ElementType, HtmlElement},
|
||||
element::{CreateElement, ElementType, HtmlElement},
|
||||
},
|
||||
renderer::Rndr,
|
||||
view::{add_attr::AddAnyAttr, RenderHtml},
|
||||
};
|
||||
|
||||
/// Applies ARIA attributes to an HTML element.
|
||||
pub trait AriaAttributes<Rndr, V>
|
||||
where
|
||||
Self: Sized + AddAnyAttr,
|
||||
V: AttributeValue,
|
||||
Self: Sized + AddAnyAttr<Rndr>,
|
||||
V: AttributeValue<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
/// Identifies the currently active descendant of a composite widget.
|
||||
fn aria_activedescendant(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaActivedescendant, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaActivedescendant, V, Rndr>>
|
||||
{
|
||||
self.add_any_attr(aria_activedescendant(value))
|
||||
}
|
||||
|
||||
@@ -25,7 +26,7 @@ where
|
||||
fn aria_atomic(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaAtomic, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaAtomic, V, Rndr>> {
|
||||
self.add_any_attr(aria_atomic(value))
|
||||
}
|
||||
|
||||
@@ -33,7 +34,8 @@ where
|
||||
fn aria_autocomplete(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaAutocomplete, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaAutocomplete, V, Rndr>>
|
||||
{
|
||||
self.add_any_attr(aria_autocomplete(value))
|
||||
}
|
||||
|
||||
@@ -41,7 +43,7 @@ where
|
||||
fn aria_busy(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaBusy, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaBusy, V, Rndr>> {
|
||||
self.add_any_attr(aria_busy(value))
|
||||
}
|
||||
|
||||
@@ -49,7 +51,7 @@ where
|
||||
fn aria_checked(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaChecked, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaChecked, V, Rndr>> {
|
||||
self.add_any_attr(aria_checked(value))
|
||||
}
|
||||
|
||||
@@ -57,7 +59,7 @@ where
|
||||
fn aria_colcount(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaColcount, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaColcount, V, Rndr>> {
|
||||
self.add_any_attr(aria_colcount(value))
|
||||
}
|
||||
|
||||
@@ -65,7 +67,7 @@ where
|
||||
fn aria_colindex(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaColindex, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaColindex, V, Rndr>> {
|
||||
self.add_any_attr(aria_colindex(value))
|
||||
}
|
||||
|
||||
@@ -73,7 +75,7 @@ where
|
||||
fn aria_colspan(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaColspan, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaColspan, V, Rndr>> {
|
||||
self.add_any_attr(aria_colspan(value))
|
||||
}
|
||||
|
||||
@@ -81,7 +83,7 @@ where
|
||||
fn aria_controls(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaControls, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaControls, V, Rndr>> {
|
||||
self.add_any_attr(aria_controls(value))
|
||||
}
|
||||
|
||||
@@ -89,7 +91,7 @@ where
|
||||
fn aria_current(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaCurrent, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaCurrent, V, Rndr>> {
|
||||
self.add_any_attr(aria_current(value))
|
||||
}
|
||||
|
||||
@@ -97,7 +99,8 @@ where
|
||||
fn aria_describedby(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaDescribedby, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaDescribedby, V, Rndr>>
|
||||
{
|
||||
self.add_any_attr(aria_describedby(value))
|
||||
}
|
||||
|
||||
@@ -105,7 +108,8 @@ where
|
||||
fn aria_description(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaDescription, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaDescription, V, Rndr>>
|
||||
{
|
||||
self.add_any_attr(aria_description(value))
|
||||
}
|
||||
|
||||
@@ -113,7 +117,7 @@ where
|
||||
fn aria_details(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaDetails, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaDetails, V, Rndr>> {
|
||||
self.add_any_attr(aria_details(value))
|
||||
}
|
||||
|
||||
@@ -121,7 +125,7 @@ where
|
||||
fn aria_disabled(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaDisabled, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaDisabled, V, Rndr>> {
|
||||
self.add_any_attr(aria_disabled(value))
|
||||
}
|
||||
|
||||
@@ -129,7 +133,7 @@ where
|
||||
fn aria_dropeffect(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaDropeffect, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaDropeffect, V, Rndr>> {
|
||||
self.add_any_attr(aria_dropeffect(value))
|
||||
}
|
||||
|
||||
@@ -137,7 +141,8 @@ where
|
||||
fn aria_errormessage(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaErrormessage, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaErrormessage, V, Rndr>>
|
||||
{
|
||||
self.add_any_attr(aria_errormessage(value))
|
||||
}
|
||||
|
||||
@@ -145,7 +150,7 @@ where
|
||||
fn aria_expanded(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaExpanded, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaExpanded, V, Rndr>> {
|
||||
self.add_any_attr(aria_expanded(value))
|
||||
}
|
||||
|
||||
@@ -153,7 +158,7 @@ where
|
||||
fn aria_flowto(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaFlowto, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaFlowto, V, Rndr>> {
|
||||
self.add_any_attr(aria_flowto(value))
|
||||
}
|
||||
|
||||
@@ -161,7 +166,7 @@ where
|
||||
fn aria_grabbed(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaGrabbed, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaGrabbed, V, Rndr>> {
|
||||
self.add_any_attr(aria_grabbed(value))
|
||||
}
|
||||
|
||||
@@ -169,7 +174,7 @@ where
|
||||
fn aria_haspopup(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaHaspopup, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaHaspopup, V, Rndr>> {
|
||||
self.add_any_attr(aria_haspopup(value))
|
||||
}
|
||||
|
||||
@@ -177,7 +182,7 @@ where
|
||||
fn aria_hidden(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaHidden, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaHidden, V, Rndr>> {
|
||||
self.add_any_attr(aria_hidden(value))
|
||||
}
|
||||
|
||||
@@ -185,7 +190,7 @@ where
|
||||
fn aria_invalid(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaInvalid, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaInvalid, V, Rndr>> {
|
||||
self.add_any_attr(aria_invalid(value))
|
||||
}
|
||||
|
||||
@@ -193,7 +198,8 @@ where
|
||||
fn aria_keyshortcuts(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaKeyshortcuts, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaKeyshortcuts, V, Rndr>>
|
||||
{
|
||||
self.add_any_attr(aria_keyshortcuts(value))
|
||||
}
|
||||
|
||||
@@ -201,7 +207,7 @@ where
|
||||
fn aria_label(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaLabel, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaLabel, V, Rndr>> {
|
||||
self.add_any_attr(aria_label(value))
|
||||
}
|
||||
|
||||
@@ -209,7 +215,7 @@ where
|
||||
fn aria_labelledby(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaLabelledby, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaLabelledby, V, Rndr>> {
|
||||
self.add_any_attr(aria_labelledby(value))
|
||||
}
|
||||
|
||||
@@ -217,7 +223,7 @@ where
|
||||
fn aria_live(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaLive, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaLive, V, Rndr>> {
|
||||
self.add_any_attr(aria_live(value))
|
||||
}
|
||||
|
||||
@@ -225,7 +231,7 @@ where
|
||||
fn aria_modal(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaModal, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaModal, V, Rndr>> {
|
||||
self.add_any_attr(aria_modal(value))
|
||||
}
|
||||
|
||||
@@ -233,7 +239,7 @@ where
|
||||
fn aria_multiline(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaMultiline, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaMultiline, V, Rndr>> {
|
||||
self.add_any_attr(aria_multiline(value))
|
||||
}
|
||||
|
||||
@@ -241,7 +247,8 @@ where
|
||||
fn aria_multiselectable(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaMultiselectable, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaMultiselectable, V, Rndr>>
|
||||
{
|
||||
self.add_any_attr(aria_multiselectable(value))
|
||||
}
|
||||
|
||||
@@ -249,7 +256,8 @@ where
|
||||
fn aria_orientation(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaOrientation, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaOrientation, V, Rndr>>
|
||||
{
|
||||
self.add_any_attr(aria_orientation(value))
|
||||
}
|
||||
|
||||
@@ -257,7 +265,7 @@ where
|
||||
fn aria_owns(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaOwns, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaOwns, V, Rndr>> {
|
||||
self.add_any_attr(aria_owns(value))
|
||||
}
|
||||
|
||||
@@ -265,7 +273,8 @@ where
|
||||
fn aria_placeholder(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaPlaceholder, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaPlaceholder, V, Rndr>>
|
||||
{
|
||||
self.add_any_attr(aria_placeholder(value))
|
||||
}
|
||||
|
||||
@@ -273,7 +282,7 @@ where
|
||||
fn aria_posinset(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaPosinset, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaPosinset, V, Rndr>> {
|
||||
self.add_any_attr(aria_posinset(value))
|
||||
}
|
||||
|
||||
@@ -281,7 +290,7 @@ where
|
||||
fn aria_pressed(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaPressed, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaPressed, V, Rndr>> {
|
||||
self.add_any_attr(aria_pressed(value))
|
||||
}
|
||||
|
||||
@@ -289,7 +298,7 @@ where
|
||||
fn aria_readonly(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaReadonly, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaReadonly, V, Rndr>> {
|
||||
self.add_any_attr(aria_readonly(value))
|
||||
}
|
||||
|
||||
@@ -297,7 +306,7 @@ where
|
||||
fn aria_relevant(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaRelevant, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaRelevant, V, Rndr>> {
|
||||
self.add_any_attr(aria_relevant(value))
|
||||
}
|
||||
|
||||
@@ -305,7 +314,7 @@ where
|
||||
fn aria_required(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaRequired, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaRequired, V, Rndr>> {
|
||||
self.add_any_attr(aria_required(value))
|
||||
}
|
||||
|
||||
@@ -313,7 +322,8 @@ where
|
||||
fn aria_roledescription(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaRoledescription, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaRoledescription, V, Rndr>>
|
||||
{
|
||||
self.add_any_attr(aria_roledescription(value))
|
||||
}
|
||||
|
||||
@@ -321,7 +331,7 @@ where
|
||||
fn aria_rowcount(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaRowcount, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaRowcount, V, Rndr>> {
|
||||
self.add_any_attr(aria_rowcount(value))
|
||||
}
|
||||
|
||||
@@ -329,7 +339,7 @@ where
|
||||
fn aria_rowindex(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaRowindex, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaRowindex, V, Rndr>> {
|
||||
self.add_any_attr(aria_rowindex(value))
|
||||
}
|
||||
|
||||
@@ -337,7 +347,7 @@ where
|
||||
fn aria_rowspan(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaRowspan, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaRowspan, V, Rndr>> {
|
||||
self.add_any_attr(aria_rowspan(value))
|
||||
}
|
||||
|
||||
@@ -345,7 +355,7 @@ where
|
||||
fn aria_selected(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaSelected, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaSelected, V, Rndr>> {
|
||||
self.add_any_attr(aria_selected(value))
|
||||
}
|
||||
|
||||
@@ -353,7 +363,7 @@ where
|
||||
fn aria_setsize(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaSetsize, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaSetsize, V, Rndr>> {
|
||||
self.add_any_attr(aria_setsize(value))
|
||||
}
|
||||
|
||||
@@ -361,7 +371,7 @@ where
|
||||
fn aria_sort(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaSort, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaSort, V, Rndr>> {
|
||||
self.add_any_attr(aria_sort(value))
|
||||
}
|
||||
|
||||
@@ -369,7 +379,7 @@ where
|
||||
fn aria_valuemax(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaValuemax, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaValuemax, V, Rndr>> {
|
||||
self.add_any_attr(aria_valuemax(value))
|
||||
}
|
||||
|
||||
@@ -377,7 +387,7 @@ where
|
||||
fn aria_valuemin(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaValuemin, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaValuemin, V, Rndr>> {
|
||||
self.add_any_attr(aria_valuemin(value))
|
||||
}
|
||||
|
||||
@@ -385,7 +395,7 @@ where
|
||||
fn aria_valuenow(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaValuenow, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaValuenow, V, Rndr>> {
|
||||
self.add_any_attr(aria_valuenow(value))
|
||||
}
|
||||
|
||||
@@ -393,16 +403,18 @@ where
|
||||
fn aria_valuetext(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<AriaValuetext, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<AriaValuetext, V, Rndr>> {
|
||||
self.add_any_attr(aria_valuetext(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<El, At, Ch, V> AriaAttributes<Rndr, V> for HtmlElement<El, At, Ch>
|
||||
impl<El, At, Ch, Rndr, V> AriaAttributes<Rndr, V>
|
||||
for HtmlElement<El, At, Ch, Rndr>
|
||||
where
|
||||
El: ElementType + Send,
|
||||
At: Attribute + Send,
|
||||
Ch: RenderHtml + Send,
|
||||
V: AttributeValue,
|
||||
El: ElementType + CreateElement<Rndr> + Send,
|
||||
At: Attribute<Rndr> + Send,
|
||||
Ch: RenderHtml<Rndr> + Send,
|
||||
V: AttributeValue<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,54 +1,65 @@
|
||||
use super::NextAttribute;
|
||||
use crate::{
|
||||
html::attribute::{Attribute, AttributeValue},
|
||||
renderer::DomRenderer,
|
||||
view::{add_attr::AddAnyAttr, Position, ToTemplate},
|
||||
};
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
use std::{borrow::Cow, marker::PhantomData, sync::Arc};
|
||||
|
||||
/// Adds a custom attribute with any key-value combintion.
|
||||
#[inline(always)]
|
||||
pub fn custom_attribute<K, V>(key: K, value: V) -> CustomAttr<K, V>
|
||||
pub fn custom_attribute<K, V, R>(key: K, value: V) -> CustomAttr<K, V, R>
|
||||
where
|
||||
K: CustomAttributeKey,
|
||||
V: AttributeValue,
|
||||
V: AttributeValue<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
CustomAttr { key, value }
|
||||
CustomAttr {
|
||||
key,
|
||||
value,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// A custom attribute with any key-value combination.
|
||||
#[derive(Debug)]
|
||||
pub struct CustomAttr<K, V>
|
||||
pub struct CustomAttr<K, V, R>
|
||||
where
|
||||
K: CustomAttributeKey,
|
||||
V: AttributeValue,
|
||||
V: AttributeValue<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
key: K,
|
||||
value: V,
|
||||
rndr: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<K, V> Clone for CustomAttr<K, V>
|
||||
impl<K, V, R> Clone for CustomAttr<K, V, R>
|
||||
where
|
||||
K: CustomAttributeKey,
|
||||
V: AttributeValue + Clone,
|
||||
V: AttributeValue<R> + Clone,
|
||||
R: DomRenderer,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
key: self.key.clone(),
|
||||
value: self.value.clone(),
|
||||
rndr: self.rndr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> Attribute for CustomAttr<K, V>
|
||||
impl<K, V, R> Attribute<R> for CustomAttr<K, V, R>
|
||||
where
|
||||
K: CustomAttributeKey,
|
||||
V: AttributeValue,
|
||||
V: AttributeValue<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
const MIN_LENGTH: usize = 0;
|
||||
type AsyncOutput = CustomAttr<K, V::AsyncOutput>;
|
||||
type AsyncOutput = CustomAttr<K, V::AsyncOutput, R>;
|
||||
type State = V::State;
|
||||
type Cloneable = CustomAttr<K, V::Cloneable>;
|
||||
type CloneableOwned = CustomAttr<K, V::CloneableOwned>;
|
||||
type Cloneable = CustomAttr<K, V::Cloneable, R>;
|
||||
type CloneableOwned = CustomAttr<K, V::CloneableOwned, R>;
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
self.key.as_ref().len() + 3 + self.value.html_len()
|
||||
@@ -64,10 +75,7 @@ where
|
||||
self.value.to_html(self.key.as_ref(), buf);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
if !K::KEY.is_empty() {
|
||||
self.value.hydrate::<FROM_SERVER>(self.key.as_ref(), el)
|
||||
} else {
|
||||
@@ -75,7 +83,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
self.value.build(el, self.key.as_ref())
|
||||
}
|
||||
|
||||
@@ -87,6 +95,7 @@ where
|
||||
CustomAttr {
|
||||
key: self.key,
|
||||
value: self.value.into_cloneable(),
|
||||
rndr: self.rndr,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +103,7 @@ where
|
||||
CustomAttr {
|
||||
key: self.key,
|
||||
value: self.value.into_cloneable_owned(),
|
||||
rndr: self.rndr,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,18 +115,20 @@ where
|
||||
CustomAttr {
|
||||
key: self.key,
|
||||
value: self.value.resolve().await,
|
||||
rndr: self.rndr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> NextAttribute for CustomAttr<K, V>
|
||||
impl<K, V, R> NextAttribute<R> for CustomAttr<K, V, R>
|
||||
where
|
||||
K: CustomAttributeKey,
|
||||
V: AttributeValue,
|
||||
V: AttributeValue<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type Output<NewAttr: Attribute> = (Self, NewAttr);
|
||||
type Output<NewAttr: Attribute<R>> = (Self, NewAttr);
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
new_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr> {
|
||||
@@ -124,10 +136,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> ToTemplate for CustomAttr<K, V>
|
||||
impl<K, V, R> ToTemplate for CustomAttr<K, V, R>
|
||||
where
|
||||
K: CustomAttributeKey,
|
||||
V: AttributeValue,
|
||||
V: AttributeValue<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
fn to_template(
|
||||
buf: &mut String,
|
||||
@@ -173,27 +186,28 @@ impl<const K: &'static str> CustomAttributeKey
|
||||
}
|
||||
|
||||
/// Adds a custom attribute to an element.
|
||||
pub trait CustomAttribute<K, V>
|
||||
pub trait CustomAttribute<K, V, Rndr>
|
||||
where
|
||||
K: CustomAttributeKey,
|
||||
V: AttributeValue,
|
||||
|
||||
Self: Sized + AddAnyAttr,
|
||||
V: AttributeValue<Rndr>,
|
||||
Rndr: DomRenderer,
|
||||
Self: Sized + AddAnyAttr<Rndr>,
|
||||
{
|
||||
/// Adds an HTML attribute by key and value.
|
||||
fn attr(
|
||||
self,
|
||||
key: K,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<CustomAttr<K, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<CustomAttr<K, V, Rndr>> {
|
||||
self.add_any_attr(custom_attribute(key, value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, K, V> CustomAttribute<K, V> for T
|
||||
impl<T, K, V, Rndr> CustomAttribute<K, V, Rndr> for T
|
||||
where
|
||||
T: AddAnyAttr,
|
||||
T: AddAnyAttr<Rndr>,
|
||||
K: CustomAttributeKey,
|
||||
V: AttributeValue,
|
||||
V: AttributeValue<Rndr>,
|
||||
Rndr: DomRenderer,
|
||||
{
|
||||
}
|
||||
|
||||
@@ -3,20 +3,22 @@ use crate::{
|
||||
html::{
|
||||
attribute::*,
|
||||
class::{class, Class, IntoClass},
|
||||
element::{ElementType, HasElementType, HtmlElement},
|
||||
element::{CreateElement, ElementType, HasElementType, HtmlElement},
|
||||
event::{on, on_target, EventDescriptor, On, Targeted},
|
||||
property::{prop, IntoProperty, Property},
|
||||
style::{style, IntoStyle, Style},
|
||||
},
|
||||
prelude::RenderHtml,
|
||||
renderer::DomRenderer,
|
||||
view::add_attr::AddAnyAttr,
|
||||
};
|
||||
use core::convert::From;
|
||||
|
||||
/// Adds an attribute that modifies the `class`.
|
||||
pub trait ClassAttribute<C>
|
||||
pub trait ClassAttribute<C, Rndr>
|
||||
where
|
||||
C: IntoClass,
|
||||
C: IntoClass<Rndr>,
|
||||
Rndr: DomRenderer,
|
||||
{
|
||||
/// The type of the element with the new attribute added.
|
||||
type Output;
|
||||
@@ -25,14 +27,16 @@ where
|
||||
fn class(self, value: C) -> Self::Output;
|
||||
}
|
||||
|
||||
impl<E, At, Ch, C> ClassAttribute<C> for HtmlElement<E, At, Ch>
|
||||
impl<E, At, Ch, C, Rndr> ClassAttribute<C, Rndr>
|
||||
for HtmlElement<E, At, Ch, Rndr>
|
||||
where
|
||||
E: ElementType + Send,
|
||||
At: Attribute + Send,
|
||||
Ch: RenderHtml + Send,
|
||||
C: IntoClass,
|
||||
E: ElementType + CreateElement<Rndr> + Send,
|
||||
At: Attribute<Rndr> + Send,
|
||||
Ch: RenderHtml<Rndr> + Send,
|
||||
C: IntoClass<Rndr>,
|
||||
Rndr: DomRenderer,
|
||||
{
|
||||
type Output = <Self as AddAnyAttr>::Output<Class<C>>;
|
||||
type Output = <Self as AddAnyAttr<Rndr>>::Output<Class<C, Rndr>>;
|
||||
|
||||
fn class(self, value: C) -> Self::Output {
|
||||
self.add_any_attr(class(value))
|
||||
@@ -40,9 +44,10 @@ where
|
||||
}
|
||||
|
||||
/// Adds an attribute that modifies the DOM properties.
|
||||
pub trait PropAttribute<K, P>
|
||||
pub trait PropAttribute<K, P, Rndr>
|
||||
where
|
||||
P: IntoProperty,
|
||||
P: IntoProperty<Rndr>,
|
||||
Rndr: DomRenderer,
|
||||
{
|
||||
/// The type of the element with the new attribute added.
|
||||
type Output;
|
||||
@@ -51,15 +56,17 @@ where
|
||||
fn prop(self, key: K, value: P) -> Self::Output;
|
||||
}
|
||||
|
||||
impl<E, At, Ch, K, P> PropAttribute<K, P> for HtmlElement<E, At, Ch>
|
||||
impl<E, At, Ch, K, P, Rndr> PropAttribute<K, P, Rndr>
|
||||
for HtmlElement<E, At, Ch, Rndr>
|
||||
where
|
||||
E: ElementType + Send,
|
||||
At: Attribute + Send,
|
||||
Ch: RenderHtml + Send,
|
||||
E: ElementType + CreateElement<Rndr> + Send,
|
||||
At: Attribute<Rndr> + Send,
|
||||
Ch: RenderHtml<Rndr> + Send,
|
||||
K: AsRef<str> + Send,
|
||||
P: IntoProperty,
|
||||
P: IntoProperty<Rndr>,
|
||||
Rndr: DomRenderer,
|
||||
{
|
||||
type Output = <Self as AddAnyAttr>::Output<Property<K, P>>;
|
||||
type Output = <Self as AddAnyAttr<Rndr>>::Output<Property<K, P, Rndr>>;
|
||||
|
||||
fn prop(self, key: K, value: P) -> Self::Output {
|
||||
self.add_any_attr(prop(key, value))
|
||||
@@ -67,9 +74,10 @@ where
|
||||
}
|
||||
|
||||
/// Adds an attribute that modifies the CSS styles.
|
||||
pub trait StyleAttribute<S>
|
||||
pub trait StyleAttribute<S, Rndr>
|
||||
where
|
||||
S: IntoStyle,
|
||||
S: IntoStyle<Rndr>,
|
||||
Rndr: DomRenderer,
|
||||
{
|
||||
/// The type of the element with the new attribute added.
|
||||
type Output;
|
||||
@@ -78,14 +86,16 @@ where
|
||||
fn style(self, value: S) -> Self::Output;
|
||||
}
|
||||
|
||||
impl<E, At, Ch, S> StyleAttribute<S> for HtmlElement<E, At, Ch>
|
||||
impl<E, At, Ch, S, Rndr> StyleAttribute<S, Rndr>
|
||||
for HtmlElement<E, At, Ch, Rndr>
|
||||
where
|
||||
E: ElementType + Send,
|
||||
At: Attribute + Send,
|
||||
Ch: RenderHtml + Send,
|
||||
S: IntoStyle,
|
||||
E: ElementType + CreateElement<Rndr> + Send,
|
||||
At: Attribute<Rndr> + Send,
|
||||
Ch: RenderHtml<Rndr> + Send,
|
||||
S: IntoStyle<Rndr>,
|
||||
Rndr: DomRenderer,
|
||||
{
|
||||
type Output = <Self as AddAnyAttr>::Output<Style<S>>;
|
||||
type Output = <Self as AddAnyAttr<Rndr>>::Output<Style<S, Rndr>>;
|
||||
|
||||
fn style(self, value: S) -> Self::Output {
|
||||
self.add_any_attr(style(value))
|
||||
@@ -93,7 +103,7 @@ where
|
||||
}
|
||||
|
||||
/// Adds an event listener to an element definition.
|
||||
pub trait OnAttribute<E, F> {
|
||||
pub trait OnAttribute<E, F, Rndr> {
|
||||
/// The type of the element with the event listener added.
|
||||
type Output;
|
||||
|
||||
@@ -101,17 +111,19 @@ pub trait OnAttribute<E, F> {
|
||||
fn on(self, event: E, cb: F) -> Self::Output;
|
||||
}
|
||||
|
||||
impl<El, At, Ch, E, F> OnAttribute<E, F> for HtmlElement<El, At, Ch>
|
||||
impl<El, At, Ch, E, F, Rndr> OnAttribute<E, F, Rndr>
|
||||
for HtmlElement<El, At, Ch, Rndr>
|
||||
where
|
||||
El: ElementType + Send,
|
||||
At: Attribute + Send,
|
||||
Ch: RenderHtml + Send,
|
||||
El: ElementType + CreateElement<Rndr> + Send,
|
||||
At: Attribute<Rndr> + Send,
|
||||
Ch: RenderHtml<Rndr> + Send,
|
||||
E: EventDescriptor + Send + 'static,
|
||||
E::EventType: 'static,
|
||||
E::EventType: From<crate::renderer::types::Event>,
|
||||
E::EventType: From<Rndr::Event>,
|
||||
F: FnMut(E::EventType) + 'static,
|
||||
Rndr: DomRenderer,
|
||||
{
|
||||
type Output = <Self as AddAnyAttr>::Output<On<E, F>>;
|
||||
type Output = <Self as AddAnyAttr<Rndr>>::Output<On<E, F, Rndr>>;
|
||||
|
||||
fn on(self, event: E, cb: F) -> Self::Output {
|
||||
self.add_any_attr(on(event, cb))
|
||||
@@ -119,7 +131,7 @@ where
|
||||
}
|
||||
|
||||
/// Adds an event listener with a typed target to an element definition.
|
||||
pub trait OnTargetAttribute<E, F, T> {
|
||||
pub trait OnTargetAttribute<E, F, T, Rndr> {
|
||||
/// The type of the element with the new attribute added.
|
||||
type Output;
|
||||
|
||||
@@ -127,36 +139,43 @@ pub trait OnTargetAttribute<E, F, T> {
|
||||
fn on_target(self, event: E, cb: F) -> Self::Output;
|
||||
}
|
||||
|
||||
impl<El, At, Ch, E, F> OnTargetAttribute<E, F, Self> for HtmlElement<El, At, Ch>
|
||||
impl<El, At, Ch, E, F, Rndr> OnTargetAttribute<E, F, Self, Rndr>
|
||||
for HtmlElement<El, At, Ch, Rndr>
|
||||
where
|
||||
El: ElementType + Send,
|
||||
At: Attribute + Send,
|
||||
Ch: RenderHtml + Send,
|
||||
El: ElementType + CreateElement<Rndr> + Send,
|
||||
At: Attribute<Rndr> + Send,
|
||||
Ch: RenderHtml<Rndr> + Send,
|
||||
E: EventDescriptor + Send + 'static,
|
||||
E::EventType: 'static,
|
||||
E::EventType: From<crate::renderer::types::Event>,
|
||||
F: FnMut(Targeted<E::EventType, <Self as HasElementType>::ElementType>)
|
||||
+ 'static,
|
||||
E::EventType: From<Rndr::Event>,
|
||||
F: FnMut(
|
||||
Targeted<E::EventType, <Self as HasElementType>::ElementType, Rndr>,
|
||||
) + 'static,
|
||||
Rndr: DomRenderer,
|
||||
{
|
||||
type Output =
|
||||
<Self as AddAnyAttr>::Output<On<E, Box<dyn FnMut(E::EventType)>>>;
|
||||
type Output = <Self as AddAnyAttr<Rndr>>::Output<
|
||||
On<E, Box<dyn FnMut(E::EventType)>, Rndr>,
|
||||
>;
|
||||
|
||||
fn on_target(self, event: E, cb: F) -> Self::Output {
|
||||
self.add_any_attr(on_target::<E, HtmlElement<El, At, Ch>, F>(event, cb))
|
||||
self.add_any_attr(
|
||||
on_target::<E, HtmlElement<El, At, Ch, Rndr>, Rndr, F>(event, cb),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Global attributes can be added to any HTML element.
|
||||
pub trait GlobalAttributes<V>
|
||||
pub trait GlobalAttributes<Rndr, V>
|
||||
where
|
||||
Self: Sized + AddAnyAttr,
|
||||
V: AttributeValue,
|
||||
Self: Sized + AddAnyAttr<Rndr>,
|
||||
V: AttributeValue<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
/// The `accesskey` global attribute provides a hint for generating a keyboard shortcut for the current element.
|
||||
fn accesskey(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<Accesskey, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Accesskey, V, Rndr>> {
|
||||
self.add_any_attr(accesskey(value))
|
||||
}
|
||||
|
||||
@@ -164,7 +183,7 @@ where
|
||||
fn autocapitalize(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<Autocapitalize, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Autocapitalize, V, Rndr>> {
|
||||
self.add_any_attr(autocapitalize(value))
|
||||
}
|
||||
|
||||
@@ -172,7 +191,7 @@ where
|
||||
fn autofocus(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<Autofocus, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Autofocus, V, Rndr>> {
|
||||
self.add_any_attr(autofocus(value))
|
||||
}
|
||||
|
||||
@@ -180,12 +199,16 @@ where
|
||||
fn contenteditable(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<Contenteditable, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Contenteditable, V, Rndr>>
|
||||
{
|
||||
self.add_any_attr(contenteditable(value))
|
||||
}
|
||||
|
||||
/// The `dir` global attribute is an enumerated attribute indicating the directionality of the element's text.
|
||||
fn dir(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Dir, V>> {
|
||||
fn dir(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Dir, V, Rndr>> {
|
||||
self.add_any_attr(dir(value))
|
||||
}
|
||||
|
||||
@@ -193,7 +216,7 @@ where
|
||||
fn draggable(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<Draggable, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Draggable, V, Rndr>> {
|
||||
self.add_any_attr(draggable(value))
|
||||
}
|
||||
|
||||
@@ -201,22 +224,31 @@ where
|
||||
fn enterkeyhint(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<Enterkeyhint, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Enterkeyhint, V, Rndr>> {
|
||||
self.add_any_attr(enterkeyhint(value))
|
||||
}
|
||||
|
||||
/// The `hidden` global attribute is a Boolean attribute indicating that the element is not yet, or is no longer, relevant.
|
||||
fn hidden(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Hidden, V>> {
|
||||
fn hidden(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Hidden, V, Rndr>> {
|
||||
self.add_any_attr(hidden(value))
|
||||
}
|
||||
|
||||
/// The `id` global attribute defines a unique identifier (ID) which must be unique in the whole document.
|
||||
fn id(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Id, V>> {
|
||||
fn id(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Id, V, Rndr>> {
|
||||
self.add_any_attr(id(value))
|
||||
}
|
||||
|
||||
/// The `inert` global attribute is a Boolean attribute that makes an element behave inertly.
|
||||
fn inert(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Inert, V>> {
|
||||
fn inert(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Inert, V, Rndr>> {
|
||||
self.add_any_attr(inert(value))
|
||||
}
|
||||
|
||||
@@ -224,17 +256,23 @@ where
|
||||
fn inputmode(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<Inputmode, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Inputmode, V, Rndr>> {
|
||||
self.add_any_attr(inputmode(value))
|
||||
}
|
||||
|
||||
/// The `is` global attribute allows you to specify that a standard HTML element should behave like a custom built-in element.
|
||||
fn is(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Is, V>> {
|
||||
fn is(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Is, V, Rndr>> {
|
||||
self.add_any_attr(is(value))
|
||||
}
|
||||
|
||||
/// The `itemid` global attribute is used to specify the unique, global identifier of an item.
|
||||
fn itemid(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Itemid, V>> {
|
||||
fn itemid(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Itemid, V, Rndr>> {
|
||||
self.add_any_attr(itemid(value))
|
||||
}
|
||||
|
||||
@@ -242,7 +280,7 @@ where
|
||||
fn itemprop(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<Itemprop, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Itemprop, V, Rndr>> {
|
||||
self.add_any_attr(itemprop(value))
|
||||
}
|
||||
|
||||
@@ -250,7 +288,7 @@ where
|
||||
fn itemref(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<Itemref, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Itemref, V, Rndr>> {
|
||||
self.add_any_attr(itemref(value))
|
||||
}
|
||||
|
||||
@@ -258,7 +296,7 @@ where
|
||||
fn itemscope(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<Itemscope, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Itemscope, V, Rndr>> {
|
||||
self.add_any_attr(itemscope(value))
|
||||
}
|
||||
|
||||
@@ -266,22 +304,31 @@ where
|
||||
fn itemtype(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<Itemtype, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Itemtype, V, Rndr>> {
|
||||
self.add_any_attr(itemtype(value))
|
||||
}
|
||||
|
||||
/// The `lang` global attribute helps define the language of an element.
|
||||
fn lang(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Lang, V>> {
|
||||
fn lang(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Lang, V, Rndr>> {
|
||||
self.add_any_attr(lang(value))
|
||||
}
|
||||
|
||||
/// The `nonce` global attribute is used to specify a cryptographic nonce.
|
||||
fn nonce(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Nonce, V>> {
|
||||
fn nonce(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Nonce, V, Rndr>> {
|
||||
self.add_any_attr(nonce(value))
|
||||
}
|
||||
|
||||
/// The `part` global attribute identifies the element as a part of a component.
|
||||
fn part(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Part, V>> {
|
||||
fn part(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Part, V, Rndr>> {
|
||||
self.add_any_attr(part(value))
|
||||
}
|
||||
|
||||
@@ -289,17 +336,23 @@ where
|
||||
fn popover(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<Popover, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Popover, V, Rndr>> {
|
||||
self.add_any_attr(popover(value))
|
||||
}
|
||||
|
||||
/// The `role` global attribute defines the role of an element in ARIA.
|
||||
fn role(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Role, V>> {
|
||||
fn role(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Role, V, Rndr>> {
|
||||
self.add_any_attr(role(value))
|
||||
}
|
||||
|
||||
/// The `slot` global attribute assigns a slot in a shadow DOM.
|
||||
fn slot(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Slot, V>> {
|
||||
fn slot(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Slot, V, Rndr>> {
|
||||
self.add_any_attr(slot(value))
|
||||
}
|
||||
|
||||
@@ -307,7 +360,7 @@ where
|
||||
fn spellcheck(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<Spellcheck, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Spellcheck, V, Rndr>> {
|
||||
self.add_any_attr(spellcheck(value))
|
||||
}
|
||||
|
||||
@@ -315,12 +368,15 @@ where
|
||||
fn tabindex(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<Tabindex, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Tabindex, V, Rndr>> {
|
||||
self.add_any_attr(tabindex(value))
|
||||
}
|
||||
|
||||
/// The `title` global attribute contains text representing advisory information.
|
||||
fn title(self, value: V) -> <Self as AddAnyAttr>::Output<Attr<Title, V>> {
|
||||
fn title(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Title, V, Rndr>> {
|
||||
self.add_any_attr(title(value))
|
||||
}
|
||||
|
||||
@@ -328,7 +384,7 @@ where
|
||||
fn translate(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<Translate, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Translate, V, Rndr>> {
|
||||
self.add_any_attr(translate(value))
|
||||
}
|
||||
|
||||
@@ -336,17 +392,20 @@ where
|
||||
fn virtualkeyboardpolicy(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<Virtualkeyboardpolicy, V>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<Virtualkeyboardpolicy, V, Rndr>>
|
||||
{
|
||||
self.add_any_attr(virtualkeyboardpolicy(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<El, At, Ch, V> GlobalAttributes<V> for HtmlElement<El, At, Ch>
|
||||
impl<El, At, Ch, Rndr, V> GlobalAttributes<Rndr, V>
|
||||
for HtmlElement<El, At, Ch, Rndr>
|
||||
where
|
||||
El: ElementType + Send,
|
||||
At: Attribute + Send,
|
||||
Ch: RenderHtml + Send,
|
||||
V: AttributeValue,
|
||||
El: ElementType + CreateElement<Rndr> + Send,
|
||||
At: Attribute<Rndr> + Send,
|
||||
Ch: RenderHtml<Rndr> + Send,
|
||||
V: AttributeValue<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
}
|
||||
|
||||
@@ -359,7 +418,7 @@ macro_rules! on_definitions {
|
||||
fn $key(
|
||||
self,
|
||||
value: V,
|
||||
) -> <Self as AddAnyAttr>::Output<Attr<[<$key:camel>], V>>
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<Attr<[<$key:camel>], V, Rndr>>
|
||||
{
|
||||
self.add_any_attr($key(value))
|
||||
}
|
||||
@@ -369,10 +428,11 @@ macro_rules! on_definitions {
|
||||
}
|
||||
|
||||
/// Provides methods for HTML event listener attributes.
|
||||
pub trait GlobalOnAttributes<V>
|
||||
pub trait GlobalOnAttributes<Rndr, V>
|
||||
where
|
||||
Self: Sized + AddAnyAttr,
|
||||
V: AttributeValue,
|
||||
Self: Sized + AddAnyAttr<Rndr>,
|
||||
V: AttributeValue<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
on_definitions! {
|
||||
/// The `onabort` attribute specifies the event handler for the abort event.
|
||||
@@ -515,11 +575,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<El, At, Ch, V> GlobalOnAttributes<V> for HtmlElement<El, At, Ch>
|
||||
impl<El, At, Ch, Rndr, V> GlobalOnAttributes<Rndr, V>
|
||||
for HtmlElement<El, At, Ch, Rndr>
|
||||
where
|
||||
El: ElementType + Send,
|
||||
At: Attribute + Send,
|
||||
Ch: RenderHtml + Send,
|
||||
V: AttributeValue,
|
||||
El: ElementType + CreateElement<Rndr> + Send,
|
||||
At: Attribute<Rndr> + Send,
|
||||
Ch: RenderHtml<Rndr> + Send,
|
||||
V: AttributeValue<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::{Attr, AttributeValue};
|
||||
use std::fmt::Debug;
|
||||
use crate::renderer::Renderer;
|
||||
use std::{fmt::Debug, marker::PhantomData};
|
||||
|
||||
/// An HTML attribute key.
|
||||
pub trait AttributeKey: Clone + Send + 'static {
|
||||
@@ -13,11 +14,11 @@ macro_rules! attributes {
|
||||
$(
|
||||
#[$meta]
|
||||
#[track_caller]
|
||||
pub fn $key<V>(value: V) -> Attr<[<$key:camel>], V>
|
||||
where V: AttributeValue,
|
||||
|
||||
pub fn $key<V, Rndr>(value: V) -> Attr<[<$key:camel>], V, Rndr>
|
||||
where V: AttributeValue<Rndr>,
|
||||
Rndr: Renderer
|
||||
{
|
||||
Attr([<$key:camel>], value)
|
||||
Attr([<$key:camel>], value, PhantomData)
|
||||
}
|
||||
|
||||
#[$meta]
|
||||
|
||||
@@ -8,25 +8,28 @@ pub mod custom;
|
||||
pub mod global;
|
||||
mod key;
|
||||
mod value;
|
||||
use crate::view::{Position, ToTemplate};
|
||||
use crate::{
|
||||
renderer::Renderer,
|
||||
view::{Position, ToTemplate},
|
||||
};
|
||||
pub use key::*;
|
||||
use std::{fmt::Debug, future::Future};
|
||||
use std::{fmt::Debug, future::Future, marker::PhantomData};
|
||||
pub use value::*;
|
||||
|
||||
/// Defines an attribute: anything that can modify an element.
|
||||
pub trait Attribute: NextAttribute + Send {
|
||||
pub trait Attribute<R: Renderer>: NextAttribute<R> + Send {
|
||||
/// The minimum length of this attribute in HTML.
|
||||
const MIN_LENGTH: usize;
|
||||
|
||||
/// The state that should be retained between building and rebuilding.
|
||||
type State;
|
||||
/// The type once all async data have loaded.
|
||||
type AsyncOutput: Attribute;
|
||||
type AsyncOutput: Attribute<R>;
|
||||
/// An equivalent to this attribute that can be cloned to be shared across elements.
|
||||
type Cloneable: Attribute + 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 + Clone + 'static;
|
||||
type CloneableOwned: Attribute<R> + Clone + 'static;
|
||||
|
||||
/// An approximation of the actual length of this attribute in HTML.
|
||||
fn html_len(&self) -> usize;
|
||||
@@ -46,13 +49,10 @@ pub trait Attribute: NextAttribute + Send {
|
||||
|
||||
/// 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: &crate::renderer::types::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: &crate::renderer::types::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);
|
||||
@@ -75,18 +75,21 @@ pub trait Attribute: NextAttribute + 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 {
|
||||
pub trait NextAttribute<R: Renderer> {
|
||||
/// The type of the new, combined attribute.
|
||||
type Output<NewAttr: Attribute>: Attribute;
|
||||
type Output<NewAttr: Attribute<R>>: Attribute<R>;
|
||||
|
||||
/// Adds a new attribute.
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
new_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>;
|
||||
}
|
||||
|
||||
impl Attribute for () {
|
||||
impl<R> Attribute<R> for ()
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
const MIN_LENGTH: usize = 0;
|
||||
|
||||
type State = ();
|
||||
@@ -107,13 +110,10 @@ impl Attribute for () {
|
||||
) {
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
_el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, _el: &R::Element) -> Self::State {
|
||||
}
|
||||
|
||||
fn build(self, _el: &crate::renderer::types::Element) -> Self::State {}
|
||||
fn build(self, _el: &R::Element) -> Self::State {}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
|
||||
@@ -130,10 +130,13 @@ impl Attribute for () {
|
||||
async fn resolve(self) -> Self::AsyncOutput {}
|
||||
}
|
||||
|
||||
impl NextAttribute for () {
|
||||
type Output<NewAttr: Attribute> = (NewAttr,);
|
||||
impl<R> NextAttribute<R> for ()
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type Output<NewAttr: Attribute<R>> = (NewAttr,);
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
new_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr> {
|
||||
@@ -143,25 +146,28 @@ impl NextAttribute for () {
|
||||
|
||||
/// An attribute with a key and value.
|
||||
#[derive(Debug)]
|
||||
pub struct Attr<K, V>(pub K, pub V)
|
||||
pub struct Attr<K, V, R>(pub K, pub V, pub PhantomData<R>)
|
||||
where
|
||||
K: AttributeKey,
|
||||
V: AttributeValue;
|
||||
V: AttributeValue<R>,
|
||||
R: Renderer;
|
||||
|
||||
impl<K, V> Clone for Attr<K, V>
|
||||
impl<K, V, R> Clone for Attr<K, V, R>
|
||||
where
|
||||
K: AttributeKey,
|
||||
V: AttributeValue + Clone,
|
||||
V: AttributeValue<R> + Clone,
|
||||
R: Renderer,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone(), self.1.clone())
|
||||
Self(self.0.clone(), self.1.clone(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> ToTemplate for Attr<K, V>
|
||||
impl<K, V, R> ToTemplate for Attr<K, V, R>
|
||||
where
|
||||
K: AttributeKey,
|
||||
V: AttributeValue,
|
||||
V: AttributeValue<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
fn to_template(
|
||||
buf: &mut String,
|
||||
@@ -174,17 +180,18 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> Attribute for Attr<K, V>
|
||||
impl<K, V, R> Attribute<R> for Attr<K, V, R>
|
||||
where
|
||||
K: AttributeKey + Send,
|
||||
V: AttributeValue + Send,
|
||||
V: AttributeValue<R> + Send,
|
||||
R: Renderer,
|
||||
{
|
||||
const MIN_LENGTH: usize = 0;
|
||||
|
||||
type State = V::State;
|
||||
type AsyncOutput = Attr<K, V::AsyncOutput>;
|
||||
type Cloneable = Attr<K, V::Cloneable>;
|
||||
type CloneableOwned = Attr<K, V::CloneableOwned>;
|
||||
type AsyncOutput = Attr<K, V::AsyncOutput, R>;
|
||||
type Cloneable = Attr<K, V::Cloneable, R>;
|
||||
type CloneableOwned = Attr<K, V::CloneableOwned, R>;
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
K::KEY.len() + 3 + self.1.html_len()
|
||||
@@ -200,14 +207,11 @@ where
|
||||
self.1.to_html(K::KEY, buf);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
self.1.hydrate::<FROM_SERVER>(K::KEY, el)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
V::build(self.1, el, K::KEY)
|
||||
}
|
||||
|
||||
@@ -216,11 +220,11 @@ where
|
||||
}
|
||||
|
||||
fn into_cloneable(self) -> Self::Cloneable {
|
||||
Attr(self.0, self.1.into_cloneable())
|
||||
Attr(self.0, self.1.into_cloneable(), PhantomData)
|
||||
}
|
||||
|
||||
fn into_cloneable_owned(self) -> Self::CloneableOwned {
|
||||
Attr(self.0, self.1.into_cloneable_owned())
|
||||
Attr(self.0, self.1.into_cloneable_owned(), PhantomData)
|
||||
}
|
||||
|
||||
fn dry_resolve(&mut self) {
|
||||
@@ -228,18 +232,19 @@ where
|
||||
}
|
||||
|
||||
async fn resolve(self) -> Self::AsyncOutput {
|
||||
Attr(self.0, self.1.resolve().await)
|
||||
Attr(self.0, self.1.resolve().await, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> NextAttribute for Attr<K, V>
|
||||
impl<K, V, R> NextAttribute<R> for Attr<K, V, R>
|
||||
where
|
||||
K: AttributeKey,
|
||||
V: AttributeValue,
|
||||
V: AttributeValue<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
type Output<NewAttr: Attribute> = (Self, NewAttr);
|
||||
type Output<NewAttr: Attribute<R>> = (Self, NewAttr);
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
new_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr> {
|
||||
@@ -249,11 +254,11 @@ where
|
||||
|
||||
macro_rules! impl_attr_for_tuples {
|
||||
($first:ident, $($ty:ident),* $(,)?) => {
|
||||
impl<$first, $($ty),*> Attribute for ($first, $($ty,)*)
|
||||
impl<$first, $($ty),*, Rndr> Attribute<Rndr> for ($first, $($ty,)*)
|
||||
where
|
||||
$first: Attribute,
|
||||
$($ty: Attribute),*,
|
||||
|
||||
$first: Attribute<Rndr>,
|
||||
$($ty: Attribute<Rndr>),*,
|
||||
Rndr: Renderer
|
||||
{
|
||||
const MIN_LENGTH: usize = $first::MIN_LENGTH $(+ $ty::MIN_LENGTH)*;
|
||||
|
||||
@@ -275,7 +280,7 @@ macro_rules! impl_attr_for_tuples {
|
||||
$($ty.to_html(buf, class, style, inner_html));*
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &Rndr::Element) -> Self::State {
|
||||
#[allow(non_snake_case)]
|
||||
let ($first, $($ty,)* ) = self;
|
||||
(
|
||||
@@ -284,7 +289,7 @@ macro_rules! impl_attr_for_tuples {
|
||||
)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &Rndr::Element) -> Self::State {
|
||||
#[allow(non_snake_case)]
|
||||
let ($first, $($ty,)*) = self;
|
||||
(
|
||||
@@ -337,15 +342,15 @@ macro_rules! impl_attr_for_tuples {
|
||||
}
|
||||
}
|
||||
|
||||
impl<$first, $($ty),*> NextAttribute for ($first, $($ty,)*)
|
||||
impl<$first, $($ty),*, Rndr> NextAttribute<Rndr> for ($first, $($ty,)*)
|
||||
where
|
||||
$first: Attribute,
|
||||
$($ty: Attribute),*,
|
||||
|
||||
$first: Attribute<Rndr>,
|
||||
$($ty: Attribute<Rndr>),*,
|
||||
Rndr: Renderer
|
||||
{
|
||||
type Output<NewAttr: Attribute> = ($first, $($ty,)* NewAttr);
|
||||
type Output<NewAttr: Attribute<Rndr>> = ($first, $($ty,)* NewAttr);
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
new_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr> {
|
||||
@@ -359,11 +364,11 @@ macro_rules! impl_attr_for_tuples {
|
||||
|
||||
macro_rules! impl_attr_for_tuples_truncate_additional {
|
||||
($first:ident, $($ty:ident),* $(,)?) => {
|
||||
impl<$first, $($ty),*> Attribute for ($first, $($ty,)*)
|
||||
impl<$first, $($ty),*, Rndr> Attribute<Rndr> for ($first, $($ty,)*)
|
||||
where
|
||||
$first: Attribute,
|
||||
$($ty: Attribute),*,
|
||||
|
||||
$first: Attribute<Rndr>,
|
||||
$($ty: Attribute<Rndr>),*,
|
||||
Rndr: Renderer
|
||||
{
|
||||
const MIN_LENGTH: usize = $first::MIN_LENGTH $(+ $ty::MIN_LENGTH)*;
|
||||
|
||||
@@ -385,7 +390,7 @@ macro_rules! impl_attr_for_tuples_truncate_additional {
|
||||
$($ty.to_html(buf, class, style, inner_html));*
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &Rndr::Element) -> Self::State {
|
||||
#[allow(non_snake_case)]
|
||||
let ($first, $($ty,)* ) = self;
|
||||
(
|
||||
@@ -394,7 +399,7 @@ macro_rules! impl_attr_for_tuples_truncate_additional {
|
||||
)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &Rndr::Element) -> Self::State {
|
||||
#[allow(non_snake_case)]
|
||||
let ($first, $($ty,)*) = self;
|
||||
(
|
||||
@@ -447,15 +452,15 @@ macro_rules! impl_attr_for_tuples_truncate_additional {
|
||||
}
|
||||
}
|
||||
|
||||
impl<$first, $($ty),*> NextAttribute for ($first, $($ty,)*)
|
||||
impl<$first, $($ty),*, Rndr> NextAttribute<Rndr> for ($first, $($ty,)*)
|
||||
where
|
||||
$first: Attribute,
|
||||
$($ty: Attribute),*,
|
||||
|
||||
$first: Attribute<Rndr>,
|
||||
$($ty: Attribute<Rndr>),*,
|
||||
Rndr: Renderer
|
||||
{
|
||||
type Output<NewAttr: Attribute> = ($first, $($ty,)*);
|
||||
type Output<NewAttr: Attribute<Rndr>> = ($first, $($ty,)*);
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
_new_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr> {
|
||||
@@ -466,9 +471,10 @@ macro_rules! impl_attr_for_tuples_truncate_additional {
|
||||
};
|
||||
}
|
||||
|
||||
impl<A> Attribute for (A,)
|
||||
impl<A, Rndr> Attribute<Rndr> for (A,)
|
||||
where
|
||||
A: Attribute,
|
||||
A: Attribute<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
const MIN_LENGTH: usize = A::MIN_LENGTH;
|
||||
|
||||
@@ -493,12 +499,12 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &Rndr::Element,
|
||||
) -> Self::State {
|
||||
self.0.hydrate::<FROM_SERVER>(el)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &Rndr::Element) -> Self::State {
|
||||
self.0.build(el)
|
||||
}
|
||||
|
||||
@@ -523,13 +529,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> NextAttribute for (A,)
|
||||
impl<A, Rndr> NextAttribute<Rndr> for (A,)
|
||||
where
|
||||
A: Attribute,
|
||||
A: Attribute<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output<NewAttr: Attribute> = (A, NewAttr);
|
||||
type Output<NewAttr: Attribute<Rndr>> = (A, NewAttr);
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
new_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr> {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::renderer::Rndr;
|
||||
use crate::renderer::Renderer;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
future::Future,
|
||||
@@ -12,12 +12,12 @@ use std::{
|
||||
};
|
||||
|
||||
/// A possible value for an HTML attribute.
|
||||
pub trait AttributeValue: Send {
|
||||
pub trait AttributeValue<R: Renderer>: Send {
|
||||
/// The state that should be retained between building and rebuilding.
|
||||
type State;
|
||||
|
||||
/// The type once all async data have loaded.
|
||||
type AsyncOutput: AttributeValue;
|
||||
type AsyncOutput: AttributeValue<R>;
|
||||
|
||||
/// A version of the value that can be cloned. This can be the same type, or a
|
||||
/// reference-counted type. Generally speaking, this does *not* need to refer to the same data,
|
||||
@@ -25,12 +25,12 @@ pub trait AttributeValue: Send {
|
||||
/// probably make it reference-counted (so that a `FnMut()` continues mutating the same
|
||||
/// closure), but making a `String` cloneable does not necessarily need to make it an
|
||||
/// `Arc<str>`, as two different clones of a `String` will still have the same value.
|
||||
type Cloneable: AttributeValue + Clone;
|
||||
type Cloneable: AttributeValue<R> + Clone;
|
||||
|
||||
/// A cloneable type that is also `'static`. This is used for spreading across types when the
|
||||
/// spreadable attribute needs to be owned. In some cases (`&'a str` to `Arc<str>`, etc.) the owned
|
||||
/// cloneable type has worse performance than the cloneable type, so they are separate.
|
||||
type CloneableOwned: AttributeValue + Clone + 'static;
|
||||
type CloneableOwned: AttributeValue<R> + Clone + 'static;
|
||||
|
||||
/// An approximation of the actual length of this attribute in HTML.
|
||||
fn html_len(&self) -> usize;
|
||||
@@ -46,15 +46,11 @@ pub trait AttributeValue: Send {
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
key: &str,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State;
|
||||
|
||||
/// Adds this attribute to the element during client-side rendering.
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::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);
|
||||
@@ -74,7 +70,10 @@ pub trait AttributeValue: Send {
|
||||
fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;
|
||||
}
|
||||
|
||||
impl AttributeValue for () {
|
||||
impl<R> AttributeValue<R> for ()
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type State = ();
|
||||
type AsyncOutput = ();
|
||||
type Cloneable = ();
|
||||
@@ -88,19 +87,9 @@ impl AttributeValue for () {
|
||||
|
||||
fn to_template(_key: &str, _buf: &mut String) {}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
_key: &str,
|
||||
_el: &crate::renderer::types::Element,
|
||||
) {
|
||||
}
|
||||
fn hydrate<const FROM_SERVER: bool>(self, _key: &str, _el: &R::Element) {}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
_el: &crate::renderer::types::Element,
|
||||
_key: &str,
|
||||
) -> Self::State {
|
||||
}
|
||||
fn build(self, _el: &R::Element, _key: &str) -> Self::State {}
|
||||
|
||||
fn rebuild(self, _key: &str, _state: &mut Self::State) {}
|
||||
|
||||
@@ -117,8 +106,11 @@ impl AttributeValue for () {
|
||||
async fn resolve(self) {}
|
||||
}
|
||||
|
||||
impl<'a> AttributeValue for &'a str {
|
||||
type State = (crate::renderer::types::Element, &'a str);
|
||||
impl<'a, R> AttributeValue<R> for &'a str
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type State = (R::Element, &'a str);
|
||||
type AsyncOutput = &'a str;
|
||||
type Cloneable = &'a str;
|
||||
type CloneableOwned = Arc<str>;
|
||||
@@ -140,29 +132,25 @@ impl<'a> AttributeValue for &'a str {
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
key: &str,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
// if we're actually hydrating from SSRed HTML, we don't need to set the attribute
|
||||
// if we're hydrating from a CSR-cloned <template>, we do need to set non-StaticAttr attributes
|
||||
if !FROM_SERVER {
|
||||
Rndr::set_attribute(el, key, self);
|
||||
R::set_attribute(el, key, self);
|
||||
}
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
Rndr::set_attribute(el, key, self);
|
||||
fn build(self, el: &R::Element, key: &str) -> Self::State {
|
||||
R::set_attribute(el, key, self);
|
||||
(el.to_owned(), self)
|
||||
}
|
||||
|
||||
fn rebuild(self, key: &str, state: &mut Self::State) {
|
||||
let (el, prev_value) = state;
|
||||
if self != *prev_value {
|
||||
Rndr::set_attribute(el, key, self);
|
||||
R::set_attribute(el, key, self);
|
||||
}
|
||||
*prev_value = self;
|
||||
}
|
||||
@@ -183,8 +171,10 @@ impl<'a> AttributeValue for &'a str {
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<const V: &'static str> AttributeValue
|
||||
impl<R, const V: &'static str> AttributeValue<R>
|
||||
for crate::view::static_types::Static<V>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = ();
|
||||
@@ -196,7 +186,7 @@ impl<const V: &'static str> AttributeValue
|
||||
}
|
||||
|
||||
fn to_html(self, key: &str, buf: &mut String) {
|
||||
<&str as AttributeValue>::to_html(V, key, buf);
|
||||
<&str as AttributeValue<R>>::to_html(V, key, buf);
|
||||
}
|
||||
|
||||
fn to_template(key: &str, buf: &mut String) {
|
||||
@@ -210,16 +200,12 @@ impl<const V: &'static str> AttributeValue
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
_key: &str,
|
||||
_el: &crate::renderer::types::Element,
|
||||
_el: &R::Element,
|
||||
) -> Self::State {
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
<&str as AttributeValue>::build(V, el, key);
|
||||
fn build(self, el: &R::Element, key: &str) -> Self::State {
|
||||
<&str as AttributeValue<R>>::build(V, el, key);
|
||||
}
|
||||
|
||||
fn rebuild(self, _key: &str, _state: &mut Self::State) {}
|
||||
@@ -239,9 +225,12 @@ impl<const V: &'static str> AttributeValue
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AttributeValue for &'a String {
|
||||
impl<'a, R> AttributeValue<R> for &'a String
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::Element, &'a String);
|
||||
type State = (R::Element, &'a String);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Arc<str>;
|
||||
|
||||
@@ -250,7 +239,7 @@ impl<'a> AttributeValue for &'a String {
|
||||
}
|
||||
|
||||
fn to_html(self, key: &str, buf: &mut String) {
|
||||
<&str as AttributeValue>::to_html(self.as_str(), key, buf);
|
||||
<&str as AttributeValue<R>>::to_html(self.as_str(), key, buf);
|
||||
}
|
||||
|
||||
fn to_template(_key: &str, _buf: &mut String) {}
|
||||
@@ -258,9 +247,9 @@ impl<'a> AttributeValue for &'a String {
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
key: &str,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
let (el, _) = <&str as AttributeValue>::hydrate::<FROM_SERVER>(
|
||||
let (el, _) = <&str as AttributeValue<R>>::hydrate::<FROM_SERVER>(
|
||||
self.as_str(),
|
||||
key,
|
||||
el,
|
||||
@@ -268,19 +257,15 @@ impl<'a> AttributeValue for &'a String {
|
||||
(el, self)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
Rndr::set_attribute(el, key, self);
|
||||
fn build(self, el: &R::Element, key: &str) -> Self::State {
|
||||
R::set_attribute(el, key, self);
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn rebuild(self, key: &str, state: &mut Self::State) {
|
||||
let (el, prev_value) = state;
|
||||
if self != *prev_value {
|
||||
Rndr::set_attribute(el, key, self);
|
||||
R::set_attribute(el, key, self);
|
||||
}
|
||||
*prev_value = self;
|
||||
}
|
||||
@@ -300,9 +285,12 @@ impl<'a> AttributeValue for &'a String {
|
||||
}
|
||||
}
|
||||
|
||||
impl AttributeValue for String {
|
||||
impl<R> AttributeValue<R> for String
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::Element, String);
|
||||
type State = (R::Element, String);
|
||||
type Cloneable = Arc<str>;
|
||||
type CloneableOwned = Arc<str>;
|
||||
|
||||
@@ -311,7 +299,7 @@ impl AttributeValue for String {
|
||||
}
|
||||
|
||||
fn to_html(self, key: &str, buf: &mut String) {
|
||||
<&str as AttributeValue>::to_html(self.as_str(), key, buf);
|
||||
<&str as AttributeValue<R>>::to_html(self.as_str(), key, buf);
|
||||
}
|
||||
|
||||
fn to_template(_key: &str, _buf: &mut String) {}
|
||||
@@ -319,9 +307,9 @@ impl AttributeValue for String {
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
key: &str,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
let (el, _) = <&str as AttributeValue>::hydrate::<FROM_SERVER>(
|
||||
let (el, _) = <&str as AttributeValue<R>>::hydrate::<FROM_SERVER>(
|
||||
self.as_str(),
|
||||
key,
|
||||
el,
|
||||
@@ -329,19 +317,15 @@ impl AttributeValue for String {
|
||||
(el, self)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
Rndr::set_attribute(el, key, &self);
|
||||
fn build(self, el: &R::Element, key: &str) -> Self::State {
|
||||
R::set_attribute(el, key, &self);
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn rebuild(self, key: &str, state: &mut Self::State) {
|
||||
let (el, prev_value) = state;
|
||||
if self != *prev_value {
|
||||
Rndr::set_attribute(el, key, &self);
|
||||
R::set_attribute(el, key, &self);
|
||||
}
|
||||
*prev_value = self;
|
||||
}
|
||||
@@ -361,9 +345,12 @@ impl AttributeValue for String {
|
||||
}
|
||||
}
|
||||
|
||||
impl AttributeValue for Arc<str> {
|
||||
impl<R> AttributeValue<R> for Arc<str>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::Element, Arc<str>);
|
||||
type State = (R::Element, Arc<str>);
|
||||
type Cloneable = Arc<str>;
|
||||
type CloneableOwned = Arc<str>;
|
||||
|
||||
@@ -372,7 +359,7 @@ impl AttributeValue for Arc<str> {
|
||||
}
|
||||
|
||||
fn to_html(self, key: &str, buf: &mut String) {
|
||||
<&str as AttributeValue>::to_html(self.as_ref(), key, buf);
|
||||
<&str as AttributeValue<R>>::to_html(self.as_ref(), key, buf);
|
||||
}
|
||||
|
||||
fn to_template(_key: &str, _buf: &mut String) {}
|
||||
@@ -380,9 +367,9 @@ impl AttributeValue for Arc<str> {
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
key: &str,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
let (el, _) = <&str as AttributeValue>::hydrate::<FROM_SERVER>(
|
||||
let (el, _) = <&str as AttributeValue<R>>::hydrate::<FROM_SERVER>(
|
||||
self.as_ref(),
|
||||
key,
|
||||
el,
|
||||
@@ -390,19 +377,15 @@ impl AttributeValue for Arc<str> {
|
||||
(el, self)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
Rndr::set_attribute(el, key, &self);
|
||||
fn build(self, el: &R::Element, key: &str) -> Self::State {
|
||||
R::set_attribute(el, key, &self);
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn rebuild(self, key: &str, state: &mut Self::State) {
|
||||
let (el, prev_value) = state;
|
||||
if self != *prev_value {
|
||||
Rndr::set_attribute(el, key, &self);
|
||||
R::set_attribute(el, key, &self);
|
||||
}
|
||||
*prev_value = self;
|
||||
}
|
||||
@@ -423,9 +406,12 @@ impl AttributeValue for Arc<str> {
|
||||
}
|
||||
// TODO impl AttributeValue for Rc<str> and Arc<str> too
|
||||
|
||||
impl AttributeValue for bool {
|
||||
impl<R> AttributeValue<R> for bool
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::Element, bool);
|
||||
type State = (R::Element, bool);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
@@ -445,23 +431,19 @@ impl AttributeValue for bool {
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
key: &str,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
// if we're actually hydrating from SSRed HTML, we don't need to set the attribute
|
||||
// if we're hydrating from a CSR-cloned <template>, we do need to set non-StaticAttr attributes
|
||||
if !FROM_SERVER {
|
||||
Rndr::set_attribute(el, key, "");
|
||||
R::set_attribute(el, key, "");
|
||||
}
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
fn build(self, el: &R::Element, key: &str) -> Self::State {
|
||||
if self {
|
||||
Rndr::set_attribute(el, key, "");
|
||||
R::set_attribute(el, key, "");
|
||||
}
|
||||
(el.clone(), self)
|
||||
}
|
||||
@@ -470,9 +452,9 @@ impl AttributeValue for bool {
|
||||
let (el, prev_value) = state;
|
||||
if self != *prev_value {
|
||||
if self {
|
||||
Rndr::set_attribute(el, key, "");
|
||||
R::set_attribute(el, key, "");
|
||||
} else {
|
||||
Rndr::remove_attribute(el, key);
|
||||
R::remove_attribute(el, key);
|
||||
}
|
||||
}
|
||||
*prev_value = self;
|
||||
@@ -493,12 +475,13 @@ impl AttributeValue for bool {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> AttributeValue for Option<V>
|
||||
impl<V, R> AttributeValue<R> for Option<V>
|
||||
where
|
||||
V: AttributeValue,
|
||||
V: AttributeValue<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Option<V::AsyncOutput>;
|
||||
type State = (crate::renderer::types::Element, Option<V::State>);
|
||||
type State = (R::Element, Option<V::State>);
|
||||
type Cloneable = Option<V::Cloneable>;
|
||||
type CloneableOwned = Option<V::CloneableOwned>;
|
||||
|
||||
@@ -520,17 +503,13 @@ where
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
key: &str,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
let state = self.map(|v| v.hydrate::<FROM_SERVER>(key, el));
|
||||
(el.clone(), state)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
fn build(self, el: &R::Element, key: &str) -> Self::State {
|
||||
let el = el.clone();
|
||||
let v = self.map(|v| v.build(&el, key));
|
||||
(el, v)
|
||||
@@ -541,7 +520,7 @@ where
|
||||
match (self, prev.as_mut()) {
|
||||
(None, None) => {}
|
||||
(None, Some(_)) => {
|
||||
Rndr::remove_attribute(el, key);
|
||||
R::remove_attribute(el, key);
|
||||
*prev = None;
|
||||
}
|
||||
(Some(value), None) => {
|
||||
@@ -582,12 +561,12 @@ pub(crate) fn escape_attr(value: &str) -> Cow<'_, str> {
|
||||
macro_rules! render_primitive {
|
||||
($($child_type:ty),* $(,)?) => {
|
||||
$(
|
||||
impl AttributeValue for $child_type
|
||||
impl<R> AttributeValue<R> for $child_type
|
||||
where
|
||||
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = $child_type;
|
||||
type State = (crate::renderer::types::Element, $child_type);
|
||||
type State = (R::Element, $child_type);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
@@ -596,7 +575,7 @@ macro_rules! render_primitive {
|
||||
}
|
||||
|
||||
fn to_html(self, key: &str, buf: &mut String) {
|
||||
<String as AttributeValue>::to_html(self.to_string(), key, buf);
|
||||
<String as AttributeValue<R>>::to_html(self.to_string(), key, buf);
|
||||
}
|
||||
|
||||
fn to_template(_key: &str, _buf: &mut String) {}
|
||||
@@ -604,25 +583,25 @@ macro_rules! render_primitive {
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
key: &str,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
// if we're actually hydrating from SSRed HTML, we don't need to set the attribute
|
||||
// if we're hydrating from a CSR-cloned <template>, we do need to set non-StaticAttr attributes
|
||||
if !FROM_SERVER {
|
||||
Rndr::set_attribute(el, key, &self.to_string());
|
||||
R::set_attribute(el, key, &self.to_string());
|
||||
}
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element, key: &str) -> Self::State {
|
||||
Rndr::set_attribute(el, key, &self.to_string());
|
||||
fn build(self, el: &R::Element, key: &str) -> Self::State {
|
||||
R::set_attribute(el, key, &self.to_string());
|
||||
(el.to_owned(), self)
|
||||
}
|
||||
|
||||
fn rebuild(self, key: &str, state: &mut Self::State) {
|
||||
let (el, prev_value) = state;
|
||||
if self != *prev_value {
|
||||
Rndr::set_attribute(el, key, &self.to_string());
|
||||
R::set_attribute(el, key, &self.to_string());
|
||||
}
|
||||
*prev_value = self;
|
||||
}
|
||||
|
||||
@@ -1,46 +1,53 @@
|
||||
use super::attribute::{Attribute, NextAttribute};
|
||||
use crate::{
|
||||
renderer::Rndr,
|
||||
renderer::DomRenderer,
|
||||
view::{Position, ToTemplate},
|
||||
};
|
||||
use std::{future::Future, sync::Arc};
|
||||
use std::{future::Future, marker::PhantomData, sync::Arc};
|
||||
|
||||
/// Adds a CSS class.
|
||||
#[inline(always)]
|
||||
pub fn class<C>(class: C) -> Class<C>
|
||||
pub fn class<C, R>(class: C) -> Class<C, R>
|
||||
where
|
||||
C: IntoClass,
|
||||
C: IntoClass<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
Class { class }
|
||||
Class {
|
||||
class,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// A CSS class.
|
||||
#[derive(Debug)]
|
||||
pub struct Class<C> {
|
||||
pub struct Class<C, R> {
|
||||
class: C,
|
||||
rndr: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<C> Clone for Class<C>
|
||||
impl<C, R> Clone for Class<C, R>
|
||||
where
|
||||
C: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
class: self.class.clone(),
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Attribute for Class<C>
|
||||
impl<C, R> Attribute<R> for Class<C, R>
|
||||
where
|
||||
C: IntoClass,
|
||||
C: IntoClass<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
const MIN_LENGTH: usize = C::MIN_LENGTH;
|
||||
|
||||
type AsyncOutput = Class<C::AsyncOutput>;
|
||||
type AsyncOutput = Class<C::AsyncOutput, R>;
|
||||
type State = C::State;
|
||||
type Cloneable = Class<C::Cloneable>;
|
||||
type CloneableOwned = Class<C::CloneableOwned>;
|
||||
type Cloneable = Class<C::Cloneable, R>;
|
||||
type CloneableOwned = Class<C::CloneableOwned, R>;
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
self.class.html_len() + 1
|
||||
@@ -57,14 +64,11 @@ where
|
||||
self.class.to_html(class);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
self.class.hydrate::<FROM_SERVER>(el)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
self.class.build(el)
|
||||
}
|
||||
|
||||
@@ -75,12 +79,14 @@ where
|
||||
fn into_cloneable(self) -> Self::Cloneable {
|
||||
Class {
|
||||
class: self.class.into_cloneable(),
|
||||
rndr: self.rndr,
|
||||
}
|
||||
}
|
||||
|
||||
fn into_cloneable_owned(self) -> Self::CloneableOwned {
|
||||
Class {
|
||||
class: self.class.into_cloneable_owned(),
|
||||
rndr: self.rndr,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,17 +97,19 @@ where
|
||||
async fn resolve(self) -> Self::AsyncOutput {
|
||||
Class {
|
||||
class: self.class.resolve().await,
|
||||
rndr: self.rndr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> NextAttribute for Class<C>
|
||||
impl<C, R> NextAttribute<R> for Class<C, R>
|
||||
where
|
||||
C: IntoClass,
|
||||
C: IntoClass<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type Output<NewAttr: Attribute> = (Self, NewAttr);
|
||||
type Output<NewAttr: Attribute<R>> = (Self, NewAttr);
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
new_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr> {
|
||||
@@ -109,9 +117,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> ToTemplate for Class<C>
|
||||
impl<C, R> ToTemplate for Class<C, R>
|
||||
where
|
||||
C: IntoClass,
|
||||
C: IntoClass<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
const CLASS: &'static str = C::TEMPLATE;
|
||||
|
||||
@@ -127,20 +136,20 @@ where
|
||||
}
|
||||
|
||||
/// A possible value for a CSS class.
|
||||
pub trait IntoClass: Send {
|
||||
pub trait IntoClass<R: DomRenderer>: Send {
|
||||
/// The HTML that should be included in a `<template>`.
|
||||
const TEMPLATE: &'static str = "";
|
||||
/// The minimum length of the HTML.
|
||||
const MIN_LENGTH: usize = Self::TEMPLATE.len();
|
||||
|
||||
/// The type after all async data have resolved.
|
||||
type AsyncOutput: IntoClass;
|
||||
type AsyncOutput: IntoClass<R>;
|
||||
/// The view state retained between building and rebuilding.
|
||||
type State;
|
||||
/// An equivalent value that can be cloned.
|
||||
type Cloneable: IntoClass + Clone;
|
||||
type Cloneable: IntoClass<R> + Clone;
|
||||
/// An equivalent value that can be cloned and is `'static`.
|
||||
type CloneableOwned: IntoClass + Clone + 'static;
|
||||
type CloneableOwned: IntoClass<R> + Clone + 'static;
|
||||
|
||||
/// The estimated length of the HTML.
|
||||
fn html_len(&self) -> usize;
|
||||
@@ -154,13 +163,10 @@ pub trait IntoClass: Send {
|
||||
|
||||
/// 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: &crate::renderer::types::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: &crate::renderer::types::Element) -> Self::State;
|
||||
fn build(self, el: &R::Element) -> Self::State;
|
||||
|
||||
/// Updates the value.
|
||||
fn rebuild(self, state: &mut Self::State);
|
||||
@@ -180,9 +186,12 @@ pub trait IntoClass: Send {
|
||||
fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;
|
||||
}
|
||||
|
||||
impl<'a> IntoClass for &'a str {
|
||||
impl<'a, R> IntoClass<R> for &'a str
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::Element, Self);
|
||||
type State = (R::Element, Self);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Arc<str>;
|
||||
|
||||
@@ -194,25 +203,22 @@ impl<'a> IntoClass for &'a str {
|
||||
class.push_str(self);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
if !FROM_SERVER {
|
||||
Rndr::set_attribute(el, "class", self);
|
||||
R::set_attribute(el, "class", self);
|
||||
}
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
Rndr::set_attribute(el, "class", self);
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
R::set_attribute(el, "class", self);
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let (el, prev) = state;
|
||||
if self != *prev {
|
||||
Rndr::set_attribute(el, "class", self);
|
||||
R::set_attribute(el, "class", self);
|
||||
}
|
||||
*prev = self;
|
||||
}
|
||||
@@ -232,9 +238,12 @@ impl<'a> IntoClass for &'a str {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoClass for String {
|
||||
impl<R> IntoClass<R> for String
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::Element, Self);
|
||||
type State = (R::Element, Self);
|
||||
type Cloneable = Arc<str>;
|
||||
type CloneableOwned = Arc<str>;
|
||||
|
||||
@@ -243,28 +252,25 @@ impl IntoClass for String {
|
||||
}
|
||||
|
||||
fn to_html(self, class: &mut String) {
|
||||
IntoClass::to_html(self.as_str(), class);
|
||||
IntoClass::<R>::to_html(self.as_str(), class);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
if !FROM_SERVER {
|
||||
Rndr::set_attribute(el, "class", &self);
|
||||
R::set_attribute(el, "class", &self);
|
||||
}
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
Rndr::set_attribute(el, "class", &self);
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
R::set_attribute(el, "class", &self);
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let (el, prev) = state;
|
||||
if self != *prev {
|
||||
Rndr::set_attribute(el, "class", &self);
|
||||
R::set_attribute(el, "class", &self);
|
||||
}
|
||||
*prev = self;
|
||||
}
|
||||
@@ -284,9 +290,12 @@ impl IntoClass for String {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoClass for Arc<str> {
|
||||
impl<R> IntoClass<R> for Arc<str>
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::Element, Self);
|
||||
type State = (R::Element, Self);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
@@ -295,28 +304,25 @@ impl IntoClass for Arc<str> {
|
||||
}
|
||||
|
||||
fn to_html(self, class: &mut String) {
|
||||
IntoClass::to_html(self.as_ref(), class);
|
||||
IntoClass::<R>::to_html(self.as_ref(), class);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
if !FROM_SERVER {
|
||||
Rndr::set_attribute(el, "class", &self);
|
||||
R::set_attribute(el, "class", &self);
|
||||
}
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
Rndr::set_attribute(el, "class", &self);
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
R::set_attribute(el, "class", &self);
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let (el, prev) = state;
|
||||
if !Arc::ptr_eq(&self, prev) {
|
||||
Rndr::set_attribute(el, "class", &self);
|
||||
R::set_attribute(el, "class", &self);
|
||||
}
|
||||
*prev = self;
|
||||
}
|
||||
@@ -336,9 +342,12 @@ impl IntoClass for Arc<str> {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoClass for (&'static str, bool) {
|
||||
impl<R> IntoClass<R> for (&'static str, bool)
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::ClassList, bool);
|
||||
type State = (R::ClassList, bool);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
@@ -353,23 +362,20 @@ impl IntoClass for (&'static str, bool) {
|
||||
}
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
let (name, include) = self;
|
||||
let class_list = Rndr::class_list(el);
|
||||
let class_list = R::class_list(el);
|
||||
if !FROM_SERVER && include {
|
||||
Rndr::add_class(&class_list, name);
|
||||
R::add_class(&class_list, name);
|
||||
}
|
||||
(class_list, self.1)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
let (name, include) = self;
|
||||
let class_list = Rndr::class_list(el);
|
||||
let class_list = R::class_list(el);
|
||||
if include {
|
||||
Rndr::add_class(&class_list, name);
|
||||
R::add_class(&class_list, name);
|
||||
}
|
||||
(class_list, self.1)
|
||||
}
|
||||
@@ -379,9 +385,9 @@ impl IntoClass for (&'static str, bool) {
|
||||
let (class_list, prev_include) = state;
|
||||
if include != *prev_include {
|
||||
if include {
|
||||
Rndr::add_class(class_list, name);
|
||||
R::add_class(class_list, name);
|
||||
} else {
|
||||
Rndr::remove_class(class_list, name);
|
||||
R::remove_class(class_list, name);
|
||||
}
|
||||
}
|
||||
*prev_include = include;
|
||||
@@ -403,7 +409,11 @@ impl IntoClass for (&'static str, bool) {
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<const V: &'static str> IntoClass for crate::view::static_types::Static<V> {
|
||||
impl<R, const V: &'static str> IntoClass<R>
|
||||
for crate::view::static_types::Static<V>
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
const TEMPLATE: &'static str = V;
|
||||
|
||||
type AsyncOutput = Self;
|
||||
@@ -423,14 +433,11 @@ impl<const V: &'static str> IntoClass for crate::view::static_types::Static<V> {
|
||||
class.push_str(V);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
_el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, _el: &R::Element) -> Self::State {
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
Rndr::set_attribute(el, "class", V);
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
R::set_attribute(el, "class", V);
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::attribute::{Attribute, NextAttribute};
|
||||
use crate::{
|
||||
prelude::AddAnyAttr,
|
||||
renderer::Renderer,
|
||||
view::{Position, ToTemplate},
|
||||
};
|
||||
use send_wrapper::SendWrapper;
|
||||
@@ -8,9 +9,10 @@ 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>
|
||||
pub trait DirectiveAttribute<T, P, D, Rndr>
|
||||
where
|
||||
D: IntoDirective<T, P>,
|
||||
D: IntoDirective<T, P, Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
/// The type of the element with the directive added.
|
||||
type Output;
|
||||
@@ -20,14 +22,15 @@ where
|
||||
fn directive(self, handler: D, param: P) -> Self::Output;
|
||||
}
|
||||
|
||||
impl<V, T, P, D> DirectiveAttribute<T, P, D> for V
|
||||
impl<V, T, P, D, Rndr> DirectiveAttribute<T, P, D, Rndr> for V
|
||||
where
|
||||
V: AddAnyAttr,
|
||||
D: IntoDirective<T, P>,
|
||||
V: AddAnyAttr<Rndr>,
|
||||
D: IntoDirective<T, P, Rndr>,
|
||||
P: Clone + 'static,
|
||||
T: 'static,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output = <Self as AddAnyAttr>::Output<Directive<T, D, P>>;
|
||||
type Output = <Self as AddAnyAttr<Rndr>>::Output<Directive<T, D, P, Rndr>>;
|
||||
|
||||
fn directive(self, handler: D, param: P) -> Self::Output {
|
||||
self.add_any_attr(directive(handler, param))
|
||||
@@ -37,22 +40,26 @@ where
|
||||
/// Adds a directive to the element, which runs some custom logic in the browser when the element
|
||||
/// is created or hydrated.
|
||||
#[inline(always)]
|
||||
pub fn directive<T, P, D>(handler: D, param: P) -> Directive<T, D, P>
|
||||
pub fn directive<T, P, D, R>(handler: D, param: P) -> Directive<T, D, P, R>
|
||||
where
|
||||
D: IntoDirective<T, P>,
|
||||
D: IntoDirective<T, P, R>,
|
||||
R: Renderer,
|
||||
{
|
||||
Directive(Some(SendWrapper::new(DirectiveInner {
|
||||
handler,
|
||||
param,
|
||||
t: PhantomData,
|
||||
rndr: PhantomData,
|
||||
})))
|
||||
}
|
||||
|
||||
/// Custom logic that runs in the browser when the element is created or hydrated.
|
||||
#[derive(Debug)]
|
||||
pub struct Directive<T, D, P>(Option<SendWrapper<DirectiveInner<T, D, P>>>);
|
||||
pub struct Directive<T, D, P, R>(
|
||||
Option<SendWrapper<DirectiveInner<T, D, P, R>>>,
|
||||
);
|
||||
|
||||
impl<T, D, P> Clone for Directive<T, D, P>
|
||||
impl<T, D, P, R> Clone for Directive<T, D, P, R>
|
||||
where
|
||||
P: Clone + 'static,
|
||||
D: Clone,
|
||||
@@ -63,13 +70,14 @@ where
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DirectiveInner<T, D, P> {
|
||||
struct DirectiveInner<T, D, P, R> {
|
||||
handler: D,
|
||||
param: P,
|
||||
t: PhantomData<T>,
|
||||
rndr: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<T, D, P> Clone for DirectiveInner<T, D, P>
|
||||
impl<T, D, P, R> Clone for DirectiveInner<T, D, P, R>
|
||||
where
|
||||
P: Clone + 'static,
|
||||
D: Clone,
|
||||
@@ -79,22 +87,24 @@ where
|
||||
handler: self.handler.clone(),
|
||||
param: self.param.clone(),
|
||||
t: PhantomData,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, P, D> Attribute for Directive<T, D, P>
|
||||
impl<T, P, D, R> Attribute<R> for Directive<T, D, P, R>
|
||||
where
|
||||
D: IntoDirective<T, P>,
|
||||
D: IntoDirective<T, P, R>,
|
||||
P: Clone + 'static, // TODO this is just here to make them cloneable
|
||||
T: 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
const MIN_LENGTH: usize = 0;
|
||||
|
||||
type AsyncOutput = Self;
|
||||
type State = crate::renderer::types::Element;
|
||||
type Cloneable = Directive<T, D::Cloneable, P>;
|
||||
type CloneableOwned = Directive<T, D::Cloneable, P>;
|
||||
type State = R::Element;
|
||||
type Cloneable = Directive<T, D::Cloneable, P, R>;
|
||||
type CloneableOwned = Directive<T, D::Cloneable, P, R>;
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
0
|
||||
@@ -109,16 +119,13 @@ where
|
||||
) {
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
let inner = self.0.expect("directive removed early").take();
|
||||
inner.handler.run(el.clone(), inner.param);
|
||||
el.clone()
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
let inner = self.0.expect("directive removed early").take();
|
||||
inner.handler.run(el.clone(), inner.param);
|
||||
el.clone()
|
||||
@@ -135,11 +142,17 @@ where
|
||||
|
||||
fn into_cloneable_owned(self) -> Self::CloneableOwned {
|
||||
let inner = self.0.map(|inner| {
|
||||
let DirectiveInner { handler, param, t } = inner.take();
|
||||
let DirectiveInner {
|
||||
handler,
|
||||
param,
|
||||
t,
|
||||
rndr,
|
||||
} = inner.take();
|
||||
SendWrapper::new(DirectiveInner {
|
||||
handler: handler.into_cloneable(),
|
||||
param,
|
||||
t,
|
||||
rndr,
|
||||
})
|
||||
});
|
||||
Directive(inner)
|
||||
@@ -158,15 +171,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D, P> NextAttribute for Directive<T, D, P>
|
||||
impl<T, D, P, R> NextAttribute<R> for Directive<T, D, P, R>
|
||||
where
|
||||
D: IntoDirective<T, P>,
|
||||
D: IntoDirective<T, P, R>,
|
||||
P: Clone + 'static,
|
||||
T: 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
type Output<NewAttr: Attribute> = (Self, NewAttr);
|
||||
type Output<NewAttr: Attribute<R>> = (Self, NewAttr);
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
new_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr> {
|
||||
@@ -174,7 +188,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D, P> ToTemplate for Directive<T, D, P> {
|
||||
impl<T, D, P, R> ToTemplate for Directive<T, D, P, R> {
|
||||
const CLASS: &'static str = "";
|
||||
|
||||
fn to_template(
|
||||
@@ -197,12 +211,12 @@ impl<T, D, P> ToTemplate for Directive<T, D, P> {
|
||||
/// # use leptos::{*, html::AnyElement};
|
||||
///
|
||||
/// // This doesn't take an attribute value
|
||||
/// fn my_directive(el: crate::renderer::types::Element) {
|
||||
/// fn my_directive(el: R::Element) {
|
||||
/// // do sth
|
||||
/// }
|
||||
///
|
||||
/// // This requires an attribute value
|
||||
/// fn another_directive(el: crate::renderer::types::Element, params: i32) {
|
||||
/// fn another_directive(el: R::Element, params: i32) {
|
||||
/// // do sth
|
||||
/// }
|
||||
///
|
||||
@@ -233,24 +247,25 @@ impl<T, D, P> ToTemplate for Directive<T, D, P> {
|
||||
/// A directive can be a function with one or two parameters.
|
||||
/// The first is the element the directive is added to and the optional
|
||||
/// second is the parameter that is provided in the attribute.
|
||||
pub trait IntoDirective<T: ?Sized, P> {
|
||||
pub trait IntoDirective<T: ?Sized, P, R: Renderer> {
|
||||
/// An equivalent to this directive that is cloneable and owned.
|
||||
type Cloneable: IntoDirective<T, P> + Clone + 'static;
|
||||
type Cloneable: IntoDirective<T, P, R> + Clone + 'static;
|
||||
|
||||
/// Calls the handler function
|
||||
fn run(&self, el: crate::renderer::types::Element, param: P);
|
||||
fn run(&self, el: R::Element, param: P);
|
||||
|
||||
/// Converts this into a cloneable type.
|
||||
fn into_cloneable(self) -> Self::Cloneable;
|
||||
}
|
||||
|
||||
impl<F> IntoDirective<(crate::renderer::types::Element,), ()> for F
|
||||
impl<F, R> IntoDirective<(R::Element,), (), R> for F
|
||||
where
|
||||
F: Fn(crate::renderer::types::Element) + 'static,
|
||||
F: Fn(R::Element) + 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
type Cloneable = Arc<dyn Fn(crate::renderer::types::Element)>;
|
||||
type Cloneable = Arc<dyn Fn(R::Element)>;
|
||||
|
||||
fn run(&self, el: crate::renderer::types::Element, _: ()) {
|
||||
fn run(&self, el: R::Element, _: ()) {
|
||||
self(el)
|
||||
}
|
||||
|
||||
@@ -259,12 +274,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDirective<(crate::renderer::types::Element,), ()>
|
||||
for Arc<dyn Fn(crate::renderer::types::Element)>
|
||||
impl<R> IntoDirective<(R::Element,), (), R> for Arc<dyn Fn(R::Element)>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type Cloneable = Arc<dyn Fn(crate::renderer::types::Element)>;
|
||||
type Cloneable = Arc<dyn Fn(R::Element)>;
|
||||
|
||||
fn run(&self, el: crate::renderer::types::Element, _: ()) {
|
||||
fn run(&self, el: R::Element, _: ()) {
|
||||
self(el)
|
||||
}
|
||||
|
||||
@@ -273,14 +289,15 @@ impl IntoDirective<(crate::renderer::types::Element,), ()>
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, P> IntoDirective<(crate::renderer::types::Element, P), P> for F
|
||||
impl<F, P, R> IntoDirective<(R::Element, P), P, R> for F
|
||||
where
|
||||
F: Fn(crate::renderer::types::Element, P) + 'static,
|
||||
F: Fn(R::Element, P) + 'static,
|
||||
P: 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
type Cloneable = Arc<dyn Fn(crate::renderer::types::Element, P)>;
|
||||
type Cloneable = Arc<dyn Fn(R::Element, P)>;
|
||||
|
||||
fn run(&self, el: crate::renderer::types::Element, param: P) {
|
||||
fn run(&self, el: R::Element, param: P) {
|
||||
self(el, param);
|
||||
}
|
||||
|
||||
@@ -289,14 +306,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> IntoDirective<(crate::renderer::types::Element, P), P>
|
||||
for Arc<dyn Fn(crate::renderer::types::Element, P)>
|
||||
impl<P, R> IntoDirective<(R::Element, P), P, R> for Arc<dyn Fn(R::Element, P)>
|
||||
where
|
||||
R: Renderer,
|
||||
P: 'static,
|
||||
{
|
||||
type Cloneable = Arc<dyn Fn(crate::renderer::types::Element, P)>;
|
||||
type Cloneable = Arc<dyn Fn(R::Element, P)>;
|
||||
|
||||
fn run(&self, el: crate::renderer::types::Element, param: P) {
|
||||
fn run(&self, el: R::Element, param: P) {
|
||||
self(el, param)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
use super::ElementWithChildren;
|
||||
use crate::html::element::{ElementType, HtmlElement};
|
||||
use std::fmt::Debug;
|
||||
use crate::{
|
||||
html::element::{CreateElement, ElementType, HtmlElement},
|
||||
renderer::{dom::Dom, Renderer},
|
||||
};
|
||||
use std::{fmt::Debug, marker::PhantomData};
|
||||
|
||||
/// Creates a custom element.
|
||||
#[track_caller]
|
||||
pub fn custom<E>(tag: E) -> HtmlElement<Custom<E>, (), ()>
|
||||
pub fn custom<E, Rndr>(tag: E) -> HtmlElement<Custom<E>, (), (), Rndr>
|
||||
where
|
||||
E: AsRef<str>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
HtmlElement {
|
||||
tag: Custom(tag),
|
||||
|
||||
rndr: PhantomData,
|
||||
attributes: (),
|
||||
children: (),
|
||||
}
|
||||
@@ -36,3 +40,16 @@ where
|
||||
}
|
||||
|
||||
impl<E> ElementWithChildren for Custom<E> {}
|
||||
|
||||
impl<E> CreateElement<Dom> for Custom<E>
|
||||
where
|
||||
E: AsRef<str>,
|
||||
{
|
||||
fn create_element(&self) -> <Dom as Renderer>::Element {
|
||||
use wasm_bindgen::intern;
|
||||
|
||||
crate::dom::document()
|
||||
.create_element(intern(self.0.as_ref()))
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::{
|
||||
event::{on, EventDescriptor},
|
||||
style::IntoStyle,
|
||||
},
|
||||
renderer::RemoveEventHandler,
|
||||
renderer::{dom::Dom, DomRenderer, RemoveEventHandler},
|
||||
};
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::Element;
|
||||
@@ -31,17 +31,17 @@ pub trait ElementExt {
|
||||
/// Adds an attribute to the element, at runtime.
|
||||
fn attr<At>(&self, attribute: At) -> At::State
|
||||
where
|
||||
At: Attribute;
|
||||
At: Attribute<Dom>;
|
||||
|
||||
/// Adds a class to the element, at runtime.
|
||||
fn class<C>(&self, class: C) -> C::State
|
||||
where
|
||||
C: IntoClass;
|
||||
C: IntoClass<Dom>;
|
||||
|
||||
/// Adds a style to the element, at runtime.
|
||||
fn style<S>(&self, style: S) -> S::State
|
||||
where
|
||||
S: IntoStyle;
|
||||
S: IntoStyle<Dom>;
|
||||
|
||||
/// Adds an event listener to the element, at runtime.
|
||||
fn on<E>(
|
||||
@@ -58,17 +58,18 @@ pub trait ElementExt {
|
||||
impl<T> ElementExt for T
|
||||
where
|
||||
T: AsRef<Element>,
|
||||
Dom: DomRenderer,
|
||||
{
|
||||
fn attr<At>(&self, attribute: At) -> At::State
|
||||
where
|
||||
At: Attribute,
|
||||
At: Attribute<Dom>,
|
||||
{
|
||||
attribute.build(self.as_ref())
|
||||
}
|
||||
|
||||
fn class<C>(&self, class: C) -> C::State
|
||||
where
|
||||
C: IntoClass,
|
||||
C: IntoClass<Dom>,
|
||||
{
|
||||
class.build(self.as_ref())
|
||||
}
|
||||
@@ -83,12 +84,12 @@ where
|
||||
E::EventType: 'static,
|
||||
E::EventType: From<JsValue>,
|
||||
{
|
||||
on::<E, _>(ev, cb).attach(self.as_ref())
|
||||
on::<E, _, Dom>(ev, cb).attach(self.as_ref())
|
||||
}
|
||||
|
||||
fn style<S>(&self, style: S) -> S::State
|
||||
where
|
||||
S: IntoStyle,
|
||||
S: IntoStyle<Dom>,
|
||||
{
|
||||
style.build(self.as_ref())
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
#[cfg(feature = "sledgehammer")]
|
||||
use crate::renderer::sledgehammer::Sledgehammer;
|
||||
use crate::{
|
||||
html::{
|
||||
attribute::{Attr, Attribute, AttributeValue},
|
||||
element::{ElementType, ElementWithChildren, HtmlElement},
|
||||
element::{
|
||||
CreateElement, ElementType, ElementWithChildren, HtmlElement,
|
||||
},
|
||||
},
|
||||
renderer::{dom::Dom, Renderer},
|
||||
view::Render,
|
||||
};
|
||||
use next_tuple::NextTuple;
|
||||
use std::fmt::Debug;
|
||||
use once_cell::unsync::Lazy;
|
||||
use std::{fmt::Debug, marker::PhantomData};
|
||||
|
||||
macro_rules! html_element_inner {
|
||||
(
|
||||
@@ -20,15 +26,15 @@ macro_rules! html_element_inner {
|
||||
paste::paste! {
|
||||
#[$meta]
|
||||
#[track_caller]
|
||||
pub fn $tag() -> HtmlElement<$struct_name, (), ()>
|
||||
pub fn $tag<Rndr>() -> HtmlElement<$struct_name, (), (), Rndr>
|
||||
where
|
||||
|
||||
Rndr: Renderer
|
||||
{
|
||||
HtmlElement {
|
||||
tag: $struct_name,
|
||||
attributes: (),
|
||||
children: (),
|
||||
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,28 +43,28 @@ macro_rules! html_element_inner {
|
||||
pub struct $struct_name;
|
||||
|
||||
// Typed attribute methods
|
||||
impl<At, Ch> HtmlElement<$struct_name, At, Ch>
|
||||
impl<At, Ch, Rndr> HtmlElement<$struct_name, At, Ch, Rndr>
|
||||
where
|
||||
At: Attribute,
|
||||
Ch: Render,
|
||||
|
||||
At: Attribute<Rndr>,
|
||||
Ch: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
$(
|
||||
#[doc = concat!("The [`", stringify!($attr), "`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/", stringify!($tag), "#", stringify!($attr) ,") attribute on `<", stringify!($tag), ">`.")]
|
||||
pub fn $attr<V>(self, value: V) -> HtmlElement <
|
||||
$struct_name,
|
||||
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>,
|
||||
Ch
|
||||
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>,
|
||||
Ch, Rndr
|
||||
>
|
||||
where
|
||||
V: AttributeValue,
|
||||
V: AttributeValue<Rndr>,
|
||||
At: NextTuple,
|
||||
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>: Attribute,
|
||||
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>: Attribute<Rndr>,
|
||||
{
|
||||
let HtmlElement { tag, children, attributes } = self;
|
||||
let HtmlElement { tag, rndr, children, attributes } = self;
|
||||
HtmlElement {
|
||||
tag,
|
||||
|
||||
rndr,
|
||||
children,
|
||||
attributes: attributes.next_tuple($crate::html::attribute::$attr(value)),
|
||||
}
|
||||
@@ -80,6 +86,28 @@ macro_rules! html_element_inner {
|
||||
}
|
||||
|
||||
impl ElementWithChildren for $struct_name {}
|
||||
|
||||
impl CreateElement<Dom> for $struct_name {
|
||||
#[track_caller]
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", fields(callsite = std::panic::Location::caller().to_string())))]
|
||||
fn create_element(&self) -> <Dom as Renderer>::Element {
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
thread_local! {
|
||||
static ELEMENT: Lazy<<Dom as Renderer>::Element> = Lazy::new(|| {
|
||||
crate::dom::document().create_element(stringify!($tag)).unwrap()
|
||||
});
|
||||
}
|
||||
ELEMENT.with(|e| e.clone_node()).unwrap().unchecked_into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "sledgehammer")]
|
||||
impl CreateElement<Sledgehammer> for $struct_name {
|
||||
fn create_element(&self) -> <Sledgehammer as Renderer>::Element {
|
||||
Sledgehammer::element(stringify!($tag))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -117,14 +145,14 @@ macro_rules! html_self_closing_elements {
|
||||
paste::paste! {
|
||||
$(
|
||||
#[$meta]
|
||||
pub fn $tag() -> HtmlElement<[<$tag:camel>], (), ()>
|
||||
pub fn $tag<Rndr>() -> HtmlElement<[<$tag:camel>], (), (), Rndr>
|
||||
where
|
||||
|
||||
Rndr: Renderer
|
||||
{
|
||||
HtmlElement {
|
||||
attributes: (),
|
||||
children: (),
|
||||
|
||||
rndr: PhantomData,
|
||||
tag: [<$tag:camel>],
|
||||
}
|
||||
}
|
||||
@@ -134,29 +162,30 @@ macro_rules! html_self_closing_elements {
|
||||
pub struct [<$tag:camel>];
|
||||
|
||||
// Typed attribute methods
|
||||
impl<At> HtmlElement<[<$tag:camel>], At, ()>
|
||||
impl<At, Rndr> HtmlElement<[<$tag:camel>], At, (), Rndr>
|
||||
where
|
||||
At: Attribute,
|
||||
|
||||
At: Attribute<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
$(
|
||||
#[doc = concat!("The [`", stringify!($attr), "`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/", stringify!($tag), "#", stringify!($attr) ,") attribute on `<", stringify!($tag), ">`.")]
|
||||
pub fn $attr<V>(self, value: V) -> HtmlElement<
|
||||
[<$tag:camel>],
|
||||
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>,
|
||||
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>,
|
||||
(),
|
||||
Rndr
|
||||
>
|
||||
where
|
||||
V: AttributeValue,
|
||||
V: AttributeValue<Rndr>,
|
||||
At: NextTuple,
|
||||
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>: Attribute,
|
||||
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>: Attribute<Rndr>,
|
||||
|
||||
{
|
||||
let HtmlElement { tag, children, attributes,
|
||||
let HtmlElement { tag, rndr, children, attributes,
|
||||
} = self;
|
||||
HtmlElement {
|
||||
tag,
|
||||
|
||||
rndr,
|
||||
children,
|
||||
attributes: attributes.next_tuple($crate::html::attribute::$attr(value)),
|
||||
}
|
||||
@@ -176,6 +205,19 @@ macro_rules! html_self_closing_elements {
|
||||
Self::TAG
|
||||
}
|
||||
}
|
||||
|
||||
impl CreateElement<Dom> for [<$tag:camel>] {
|
||||
fn create_element(&self) -> <Dom as Renderer>::Element {
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
thread_local! {
|
||||
static ELEMENT: Lazy<<Dom as Renderer>::Element> = Lazy::new(|| {
|
||||
crate::dom::document().create_element(stringify!($tag)).unwrap()
|
||||
});
|
||||
}
|
||||
ELEMENT.with(|e| e.clone_node()).unwrap().unchecked_into()
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use super::{ElementWithChildren, HtmlElement};
|
||||
use crate::{
|
||||
html::attribute::{Attribute, NextAttribute},
|
||||
renderer::Rndr,
|
||||
renderer::{DomRenderer, Renderer},
|
||||
view::add_attr::AddAnyAttr,
|
||||
};
|
||||
use std::{future::Future, sync::Arc};
|
||||
use std::{future::Future, marker::PhantomData, sync::Arc};
|
||||
|
||||
/// Returns an [`Attribute`] that sets the inner HTML of an element.
|
||||
///
|
||||
@@ -15,40 +15,47 @@ use std::{future::Future, sync::Arc};
|
||||
/// sanitize the input to avoid a cross-site scripting (XSS)
|
||||
/// vulnerability.
|
||||
#[inline(always)]
|
||||
pub fn inner_html<T>(value: T) -> InnerHtml<T>
|
||||
pub fn inner_html<T, R>(value: T) -> InnerHtml<T, R>
|
||||
where
|
||||
T: InnerHtmlValue,
|
||||
T: InnerHtmlValue<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
InnerHtml { value }
|
||||
InnerHtml {
|
||||
value,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the inner HTML of an element.
|
||||
#[derive(Debug)]
|
||||
pub struct InnerHtml<T> {
|
||||
pub struct InnerHtml<T, R> {
|
||||
value: T,
|
||||
rndr: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<T> Clone for InnerHtml<T>
|
||||
impl<T, R> Clone for InnerHtml<T, R>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
value: self.value.clone(),
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Attribute for InnerHtml<T>
|
||||
impl<T, R> Attribute<R> for InnerHtml<T, R>
|
||||
where
|
||||
T: InnerHtmlValue,
|
||||
T: InnerHtmlValue<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
const MIN_LENGTH: usize = 0;
|
||||
|
||||
type AsyncOutput = InnerHtml<T::AsyncOutput>;
|
||||
type AsyncOutput = InnerHtml<T::AsyncOutput, R>;
|
||||
type State = T::State;
|
||||
type Cloneable = InnerHtml<T::Cloneable>;
|
||||
type CloneableOwned = InnerHtml<T::CloneableOwned>;
|
||||
type Cloneable = InnerHtml<T::Cloneable, R>;
|
||||
type CloneableOwned = InnerHtml<T::CloneableOwned, R>;
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
self.value.html_len()
|
||||
@@ -66,12 +73,12 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
self.value.hydrate::<FROM_SERVER>(el)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &<R as Renderer>::Element) -> Self::State {
|
||||
self.value.build(el)
|
||||
}
|
||||
|
||||
@@ -82,12 +89,14 @@ where
|
||||
fn into_cloneable(self) -> Self::Cloneable {
|
||||
InnerHtml {
|
||||
value: self.value.into_cloneable(),
|
||||
rndr: self.rndr,
|
||||
}
|
||||
}
|
||||
|
||||
fn into_cloneable_owned(self) -> Self::CloneableOwned {
|
||||
InnerHtml {
|
||||
value: self.value.into_cloneable_owned(),
|
||||
rndr: self.rndr,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,17 +107,19 @@ where
|
||||
async fn resolve(self) -> Self::AsyncOutput {
|
||||
InnerHtml {
|
||||
value: self.value.resolve().await,
|
||||
rndr: self.rndr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> NextAttribute for InnerHtml<T>
|
||||
impl<T, R> NextAttribute<R> for InnerHtml<T, R>
|
||||
where
|
||||
T: InnerHtmlValue,
|
||||
T: InnerHtmlValue<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type Output<NewAttr: Attribute> = (Self, NewAttr);
|
||||
type Output<NewAttr: Attribute<R>> = (Self, NewAttr);
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
new_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr> {
|
||||
@@ -117,11 +128,11 @@ where
|
||||
}
|
||||
|
||||
/// Sets the inner HTML of an element.
|
||||
pub trait InnerHtmlAttribute<T>
|
||||
pub trait InnerHtmlAttribute<T, Rndr>
|
||||
where
|
||||
T: InnerHtmlValue,
|
||||
|
||||
Self: Sized + AddAnyAttr,
|
||||
T: InnerHtmlValue<Rndr>,
|
||||
Rndr: DomRenderer,
|
||||
Self: Sized + AddAnyAttr<Rndr>,
|
||||
{
|
||||
/// Sets the inner HTML of this element.
|
||||
///
|
||||
@@ -134,36 +145,38 @@ where
|
||||
fn inner_html(
|
||||
self,
|
||||
value: T,
|
||||
) -> <Self as AddAnyAttr>::Output<InnerHtml<T>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<InnerHtml<T, Rndr>> {
|
||||
self.add_any_attr(inner_html(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E, At> InnerHtmlAttribute<T> for HtmlElement<E, At, ()>
|
||||
impl<T, E, At, Rndr> InnerHtmlAttribute<T, Rndr>
|
||||
for HtmlElement<E, At, (), Rndr>
|
||||
where
|
||||
Self: AddAnyAttr,
|
||||
Self: AddAnyAttr<Rndr>,
|
||||
E: ElementWithChildren,
|
||||
At: Attribute,
|
||||
T: InnerHtmlValue,
|
||||
At: Attribute<Rndr>,
|
||||
T: InnerHtmlValue<Rndr>,
|
||||
Rndr: DomRenderer,
|
||||
{
|
||||
fn inner_html(
|
||||
self,
|
||||
value: T,
|
||||
) -> <Self as AddAnyAttr>::Output<InnerHtml<T>> {
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<InnerHtml<T, Rndr>> {
|
||||
self.add_any_attr(inner_html(value))
|
||||
}
|
||||
}
|
||||
|
||||
/// A possible value for [`InnerHtml`].
|
||||
pub trait InnerHtmlValue: Send {
|
||||
pub trait InnerHtmlValue<R: DomRenderer>: Send {
|
||||
/// The type after all async data have resolved.
|
||||
type AsyncOutput: InnerHtmlValue;
|
||||
type AsyncOutput: InnerHtmlValue<R>;
|
||||
/// The view state retained between building and rebuilding.
|
||||
type State;
|
||||
/// An equivalent value that can be cloned.
|
||||
type Cloneable: InnerHtmlValue + Clone;
|
||||
type Cloneable: InnerHtmlValue<R> + Clone;
|
||||
/// An equivalent value that can be cloned and is `'static`.
|
||||
type CloneableOwned: InnerHtmlValue + Clone + 'static;
|
||||
type CloneableOwned: InnerHtmlValue<R> + Clone + 'static;
|
||||
|
||||
/// The estimated length of the HTML.
|
||||
fn html_len(&self) -> usize;
|
||||
@@ -176,13 +189,10 @@ pub trait InnerHtmlValue: Send {
|
||||
|
||||
/// 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: &crate::renderer::types::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: &crate::renderer::types::Element) -> Self::State;
|
||||
fn build(self, el: &R::Element) -> Self::State;
|
||||
|
||||
/// Updates the value.
|
||||
fn rebuild(self, state: &mut Self::State);
|
||||
@@ -202,9 +212,12 @@ pub trait InnerHtmlValue: Send {
|
||||
fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;
|
||||
}
|
||||
|
||||
impl InnerHtmlValue for String {
|
||||
impl<R> InnerHtmlValue<R> for String
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::Element, Self);
|
||||
type State = (R::Element, Self);
|
||||
type Cloneable = Arc<str>;
|
||||
type CloneableOwned = Arc<str>;
|
||||
|
||||
@@ -220,22 +233,22 @@ impl InnerHtmlValue for String {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
if !FROM_SERVER {
|
||||
Rndr::set_inner_html(el, &self);
|
||||
R::set_inner_html(el, &self);
|
||||
}
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
Rndr::set_inner_html(el, &self);
|
||||
fn build(self, el: &<R as Renderer>::Element) -> Self::State {
|
||||
R::set_inner_html(el, &self);
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
if self != state.1 {
|
||||
Rndr::set_inner_html(&state.0, &self);
|
||||
R::set_inner_html(&state.0, &self);
|
||||
state.1 = self;
|
||||
}
|
||||
}
|
||||
@@ -255,9 +268,12 @@ impl InnerHtmlValue for String {
|
||||
}
|
||||
}
|
||||
|
||||
impl InnerHtmlValue for Arc<str> {
|
||||
impl<R> InnerHtmlValue<R> for Arc<str>
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::Element, Self);
|
||||
type State = (R::Element, Self);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
@@ -273,22 +289,22 @@ impl InnerHtmlValue for Arc<str> {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
if !FROM_SERVER {
|
||||
Rndr::set_inner_html(el, &self);
|
||||
R::set_inner_html(el, &self);
|
||||
}
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
Rndr::set_inner_html(el, &self);
|
||||
fn build(self, el: &<R as Renderer>::Element) -> Self::State {
|
||||
R::set_inner_html(el, &self);
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
if !Arc::ptr_eq(&self, &state.1) {
|
||||
Rndr::set_inner_html(&state.0, &self);
|
||||
R::set_inner_html(&state.0, &self);
|
||||
state.1 = self;
|
||||
}
|
||||
}
|
||||
@@ -308,9 +324,12 @@ impl InnerHtmlValue for Arc<str> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> InnerHtmlValue for &'a str {
|
||||
impl<'a, R> InnerHtmlValue<R> for &'a str
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::Element, Self);
|
||||
type State = (R::Element, Self);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Arc<str>;
|
||||
|
||||
@@ -326,22 +345,22 @@ impl<'a> InnerHtmlValue for &'a str {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
if !FROM_SERVER {
|
||||
Rndr::set_inner_html(el, self);
|
||||
R::set_inner_html(el, self);
|
||||
}
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
Rndr::set_inner_html(el, self);
|
||||
fn build(self, el: &<R as Renderer>::Element) -> Self::State {
|
||||
R::set_inner_html(el, self);
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
if self != state.1 {
|
||||
Rndr::set_inner_html(&state.0, self);
|
||||
R::set_inner_html(&state.0, self);
|
||||
state.1 = self;
|
||||
}
|
||||
}
|
||||
@@ -361,12 +380,13 @@ impl<'a> InnerHtmlValue for &'a str {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> InnerHtmlValue for Option<T>
|
||||
impl<T, R> InnerHtmlValue<R> for Option<T>
|
||||
where
|
||||
T: InnerHtmlValue,
|
||||
T: InnerHtmlValue<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::Element, Option<T::State>);
|
||||
type State = (R::Element, Option<T::State>);
|
||||
type Cloneable = Option<T::Cloneable>;
|
||||
type CloneableOwned = Option<T::CloneableOwned>;
|
||||
|
||||
@@ -387,12 +407,12 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
(el.clone(), self.map(|n| n.hydrate::<FROM_SERVER>(el)))
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &<R as Renderer>::Element) -> Self::State {
|
||||
(el.clone(), self.map(|n| n.build(el)))
|
||||
}
|
||||
|
||||
@@ -400,7 +420,7 @@ where
|
||||
let new_state = match (self, &mut state.1) {
|
||||
(None, None) => None,
|
||||
(None, Some(_)) => {
|
||||
Rndr::set_inner_html(&state.0, "");
|
||||
R::set_inner_html(&state.0, "");
|
||||
Some(None)
|
||||
}
|
||||
(Some(new), None) => Some(Some(new.build(&state.0))),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
html::attribute::Attribute,
|
||||
hydration::Cursor,
|
||||
renderer::{CastFrom, Rndr},
|
||||
renderer::{CastFrom, Renderer},
|
||||
ssr::StreamBuilder,
|
||||
view::{
|
||||
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
|
||||
@@ -13,7 +13,7 @@ use const_str_slice_concat::{
|
||||
};
|
||||
use futures::future::join;
|
||||
use next_tuple::NextTuple;
|
||||
use std::ops::Deref;
|
||||
use std::{marker::PhantomData, ops::Deref};
|
||||
|
||||
mod custom;
|
||||
mod element_ext;
|
||||
@@ -27,26 +27,29 @@ pub use inner_html::*;
|
||||
|
||||
/// The typed representation of an HTML element.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct HtmlElement<E, At, Ch> {
|
||||
pub struct HtmlElement<E, At, Ch, Rndr> {
|
||||
pub(crate) tag: E,
|
||||
pub(crate) rndr: PhantomData<Rndr>,
|
||||
pub(crate) attributes: At,
|
||||
pub(crate) children: Ch,
|
||||
}
|
||||
|
||||
impl<E: Clone, At: Clone, Ch: Clone> Clone for HtmlElement<E, At, Ch> {
|
||||
impl<E: Clone, At: Clone, Ch: Clone, Rndr> Clone
|
||||
for HtmlElement<E, At, Ch, Rndr>
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
HtmlElement {
|
||||
tag: self.tag.clone(),
|
||||
|
||||
rndr: PhantomData,
|
||||
attributes: self.attributes.clone(),
|
||||
children: self.children.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Copy, At: Copy, Ch: Copy> Copy for HtmlElement<E, At, Ch> {}
|
||||
impl<E: Copy, At: Copy, Ch: Copy, Rndr> Copy for HtmlElement<E, At, Ch, Rndr> {}
|
||||
|
||||
/*impl<E, At, Ch> ElementType for HtmlElement<E, At, Ch>
|
||||
/*impl<E, At, Ch, Rndr> ElementType for HtmlElement<E, At, Ch, Rndr>
|
||||
where
|
||||
E: ElementType,
|
||||
{
|
||||
@@ -61,42 +64,48 @@ where
|
||||
}
|
||||
}*/
|
||||
|
||||
impl<E, At, Ch, NewChild> ElementChild<NewChild> for HtmlElement<E, At, Ch>
|
||||
impl<E, At, Ch, NewChild, Rndr> ElementChild<Rndr, NewChild>
|
||||
for HtmlElement<E, At, Ch, Rndr>
|
||||
where
|
||||
E: ElementWithChildren,
|
||||
Ch: Render + NextTuple,
|
||||
<Ch as NextTuple>::Output<NewChild>: Render,
|
||||
|
||||
NewChild: Render,
|
||||
Ch: Render<Rndr> + NextTuple,
|
||||
<Ch as NextTuple>::Output<NewChild>: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
NewChild: Render<Rndr>,
|
||||
{
|
||||
type Output = HtmlElement<E, At, <Ch as NextTuple>::Output<NewChild>>;
|
||||
type Output = HtmlElement<E, At, <Ch as NextTuple>::Output<NewChild>, Rndr>;
|
||||
|
||||
fn child(self, child: NewChild) -> Self::Output {
|
||||
let HtmlElement {
|
||||
tag,
|
||||
|
||||
rndr,
|
||||
attributes,
|
||||
children,
|
||||
} = self;
|
||||
HtmlElement {
|
||||
tag,
|
||||
|
||||
rndr,
|
||||
attributes,
|
||||
children: children.next_tuple(child),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, At, Ch> AddAnyAttr for HtmlElement<E, At, Ch>
|
||||
impl<E, At, Ch, Rndr> AddAnyAttr<Rndr> for HtmlElement<E, At, Ch, Rndr>
|
||||
where
|
||||
E: ElementType + Send,
|
||||
At: Attribute + Send,
|
||||
Ch: RenderHtml + Send,
|
||||
E: ElementType + CreateElement<Rndr> + Send,
|
||||
At: Attribute<Rndr> + Send,
|
||||
Ch: RenderHtml<Rndr> + Send,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> =
|
||||
HtmlElement<E, <At as NextAttribute>::Output<SomeNewAttr>, Ch>;
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> = HtmlElement<
|
||||
E,
|
||||
<At as NextAttribute<Rndr>>::Output<SomeNewAttr>,
|
||||
Ch,
|
||||
Rndr,
|
||||
>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr> {
|
||||
@@ -104,19 +113,22 @@ where
|
||||
tag,
|
||||
attributes,
|
||||
children,
|
||||
rndr,
|
||||
} = self;
|
||||
HtmlElement {
|
||||
tag,
|
||||
attributes: attributes.add_any_attr(attr),
|
||||
children,
|
||||
rndr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a child to the element.
|
||||
pub trait ElementChild<NewChild>
|
||||
pub trait ElementChild<Rndr, NewChild>
|
||||
where
|
||||
NewChild: Render,
|
||||
NewChild: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
/// The type of the element, with the child added.
|
||||
type Output;
|
||||
@@ -151,20 +163,27 @@ pub trait HasElementType {
|
||||
|
||||
pub(crate) trait ElementWithChildren {}
|
||||
|
||||
impl<E, At, Ch> HasElementType for HtmlElement<E, At, Ch>
|
||||
/// Creates an element.
|
||||
pub trait CreateElement<R: Renderer> {
|
||||
/// Creates an element.
|
||||
fn create_element(&self) -> R::Element;
|
||||
}
|
||||
|
||||
impl<E, At, Ch, Rndr> HasElementType for HtmlElement<E, At, Ch, Rndr>
|
||||
where
|
||||
E: ElementType,
|
||||
{
|
||||
type ElementType = E::Output;
|
||||
}
|
||||
|
||||
impl<E, At, Ch> Render for HtmlElement<E, At, Ch>
|
||||
impl<E, At, Ch, Rndr> Render<Rndr> for HtmlElement<E, At, Ch, Rndr>
|
||||
where
|
||||
E: ElementType,
|
||||
At: Attribute,
|
||||
Ch: Render,
|
||||
E: ElementType + CreateElement<Rndr>,
|
||||
At: Attribute<Rndr>,
|
||||
Ch: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type State = ElementState<At::State, Ch::State>;
|
||||
type State = ElementState<At::State, Ch::State, Rndr>;
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let ElementState {
|
||||
@@ -177,7 +196,7 @@ where
|
||||
}
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let el = Rndr::create_element(self.tag.tag());
|
||||
let el = Rndr::create_element(self.tag);
|
||||
|
||||
let attrs = self.attributes.build(&el);
|
||||
let children = if E::SELF_CLOSING {
|
||||
@@ -191,17 +210,19 @@ where
|
||||
el,
|
||||
attrs,
|
||||
children,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, At, Ch> RenderHtml for HtmlElement<E, At, Ch>
|
||||
impl<E, At, Ch, Rndr> RenderHtml<Rndr> for HtmlElement<E, At, Ch, Rndr>
|
||||
where
|
||||
E: ElementType + Send,
|
||||
At: Attribute + Send,
|
||||
Ch: RenderHtml + Send,
|
||||
E: ElementType + CreateElement<Rndr> + Send,
|
||||
At: Attribute<Rndr> + Send,
|
||||
Ch: RenderHtml<Rndr> + Send,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type AsyncOutput = HtmlElement<E, At::AsyncOutput, Ch::AsyncOutput>;
|
||||
type AsyncOutput = HtmlElement<E, At::AsyncOutput, Ch::AsyncOutput, Rndr>;
|
||||
|
||||
const MIN_LENGTH: usize = if E::SELF_CLOSING {
|
||||
3 // < ... />
|
||||
@@ -226,7 +247,7 @@ where
|
||||
join(self.attributes.resolve(), self.children.resolve()).await;
|
||||
HtmlElement {
|
||||
tag: self.tag,
|
||||
|
||||
rndr: PhantomData,
|
||||
attributes,
|
||||
children,
|
||||
}
|
||||
@@ -329,7 +350,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
// non-Static custom elements need special support in templates
|
||||
@@ -344,8 +365,7 @@ where
|
||||
} else if curr_position != Position::Current {
|
||||
cursor.sibling();
|
||||
}
|
||||
let el = crate::renderer::types::Element::cast_from(cursor.current())
|
||||
.unwrap();
|
||||
let el = Rndr::Element::cast_from(cursor.current()).unwrap();
|
||||
|
||||
let attrs = self.attributes.hydrate::<FROM_SERVER>(&el);
|
||||
|
||||
@@ -358,26 +378,23 @@ where
|
||||
};
|
||||
|
||||
// go to next sibling
|
||||
cursor.set(
|
||||
<crate::renderer::types::Element as AsRef<
|
||||
crate::renderer::types::Node,
|
||||
>>::as_ref(&el)
|
||||
.clone(),
|
||||
);
|
||||
cursor.set(el.as_ref().clone());
|
||||
position.set(Position::NextChild);
|
||||
|
||||
ElementState {
|
||||
el,
|
||||
attrs,
|
||||
children,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders an [`Attribute`] (which can be one or more HTML attributes) into an HTML buffer.
|
||||
pub fn attributes_to_html<At>(attr: At, buf: &mut String) -> String
|
||||
pub fn attributes_to_html<At, R>(attr: At, buf: &mut String) -> String
|
||||
where
|
||||
At: Attribute,
|
||||
At: Attribute<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
// `class` and `style` are created first, and pushed later
|
||||
// this is because they can be filled by a mixture of values that include
|
||||
@@ -412,38 +429,36 @@ where
|
||||
}
|
||||
|
||||
/// The retained view state for an HTML element.
|
||||
pub struct ElementState<At, Ch> {
|
||||
pub(crate) el: crate::renderer::types::Element,
|
||||
pub struct ElementState<At, Ch, R: Renderer> {
|
||||
pub(crate) el: R::Element,
|
||||
pub(crate) attrs: At,
|
||||
pub(crate) children: Option<Ch>,
|
||||
rndr: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<At, Ch> Deref for ElementState<At, Ch> {
|
||||
type Target = crate::renderer::types::Element;
|
||||
impl<At, Ch, R: Renderer> Deref for ElementState<At, Ch, R> {
|
||||
type Target = R::Element;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.el
|
||||
}
|
||||
}
|
||||
|
||||
impl<At, Ch> Mountable for ElementState<At, Ch> {
|
||||
impl<At, Ch, R> Mountable<R> for ElementState<At, Ch, R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
Rndr::remove(self.el.as_ref());
|
||||
R::remove(self.el.as_ref());
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
) {
|
||||
Rndr::insert_node(parent, self.el.as_ref(), marker);
|
||||
fn mount(&mut self, parent: &R::Element, marker: Option<&R::Node>) {
|
||||
R::insert_node(parent, self.el.as_ref(), marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
if let Some(parent) = Rndr::get_parent(self.el.as_ref()) {
|
||||
if let Some(element) =
|
||||
crate::renderer::types::Element::cast_from(parent)
|
||||
{
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
if let Some(parent) = R::get_parent(self.el.as_ref()) {
|
||||
if let Some(element) = R::Element::cast_from(parent) {
|
||||
child.mount(&element, Some(self.el.as_ref()));
|
||||
return true;
|
||||
}
|
||||
@@ -452,11 +467,12 @@ impl<At, Ch> Mountable for ElementState<At, Ch> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, At, Ch> ToTemplate for HtmlElement<E, At, Ch>
|
||||
impl<E, At, Ch, Rndr> ToTemplate for HtmlElement<E, At, Ch, Rndr>
|
||||
where
|
||||
E: ElementType,
|
||||
At: Attribute + ToTemplate,
|
||||
Ch: Render + ToTemplate,
|
||||
At: Attribute<Rndr> + ToTemplate,
|
||||
Ch: Render<Rndr> + ToTemplate,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
const TEMPLATE: &'static str = str_from_buffer(&const_concat(&[
|
||||
"<",
|
||||
@@ -539,7 +555,7 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
||||
#[cfg(all(test, feature = "testing"))]
|
||||
mod tests {
|
||||
#[cfg(feature = "nightly")]
|
||||
@@ -598,4 +614,3 @@ mod tests {
|
||||
assert_eq!(html.len(), allocated_len);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
html::attribute::Attribute,
|
||||
renderer::{CastFrom, RemoveEventHandler, Rndr},
|
||||
renderer::{CastFrom, DomRenderer, RemoveEventHandler},
|
||||
view::{Position, ToTemplate},
|
||||
};
|
||||
use send_wrapper::SendWrapper;
|
||||
@@ -51,12 +51,13 @@ where
|
||||
}
|
||||
|
||||
/// An event listener with a typed event target.
|
||||
pub struct Targeted<E, T> {
|
||||
pub struct Targeted<E, T, R> {
|
||||
event: E,
|
||||
el_ty: PhantomData<T>,
|
||||
rndr: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<E, T> Targeted<E, T> {
|
||||
impl<E, T, R> Targeted<E, T, R> {
|
||||
/// Returns the inner event.
|
||||
pub fn into_inner(self) -> E {
|
||||
self.event
|
||||
@@ -65,17 +66,17 @@ impl<E, T> Targeted<E, T> {
|
||||
/// Returns the event's target, as an HTML element of the correct type.
|
||||
pub fn target(&self) -> T
|
||||
where
|
||||
T: CastFrom<crate::renderer::types::Element>,
|
||||
|
||||
crate::renderer::types::Event: From<E>,
|
||||
T: CastFrom<R::Element>,
|
||||
R: DomRenderer,
|
||||
R::Event: From<E>,
|
||||
E: Clone,
|
||||
{
|
||||
let ev = crate::renderer::types::Event::from(self.event.clone());
|
||||
Rndr::event_target(&ev)
|
||||
let ev = R::Event::from(self.event.clone());
|
||||
R::event_target(&ev)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, T> Deref for Targeted<E, T> {
|
||||
impl<E, T, R> Deref for Targeted<E, T, R> {
|
||||
type Target = E;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@@ -83,60 +84,64 @@ impl<E, T> Deref for Targeted<E, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, T> DerefMut for Targeted<E, T> {
|
||||
impl<E, T, R> DerefMut for Targeted<E, T, R> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.event
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, T> From<E> for Targeted<E, T> {
|
||||
impl<E, T, R> From<E> for Targeted<E, T, R> {
|
||||
fn from(event: E) -> Self {
|
||||
Targeted {
|
||||
event,
|
||||
el_ty: PhantomData,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an [`Attribute`] that will add an event listener to an element.
|
||||
pub fn on<E, F>(event: E, cb: F) -> On<E, F>
|
||||
pub fn on<E, F, R>(event: E, cb: F) -> On<E, F, R>
|
||||
where
|
||||
F: FnMut(E::EventType) + 'static,
|
||||
E: EventDescriptor + Send + 'static,
|
||||
E::EventType: 'static,
|
||||
E::EventType: From<crate::renderer::types::Event>,
|
||||
E::EventType: From<R::Event>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
On {
|
||||
event,
|
||||
cb: Some(SendWrapper::new(cb)),
|
||||
ty: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an [`Attribute`] that will add an event listener with a typed target to an element.
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn on_target<E, T, F>(
|
||||
pub fn on_target<E, T, R, F>(
|
||||
event: E,
|
||||
mut cb: F,
|
||||
) -> On<E, Box<dyn FnMut(E::EventType)>>
|
||||
) -> On<E, Box<dyn FnMut(E::EventType)>, R>
|
||||
where
|
||||
T: HasElementType,
|
||||
F: FnMut(Targeted<E::EventType, <T as HasElementType>::ElementType>)
|
||||
F: FnMut(Targeted<E::EventType, <T as HasElementType>::ElementType, R>)
|
||||
+ 'static,
|
||||
E: EventDescriptor + Send + 'static,
|
||||
E::EventType: 'static,
|
||||
|
||||
E::EventType: From<crate::renderer::types::Event>,
|
||||
R: DomRenderer,
|
||||
E::EventType: From<R::Event>,
|
||||
{
|
||||
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> {
|
||||
pub struct On<E, F, R> {
|
||||
event: E,
|
||||
cb: Option<SendWrapper<F>>,
|
||||
ty: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<E, F> Clone for On<E, F>
|
||||
impl<E, F, R> Clone for On<E, F, R>
|
||||
where
|
||||
E: Clone,
|
||||
F: Clone,
|
||||
@@ -145,33 +150,30 @@ where
|
||||
Self {
|
||||
event: self.event.clone(),
|
||||
cb: self.cb.clone(),
|
||||
ty: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, F> On<E, F>
|
||||
impl<E, F, R> On<E, F, R>
|
||||
where
|
||||
F: EventCallback<E::EventType>,
|
||||
E: EventDescriptor + Send + 'static,
|
||||
E::EventType: 'static,
|
||||
E::EventType: From<crate::renderer::types::Event>,
|
||||
R: DomRenderer,
|
||||
E::EventType: From<R::Event>,
|
||||
{
|
||||
/// Attaches the event listener to the element.
|
||||
pub fn attach(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> RemoveEventHandler<crate::renderer::types::Element> {
|
||||
fn attach_inner(
|
||||
el: &crate::renderer::types::Element,
|
||||
cb: Box<dyn FnMut(crate::renderer::types::Event)>,
|
||||
pub fn attach(self, el: &R::Element) -> RemoveEventHandler<R::Element> {
|
||||
fn attach_inner<R: DomRenderer>(
|
||||
el: &R::Element,
|
||||
cb: Box<dyn FnMut(R::Event)>,
|
||||
name: Cow<'static, str>,
|
||||
delegation_key: Option<Cow<'static, str>>,
|
||||
) -> RemoveEventHandler<crate::renderer::types::Element> {
|
||||
) -> RemoveEventHandler<R::Element> {
|
||||
match delegation_key {
|
||||
None => Rndr::add_event_listener(el, &name, cb),
|
||||
Some(key) => {
|
||||
Rndr::add_event_listener_delegated(el, name, key, cb)
|
||||
}
|
||||
None => R::add_event_listener(el, &name, cb),
|
||||
Some(key) => R::add_event_listener_delegated(el, name, key, cb),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +182,7 @@ where
|
||||
#[cfg(feature = "tracing")]
|
||||
let span = tracing::Span::current();
|
||||
|
||||
let cb = Box::new(move |ev: crate::renderer::types::Event| {
|
||||
let cb = Box::new(move |ev: R::Event| {
|
||||
#[cfg(all(debug_assertions, feature = "reactive_graph"))]
|
||||
let _rx_guard =
|
||||
reactive_graph::diagnostics::SpecialNonReactiveZone::enter();
|
||||
@@ -189,9 +191,9 @@ where
|
||||
|
||||
let ev = E::EventType::from(ev);
|
||||
cb.invoke(ev);
|
||||
}) as Box<dyn FnMut(crate::renderer::types::Event)>;
|
||||
}) as Box<dyn FnMut(R::Event)>;
|
||||
|
||||
attach_inner(
|
||||
attach_inner::<R>(
|
||||
el,
|
||||
cb,
|
||||
self.event.name(),
|
||||
@@ -201,32 +203,30 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, F> Debug for On<E, F>
|
||||
impl<E, F, R> Debug for On<E, F, R>
|
||||
where
|
||||
E: Debug,
|
||||
R: DomRenderer,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("On").field(&self.event).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, F> Attribute for On<E, F>
|
||||
impl<E, F, R> Attribute<R> for On<E, F, R>
|
||||
where
|
||||
F: EventCallback<E::EventType>,
|
||||
E: EventDescriptor + Send + 'static,
|
||||
E::EventType: 'static,
|
||||
|
||||
E::EventType: From<crate::renderer::types::Event>,
|
||||
R: DomRenderer,
|
||||
E::EventType: From<R::Event>,
|
||||
{
|
||||
const MIN_LENGTH: usize = 0;
|
||||
type AsyncOutput = Self;
|
||||
// a function that can be called once to remove the event listener
|
||||
type State = (
|
||||
crate::renderer::types::Element,
|
||||
Option<RemoveEventHandler<crate::renderer::types::Element>>,
|
||||
);
|
||||
type Cloneable = On<E, SharedEventCallback<E::EventType>>;
|
||||
type CloneableOwned = On<E, SharedEventCallback<E::EventType>>;
|
||||
type State = (R::Element, Option<RemoveEventHandler<R::Element>>);
|
||||
type Cloneable = On<E, SharedEventCallback<E::EventType>, R>;
|
||||
type CloneableOwned = On<E, SharedEventCallback<E::EventType>, R>;
|
||||
|
||||
#[inline(always)]
|
||||
fn html_len(&self) -> usize {
|
||||
@@ -244,16 +244,13 @@ where
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
let cleanup = self.attach(el);
|
||||
(el.clone(), Some(cleanup))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
let cleanup = self.attach(el);
|
||||
(el.clone(), Some(cleanup))
|
||||
}
|
||||
@@ -271,6 +268,7 @@ where
|
||||
On {
|
||||
cb: self.cb.map(|cb| SendWrapper::new(cb.take().into_shared())),
|
||||
event: self.event,
|
||||
ty: self.ty,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,6 +276,7 @@ where
|
||||
On {
|
||||
cb: self.cb.map(|cb| SendWrapper::new(cb.take().into_shared())),
|
||||
event: self.event,
|
||||
ty: self.ty,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,17 +293,17 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, F> NextAttribute for On<E, F>
|
||||
impl<E, F, R> NextAttribute<R> for On<E, F, R>
|
||||
where
|
||||
F: EventCallback<E::EventType>,
|
||||
E: EventDescriptor + Send + 'static,
|
||||
E::EventType: 'static,
|
||||
|
||||
E::EventType: From<crate::renderer::types::Event>,
|
||||
R: DomRenderer,
|
||||
E::EventType: From<R::Event>,
|
||||
{
|
||||
type Output<NewAttr: Attribute> = (Self, NewAttr);
|
||||
type Output<NewAttr: Attribute<R>> = (Self, NewAttr);
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
new_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr> {
|
||||
@@ -312,7 +311,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, F> ToTemplate for On<E, F> {
|
||||
impl<E, F, R> ToTemplate for On<E, F, R> {
|
||||
#[inline(always)]
|
||||
fn to_template(
|
||||
_buf: &mut String,
|
||||
@@ -405,9 +404,9 @@ impl<E: FromWasmAbi> Custom<E> {
|
||||
/// # use tachys::prelude::*;
|
||||
/// # use tachys::html;
|
||||
/// # use tachys::html::event as ev;
|
||||
/// # fn custom_event() -> impl Render {
|
||||
/// # fn custom_event() -> impl Render<Dom> {
|
||||
/// let mut non_passive_wheel = ev::Custom::new("wheel");
|
||||
/// non_passive_wheel.options_mut().set_passive(false);
|
||||
/// non_passive_wheel.options_mut().passive(false);
|
||||
///
|
||||
/// let canvas =
|
||||
/// html::element::canvas().on(non_passive_wheel, |e: ev::WheelEvent| {
|
||||
|
||||
@@ -2,26 +2,30 @@ use super::attribute::Attribute;
|
||||
use crate::{
|
||||
hydration::Cursor,
|
||||
prelude::{Render, RenderHtml},
|
||||
renderer::Renderer,
|
||||
ssr::StreamBuilder,
|
||||
view::{add_attr::AddAnyAttr, Position, PositionState},
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// An island of interactivity in an otherwise-inert HTML document.
|
||||
pub struct Island<View> {
|
||||
pub struct Island<Rndr, View> {
|
||||
component: &'static str,
|
||||
props_json: String,
|
||||
view: View,
|
||||
rndr: PhantomData<Rndr>,
|
||||
}
|
||||
const ISLAND_TAG: &str = "leptos-island";
|
||||
const ISLAND_CHILDREN_TAG: &str = "leptos-children";
|
||||
|
||||
impl<View> Island<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 {
|
||||
Island {
|
||||
component,
|
||||
props_json: String::new(),
|
||||
view,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,9 +57,10 @@ impl<View> Island<View> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<View> Render for Island<View>
|
||||
impl<Rndr, View> Render<Rndr> for Island<Rndr, View>
|
||||
where
|
||||
View: Render,
|
||||
View: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type State = View::State;
|
||||
|
||||
@@ -68,38 +73,42 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<View> AddAnyAttr for Island<View>
|
||||
impl<Rndr, View> AddAnyAttr<Rndr> for Island<Rndr, View>
|
||||
where
|
||||
View: RenderHtml,
|
||||
View: RenderHtml<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> =
|
||||
Island<<View as AddAnyAttr>::Output<SomeNewAttr>>;
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> =
|
||||
Island<Rndr, <View as AddAnyAttr<Rndr>>::Output<SomeNewAttr>>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
let Island {
|
||||
component,
|
||||
props_json,
|
||||
view,
|
||||
rndr,
|
||||
} = self;
|
||||
Island {
|
||||
component,
|
||||
props_json,
|
||||
view: view.add_any_attr(attr),
|
||||
rndr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<View> RenderHtml for Island<View>
|
||||
impl<Rndr, View> RenderHtml<Rndr> for Island<Rndr, View>
|
||||
where
|
||||
View: RenderHtml,
|
||||
View: RenderHtml<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type AsyncOutput = Island<View::AsyncOutput>;
|
||||
type AsyncOutput = Island<Rndr, View::AsyncOutput>;
|
||||
|
||||
const MIN_LENGTH: usize = ISLAND_TAG.len() * 2
|
||||
+ "<>".len()
|
||||
@@ -116,11 +125,13 @@ where
|
||||
component,
|
||||
props_json,
|
||||
view,
|
||||
rndr,
|
||||
} = self;
|
||||
Island {
|
||||
component,
|
||||
props_json,
|
||||
view: view.resolve().await,
|
||||
rndr,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +178,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
if position.get() == Position::FirstChild {
|
||||
@@ -181,14 +192,18 @@ where
|
||||
}
|
||||
|
||||
/// The children that will be projected into an [`Island`].
|
||||
pub struct IslandChildren<View> {
|
||||
pub struct IslandChildren<Rndr, View> {
|
||||
view: View,
|
||||
rndr: PhantomData<Rndr>,
|
||||
}
|
||||
|
||||
impl<View> IslandChildren<View> {
|
||||
impl<Rndr, View> IslandChildren<Rndr, View> {
|
||||
/// Creates a new representation of the children.
|
||||
pub fn new(view: View) -> Self {
|
||||
IslandChildren { view }
|
||||
IslandChildren {
|
||||
view,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn open_tag(buf: &mut String) {
|
||||
@@ -204,9 +219,10 @@ impl<View> IslandChildren<View> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<View> Render for IslandChildren<View>
|
||||
impl<Rndr, View> Render<Rndr> for IslandChildren<Rndr, View>
|
||||
where
|
||||
View: Render,
|
||||
View: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type State = ();
|
||||
|
||||
@@ -215,32 +231,35 @@ where
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
}
|
||||
|
||||
impl<View> AddAnyAttr for IslandChildren<View>
|
||||
impl<Rndr, View> AddAnyAttr<Rndr> for IslandChildren<Rndr, View>
|
||||
where
|
||||
View: RenderHtml,
|
||||
View: RenderHtml<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> =
|
||||
IslandChildren<<View as AddAnyAttr>::Output<SomeNewAttr>>;
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> =
|
||||
IslandChildren<Rndr, <View as AddAnyAttr<Rndr>>::Output<SomeNewAttr>>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
let IslandChildren { view } = self;
|
||||
let IslandChildren { view, rndr } = self;
|
||||
IslandChildren {
|
||||
view: view.add_any_attr(attr),
|
||||
rndr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<View> RenderHtml for IslandChildren<View>
|
||||
impl<Rndr, View> RenderHtml<Rndr> for IslandChildren<Rndr, View>
|
||||
where
|
||||
View: RenderHtml,
|
||||
View: RenderHtml<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type AsyncOutput = IslandChildren<View::AsyncOutput>;
|
||||
type AsyncOutput = IslandChildren<Rndr, View::AsyncOutput>;
|
||||
|
||||
const MIN_LENGTH: usize = ISLAND_CHILDREN_TAG.len() * 2
|
||||
+ "<>".len()
|
||||
@@ -252,9 +271,10 @@ where
|
||||
}
|
||||
|
||||
async fn resolve(self) -> Self::AsyncOutput {
|
||||
let IslandChildren { view } = self;
|
||||
let IslandChildren { view, rndr } = self;
|
||||
IslandChildren {
|
||||
view: view.resolve().await,
|
||||
rndr,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,7 +321,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
// island children aren't hydrated
|
||||
|
||||
@@ -3,10 +3,10 @@ use crate::{
|
||||
hydration::Cursor,
|
||||
no_attrs,
|
||||
prelude::AddAnyAttr,
|
||||
renderer::{CastFrom, Rndr},
|
||||
renderer::{CastFrom, DomRenderer, Renderer},
|
||||
view::{Position, PositionState, Render, RenderHtml},
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
use std::{borrow::Cow, marker::PhantomData};
|
||||
|
||||
/// Types for HTML attributes.
|
||||
pub mod attribute;
|
||||
@@ -28,16 +28,20 @@ pub mod property;
|
||||
pub mod style;
|
||||
|
||||
/// A `<!DOCTYPE>` declaration.
|
||||
pub struct Doctype {
|
||||
pub struct Doctype<R: Renderer> {
|
||||
value: &'static str,
|
||||
rndr: PhantomData<R>,
|
||||
}
|
||||
|
||||
/// Creates a `<!DOCTYPE>`.
|
||||
pub fn doctype(value: &'static str) -> Doctype {
|
||||
Doctype { value }
|
||||
pub fn doctype<R: Renderer>(value: &'static str) -> Doctype<R> {
|
||||
Doctype {
|
||||
value,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for Doctype {
|
||||
impl<R: Renderer> Render<R> for Doctype<R> {
|
||||
type State = ();
|
||||
|
||||
fn build(self) -> Self::State {}
|
||||
@@ -45,9 +49,12 @@ impl Render for Doctype {
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
}
|
||||
|
||||
no_attrs!(Doctype);
|
||||
no_attrs!(Doctype<R>);
|
||||
|
||||
impl RenderHtml for Doctype {
|
||||
impl<R> RenderHtml<R> for Doctype<R>
|
||||
where
|
||||
R: Renderer + Send,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
const MIN_LENGTH: usize = "<!DOCTYPE html>".len();
|
||||
@@ -72,7 +79,7 @@ impl RenderHtml for Doctype {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
_cursor: &Cursor,
|
||||
_cursor: &Cursor<R>,
|
||||
_position: &PositionState,
|
||||
) -> Self::State {
|
||||
}
|
||||
@@ -90,8 +97,11 @@ impl InertElement {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for InertElement {
|
||||
type State = crate::renderer::types::Element;
|
||||
impl<Rndr> Render<Rndr> for InertElement
|
||||
where
|
||||
Rndr: DomRenderer,
|
||||
{
|
||||
type State = Rndr::Element;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
Rndr::create_element_from_html(&self.html)
|
||||
@@ -100,15 +110,18 @@ impl Render for InertElement {
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
}
|
||||
|
||||
impl AddAnyAttr for InertElement {
|
||||
type Output<SomeNewAttr: Attribute> = Self;
|
||||
impl<Rndr> AddAnyAttr<Rndr> for InertElement
|
||||
where
|
||||
Rndr: DomRenderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> = Self;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
panic!(
|
||||
"InertElement does not support adding attributes. It should only \
|
||||
@@ -117,7 +130,10 @@ impl AddAnyAttr for InertElement {
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderHtml for InertElement {
|
||||
impl<Rndr> RenderHtml<Rndr> for InertElement
|
||||
where
|
||||
Rndr: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
const MIN_LENGTH: usize = 0;
|
||||
@@ -145,7 +161,7 @@ impl RenderHtml for InertElement {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let curr_position = position.get();
|
||||
@@ -154,8 +170,7 @@ impl RenderHtml for InertElement {
|
||||
} else if curr_position != Position::Current {
|
||||
cursor.sibling();
|
||||
}
|
||||
let el = crate::renderer::types::Element::cast_from(cursor.current())
|
||||
.unwrap();
|
||||
let el = Rndr::Element::cast_from(cursor.current()).unwrap();
|
||||
position.set(Position::NextChild);
|
||||
el
|
||||
}
|
||||
|
||||
@@ -3,27 +3,30 @@ use super::{
|
||||
element::ElementType,
|
||||
};
|
||||
use crate::{
|
||||
html::element::HtmlElement, prelude::Render, view::add_attr::AddAnyAttr,
|
||||
html::element::HtmlElement, prelude::Render, renderer::Renderer,
|
||||
view::add_attr::AddAnyAttr,
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Describes a container that can be used to hold a reference to an HTML element.
|
||||
pub trait NodeRefContainer<E>: Send + Clone
|
||||
pub trait NodeRefContainer<E, Rndr>: Send + Clone
|
||||
where
|
||||
E: ElementType,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
/// Fills the container with the element.
|
||||
fn load(self, el: &crate::renderer::types::Element);
|
||||
fn load(self, el: &Rndr::Element);
|
||||
}
|
||||
|
||||
/// An [`Attribute`] that will fill a [`NodeRefContainer`] with an HTML element.
|
||||
#[derive(Debug)]
|
||||
pub struct NodeRefAttr<E, C> {
|
||||
pub struct NodeRefAttr<E, C, Rndr> {
|
||||
container: C,
|
||||
ty: PhantomData<E>,
|
||||
rndr: PhantomData<Rndr>,
|
||||
}
|
||||
|
||||
impl<E, C> Clone for NodeRefAttr<E, C>
|
||||
impl<E, C, Rndr> Clone for NodeRefAttr<E, C, Rndr>
|
||||
where
|
||||
C: Clone,
|
||||
{
|
||||
@@ -31,32 +34,35 @@ where
|
||||
Self {
|
||||
container: self.container.clone(),
|
||||
ty: PhantomData,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an attribute that will fill a [`NodeRefContainer`] with the element it is applied to.
|
||||
pub fn node_ref<E, C>(container: C) -> NodeRefAttr<E, C>
|
||||
pub fn node_ref<E, C, Rndr>(container: C) -> NodeRefAttr<E, C, Rndr>
|
||||
where
|
||||
E: ElementType,
|
||||
C: NodeRefContainer<E>,
|
||||
C: NodeRefContainer<E, Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
NodeRefAttr {
|
||||
container,
|
||||
ty: PhantomData,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, C> Attribute for NodeRefAttr<E, C>
|
||||
impl<E, C, Rndr> Attribute<Rndr> for NodeRefAttr<E, C, Rndr>
|
||||
where
|
||||
E: ElementType,
|
||||
C: NodeRefContainer<E>,
|
||||
|
||||
crate::renderer::types::Element: PartialEq,
|
||||
C: NodeRefContainer<E, Rndr>,
|
||||
Rndr: Renderer,
|
||||
Rndr::Element: PartialEq,
|
||||
{
|
||||
const MIN_LENGTH: usize = 0;
|
||||
type AsyncOutput = Self;
|
||||
type State = crate::renderer::types::Element;
|
||||
type State = Rndr::Element;
|
||||
type Cloneable = ();
|
||||
type CloneableOwned = ();
|
||||
|
||||
@@ -76,13 +82,13 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<Rndr as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
self.container.load(el);
|
||||
el.to_owned()
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &<Rndr as Renderer>::Element) -> Self::State {
|
||||
self.container.load(el);
|
||||
el.to_owned()
|
||||
}
|
||||
@@ -106,16 +112,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, C> NextAttribute for NodeRefAttr<E, C>
|
||||
impl<E, C, Rndr> NextAttribute<Rndr> for NodeRefAttr<E, C, Rndr>
|
||||
where
|
||||
E: ElementType,
|
||||
C: NodeRefContainer<E>,
|
||||
|
||||
crate::renderer::types::Element: PartialEq,
|
||||
C: NodeRefContainer<E, Rndr>,
|
||||
Rndr: Renderer,
|
||||
Rndr::Element: PartialEq,
|
||||
{
|
||||
type Output<NewAttr: Attribute> = (Self, NewAttr);
|
||||
type Output<NewAttr: Attribute<Rndr>> = (Self, NewAttr);
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
new_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr> {
|
||||
@@ -124,33 +130,35 @@ where
|
||||
}
|
||||
|
||||
/// Adds the `node_ref` attribute to an element.
|
||||
pub trait NodeRefAttribute<E, C>
|
||||
pub trait NodeRefAttribute<E, C, Rndr>
|
||||
where
|
||||
E: ElementType,
|
||||
C: NodeRefContainer<E>,
|
||||
|
||||
crate::renderer::types::Element: PartialEq,
|
||||
C: NodeRefContainer<E, Rndr>,
|
||||
Rndr: Renderer,
|
||||
Rndr::Element: PartialEq,
|
||||
{
|
||||
/// Binds this HTML element to a [`NodeRefContainer`].
|
||||
fn node_ref(
|
||||
self,
|
||||
container: C,
|
||||
) -> <Self as AddAnyAttr>::Output<NodeRefAttr<E, C>>
|
||||
) -> <Self as AddAnyAttr<Rndr>>::Output<NodeRefAttr<E, C, Rndr>>
|
||||
where
|
||||
Self: Sized + AddAnyAttr,
|
||||
<Self as AddAnyAttr>::Output<NodeRefAttr<E, C>>: Render,
|
||||
Self: Sized + AddAnyAttr<Rndr>,
|
||||
<Self as AddAnyAttr<Rndr>>::Output<NodeRefAttr<E, C, Rndr>>:
|
||||
Render<Rndr>,
|
||||
{
|
||||
self.add_any_attr(node_ref(container))
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, At, Ch, C> NodeRefAttribute<E, C> for HtmlElement<E, At, Ch>
|
||||
impl<E, At, Ch, C, Rndr> NodeRefAttribute<E, C, Rndr>
|
||||
for HtmlElement<E, At, Ch, Rndr>
|
||||
where
|
||||
E: ElementType,
|
||||
At: Attribute,
|
||||
Ch: Render,
|
||||
C: NodeRefContainer<E>,
|
||||
|
||||
crate::renderer::types::Element: PartialEq,
|
||||
At: Attribute<Rndr>,
|
||||
Ch: Render<Rndr>,
|
||||
C: NodeRefContainer<E, Rndr>,
|
||||
Rndr: Renderer,
|
||||
Rndr::Element: PartialEq,
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,34 +1,37 @@
|
||||
use super::attribute::{Attribute, NextAttribute};
|
||||
use crate::{
|
||||
renderer::Rndr,
|
||||
renderer::DomRenderer,
|
||||
view::{Position, ToTemplate},
|
||||
};
|
||||
use send_wrapper::SendWrapper;
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
use std::{borrow::Cow, marker::PhantomData, sync::Arc};
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
/// Creates an [`Attribute`] that will set a DOM property on an element.
|
||||
#[inline(always)]
|
||||
pub fn prop<K, P>(key: K, value: P) -> Property<K, P>
|
||||
pub fn prop<K, P, R>(key: K, value: P) -> Property<K, P, R>
|
||||
where
|
||||
K: AsRef<str>,
|
||||
P: IntoProperty,
|
||||
P: IntoProperty<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
Property {
|
||||
key,
|
||||
value: Some(SendWrapper::new(value)),
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`Attribute`] that will set a DOM property on an element.
|
||||
#[derive(Debug)]
|
||||
pub struct Property<K, P> {
|
||||
pub struct Property<K, P, R> {
|
||||
key: K,
|
||||
// property values will only be accessed in the browser
|
||||
value: Option<SendWrapper<P>>,
|
||||
rndr: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<K, P> Clone for Property<K, P>
|
||||
impl<K, P, R> Clone for Property<K, P, R>
|
||||
where
|
||||
K: Clone,
|
||||
P: Clone,
|
||||
@@ -37,21 +40,23 @@ where
|
||||
Self {
|
||||
key: self.key.clone(),
|
||||
value: self.value.clone(),
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, P> Attribute for Property<K, P>
|
||||
impl<K, P, R> Attribute<R> for Property<K, P, R>
|
||||
where
|
||||
K: AsRef<str> + Send,
|
||||
P: IntoProperty,
|
||||
P: IntoProperty<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
const MIN_LENGTH: usize = 0;
|
||||
|
||||
type AsyncOutput = Self;
|
||||
type State = P::State;
|
||||
type Cloneable = Property<Arc<str>, P::Cloneable>;
|
||||
type CloneableOwned = Property<Arc<str>, P::CloneableOwned>;
|
||||
type Cloneable = Property<Arc<str>, P::Cloneable, R>;
|
||||
type CloneableOwned = Property<Arc<str>, P::CloneableOwned, R>;
|
||||
|
||||
#[inline(always)]
|
||||
fn html_len(&self) -> usize {
|
||||
@@ -67,17 +72,14 @@ where
|
||||
) {
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
self.value
|
||||
.expect("property removed early")
|
||||
.take()
|
||||
.hydrate::<FROM_SERVER>(el, self.key.as_ref())
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
self.value
|
||||
.expect("property removed early")
|
||||
.take()
|
||||
@@ -97,6 +99,7 @@ where
|
||||
value: self
|
||||
.value
|
||||
.map(|value| SendWrapper::new(value.take().into_cloneable())),
|
||||
rndr: self.rndr,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +109,7 @@ where
|
||||
value: self.value.map(|value| {
|
||||
SendWrapper::new(value.take().into_cloneable_owned())
|
||||
}),
|
||||
rndr: self.rndr,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,14 +126,15 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, P> NextAttribute for Property<K, P>
|
||||
impl<K, P, R> NextAttribute<R> for Property<K, P, R>
|
||||
where
|
||||
K: AsRef<str> + Send,
|
||||
P: IntoProperty,
|
||||
P: IntoProperty<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type Output<NewAttr: Attribute> = (Self, NewAttr);
|
||||
type Output<NewAttr: Attribute<R>> = (Self, NewAttr);
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
new_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr> {
|
||||
@@ -137,10 +142,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, P> ToTemplate for Property<K, P>
|
||||
impl<K, P, R> ToTemplate for Property<K, P, R>
|
||||
where
|
||||
K: AsRef<str>,
|
||||
P: IntoProperty,
|
||||
P: IntoProperty<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
fn to_template(
|
||||
_buf: &mut String,
|
||||
@@ -153,27 +159,23 @@ where
|
||||
}
|
||||
|
||||
/// A possible value for a DOM property.
|
||||
pub trait IntoProperty {
|
||||
pub trait IntoProperty<R: DomRenderer> {
|
||||
/// The view state retained between building and rebuilding.
|
||||
type State;
|
||||
/// An equivalent value that can be cloned.
|
||||
type Cloneable: IntoProperty + Clone;
|
||||
type Cloneable: IntoProperty<R> + Clone;
|
||||
/// An equivalent value that can be cloned and is `'static`.
|
||||
type CloneableOwned: IntoProperty + Clone + 'static;
|
||||
type CloneableOwned: IntoProperty<R> + Clone + 'static;
|
||||
|
||||
/// Adds the property on an element created from HTML.
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
key: &str,
|
||||
) -> Self::State;
|
||||
|
||||
/// Adds the property during client-side rendering.
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::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);
|
||||
@@ -187,35 +189,34 @@ pub trait IntoProperty {
|
||||
|
||||
macro_rules! prop_type {
|
||||
($prop_type:ty) => {
|
||||
impl IntoProperty for $prop_type {
|
||||
type State = (crate::renderer::types::Element, JsValue);
|
||||
impl<R> IntoProperty<R> for $prop_type
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = (R::Element, JsValue);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
let value = self.into();
|
||||
Rndr::set_property(el, key, &value);
|
||||
R::set_property(el, key, &value);
|
||||
(el.clone(), value)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
fn build(self, el: &R::Element, key: &str) -> Self::State {
|
||||
let value = self.into();
|
||||
Rndr::set_property(el, key, &value);
|
||||
R::set_property(el, key, &value);
|
||||
(el.clone(), value)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State, key: &str) {
|
||||
let (el, prev) = state;
|
||||
let value = self.into();
|
||||
Rndr::set_property(el, key, &value);
|
||||
R::set_property(el, key, &value);
|
||||
*prev = value;
|
||||
}
|
||||
|
||||
@@ -228,33 +229,32 @@ macro_rules! prop_type {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoProperty for Option<$prop_type> {
|
||||
type State = (crate::renderer::types::Element, JsValue);
|
||||
impl<R> IntoProperty<R> for Option<$prop_type>
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = (R::Element, JsValue);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
let was_some = self.is_some();
|
||||
let value = self.into();
|
||||
if was_some {
|
||||
Rndr::set_property(el, key, &value);
|
||||
R::set_property(el, key, &value);
|
||||
}
|
||||
(el.clone(), value)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
fn build(self, el: &R::Element, key: &str) -> Self::State {
|
||||
let was_some = self.is_some();
|
||||
let value = self.into();
|
||||
if was_some {
|
||||
Rndr::set_property(el, key, &value);
|
||||
R::set_property(el, key, &value);
|
||||
}
|
||||
(el.clone(), value)
|
||||
}
|
||||
@@ -262,7 +262,7 @@ macro_rules! prop_type {
|
||||
fn rebuild(self, state: &mut Self::State, key: &str) {
|
||||
let (el, prev) = state;
|
||||
let value = self.into();
|
||||
Rndr::set_property(el, key, &value);
|
||||
R::set_property(el, key, &value);
|
||||
*prev = value;
|
||||
}
|
||||
|
||||
@@ -279,35 +279,34 @@ macro_rules! prop_type {
|
||||
|
||||
macro_rules! prop_type_str {
|
||||
($prop_type:ty) => {
|
||||
impl IntoProperty for $prop_type {
|
||||
type State = (crate::renderer::types::Element, JsValue);
|
||||
impl<R> IntoProperty<R> for $prop_type
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = (R::Element, JsValue);
|
||||
type Cloneable = Arc<str>;
|
||||
type CloneableOwned = Arc<str>;
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
let value = JsValue::from(&*self);
|
||||
Rndr::set_property(el, key, &value);
|
||||
R::set_property(el, key, &value);
|
||||
(el.clone(), value)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
fn build(self, el: &R::Element, key: &str) -> Self::State {
|
||||
let value = JsValue::from(&*self);
|
||||
Rndr::set_property(el, key, &value);
|
||||
R::set_property(el, key, &value);
|
||||
(el.clone(), value)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State, key: &str) {
|
||||
let (el, prev) = state;
|
||||
let value = JsValue::from(&*self);
|
||||
Rndr::set_property(el, key, &value);
|
||||
R::set_property(el, key, &value);
|
||||
*prev = value;
|
||||
}
|
||||
|
||||
@@ -322,33 +321,32 @@ macro_rules! prop_type_str {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoProperty for Option<$prop_type> {
|
||||
type State = (crate::renderer::types::Element, JsValue);
|
||||
impl<R> IntoProperty<R> for Option<$prop_type>
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = (R::Element, JsValue);
|
||||
type Cloneable = Option<Arc<str>>;
|
||||
type CloneableOwned = Option<Arc<str>>;
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
let was_some = self.is_some();
|
||||
let value = JsValue::from(self.map(|n| JsValue::from_str(&n)));
|
||||
if was_some {
|
||||
Rndr::set_property(el, key, &value);
|
||||
R::set_property(el, key, &value);
|
||||
}
|
||||
(el.clone(), value)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
fn build(self, el: &R::Element, key: &str) -> Self::State {
|
||||
let was_some = self.is_some();
|
||||
let value = JsValue::from(self.map(|n| JsValue::from_str(&n)));
|
||||
if was_some {
|
||||
Rndr::set_property(el, key, &value);
|
||||
R::set_property(el, key, &value);
|
||||
}
|
||||
(el.clone(), value)
|
||||
}
|
||||
@@ -356,7 +354,7 @@ macro_rules! prop_type_str {
|
||||
fn rebuild(self, state: &mut Self::State, key: &str) {
|
||||
let (el, prev) = state;
|
||||
let value = JsValue::from(self.map(|n| JsValue::from_str(&n)));
|
||||
Rndr::set_property(el, key, &value);
|
||||
R::set_property(el, key, &value);
|
||||
*prev = value;
|
||||
}
|
||||
|
||||
@@ -377,35 +375,34 @@ macro_rules! prop_type_str {
|
||||
};
|
||||
}
|
||||
|
||||
impl IntoProperty for Arc<str> {
|
||||
type State = (crate::renderer::types::Element, JsValue);
|
||||
impl<R> IntoProperty<R> for Arc<str>
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = (R::Element, JsValue);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
let value = JsValue::from_str(self.as_ref());
|
||||
Rndr::set_property(el, key, &value);
|
||||
R::set_property(el, key, &value);
|
||||
(el.clone(), value)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
fn build(self, el: &R::Element, key: &str) -> Self::State {
|
||||
let value = JsValue::from_str(self.as_ref());
|
||||
Rndr::set_property(el, key, &value);
|
||||
R::set_property(el, key, &value);
|
||||
(el.clone(), value)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State, key: &str) {
|
||||
let (el, prev) = state;
|
||||
let value = JsValue::from_str(self.as_ref());
|
||||
Rndr::set_property(el, key, &value);
|
||||
R::set_property(el, key, &value);
|
||||
*prev = value;
|
||||
}
|
||||
|
||||
@@ -418,33 +415,32 @@ impl IntoProperty for Arc<str> {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoProperty for Option<Arc<str>> {
|
||||
type State = (crate::renderer::types::Element, JsValue);
|
||||
impl<R> IntoProperty<R> for Option<Arc<str>>
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = (R::Element, JsValue);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
let was_some = self.is_some();
|
||||
let value = JsValue::from(self.map(|n| JsValue::from_str(&n)));
|
||||
if was_some {
|
||||
Rndr::set_property(el, key, &value);
|
||||
R::set_property(el, key, &value);
|
||||
}
|
||||
(el.clone(), value)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
fn build(self, el: &R::Element, key: &str) -> Self::State {
|
||||
let was_some = self.is_some();
|
||||
let value = JsValue::from(self.map(|n| JsValue::from_str(&n)));
|
||||
if was_some {
|
||||
Rndr::set_property(el, key, &value);
|
||||
R::set_property(el, key, &value);
|
||||
}
|
||||
(el.clone(), value)
|
||||
}
|
||||
@@ -452,7 +448,7 @@ impl IntoProperty for Option<Arc<str>> {
|
||||
fn rebuild(self, state: &mut Self::State, key: &str) {
|
||||
let (el, prev) = state;
|
||||
let value = JsValue::from(self.map(|n| JsValue::from_str(&n)));
|
||||
Rndr::set_property(el, key, &value);
|
||||
R::set_property(el, key, &value);
|
||||
*prev = value;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,47 +2,54 @@ use super::attribute::{Attribute, NextAttribute};
|
||||
#[cfg(feature = "nightly")]
|
||||
use crate::view::static_types::Static;
|
||||
use crate::{
|
||||
renderer::Rndr,
|
||||
renderer::DomRenderer,
|
||||
view::{Position, ToTemplate},
|
||||
};
|
||||
use std::{future::Future, sync::Arc};
|
||||
use std::{future::Future, marker::PhantomData, sync::Arc};
|
||||
|
||||
/// Returns an [`Attribute`] that will add to an element's CSS styles.
|
||||
#[inline(always)]
|
||||
pub fn style<S>(style: S) -> Style<S>
|
||||
pub fn style<S, R>(style: S) -> Style<S, R>
|
||||
where
|
||||
S: IntoStyle,
|
||||
S: IntoStyle<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
Style { style }
|
||||
Style {
|
||||
style,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`Attribute`] that will add to an element's CSS styles.
|
||||
#[derive(Debug)]
|
||||
pub struct Style<S> {
|
||||
pub struct Style<S, R> {
|
||||
style: S,
|
||||
rndr: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<S> Clone for Style<S>
|
||||
impl<S, R> Clone for Style<S, R>
|
||||
where
|
||||
S: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
style: self.style.clone(),
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Attribute for Style<S>
|
||||
impl<S, R> Attribute<R> for Style<S, R>
|
||||
where
|
||||
S: IntoStyle,
|
||||
S: IntoStyle<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
const MIN_LENGTH: usize = 0;
|
||||
|
||||
type AsyncOutput = Style<S::AsyncOutput>;
|
||||
type AsyncOutput = Style<S::AsyncOutput, R>;
|
||||
type State = S::State;
|
||||
type Cloneable = Style<S::Cloneable>;
|
||||
type CloneableOwned = Style<S::CloneableOwned>;
|
||||
type Cloneable = Style<S::Cloneable, R>;
|
||||
type CloneableOwned = Style<S::CloneableOwned, R>;
|
||||
|
||||
// TODO
|
||||
#[inline(always)]
|
||||
@@ -60,14 +67,11 @@ where
|
||||
self.style.to_html(style);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
self.style.hydrate::<FROM_SERVER>(el)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
self.style.build(el)
|
||||
}
|
||||
|
||||
@@ -78,12 +82,14 @@ where
|
||||
fn into_cloneable(self) -> Self::Cloneable {
|
||||
Style {
|
||||
style: self.style.into_cloneable(),
|
||||
rndr: self.rndr,
|
||||
}
|
||||
}
|
||||
|
||||
fn into_cloneable_owned(self) -> Self::CloneableOwned {
|
||||
Style {
|
||||
style: self.style.into_cloneable_owned(),
|
||||
rndr: self.rndr,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,17 +100,19 @@ where
|
||||
async fn resolve(self) -> Self::AsyncOutput {
|
||||
Style {
|
||||
style: self.style.resolve().await,
|
||||
rndr: self.rndr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> NextAttribute for Style<S>
|
||||
impl<S, R> NextAttribute<R> for Style<S, R>
|
||||
where
|
||||
S: IntoStyle,
|
||||
S: IntoStyle<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type Output<NewAttr: Attribute> = (Self, NewAttr);
|
||||
type Output<NewAttr: Attribute<R>> = (Self, NewAttr);
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
new_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr> {
|
||||
@@ -112,9 +120,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> ToTemplate for Style<S>
|
||||
impl<S, R> ToTemplate for Style<S, R>
|
||||
where
|
||||
S: IntoStyle,
|
||||
S: IntoStyle<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
fn to_template(
|
||||
_buf: &mut String,
|
||||
@@ -129,28 +138,25 @@ where
|
||||
|
||||
/// Any type that can be added to the `style` attribute or set as a style in
|
||||
/// the [`CssStyleDeclaration`]. This could be a plain string, or a property name-value pair.
|
||||
pub trait IntoStyle: Send {
|
||||
pub trait IntoStyle<R: DomRenderer>: Send {
|
||||
/// The type after all async data have resolved.
|
||||
type AsyncOutput: IntoStyle;
|
||||
type AsyncOutput: IntoStyle<R>;
|
||||
/// The view state retained between building and rebuilding.
|
||||
type State;
|
||||
/// An equivalent value that can be cloned.
|
||||
type Cloneable: IntoStyle + Clone;
|
||||
type Cloneable: IntoStyle<R> + Clone;
|
||||
/// An equivalent value that can be cloned and is `'static`.
|
||||
type CloneableOwned: IntoStyle + Clone + 'static;
|
||||
type CloneableOwned: IntoStyle<R> + Clone + 'static;
|
||||
|
||||
/// Renders the style to HTML.
|
||||
fn to_html(self, style: &mut String);
|
||||
|
||||
/// Adds interactivity as necessary, given DOM nodes that were created from HTML that has
|
||||
/// either been rendered on the server, or cloned for a `<template>`.
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::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: &crate::renderer::types::Element) -> Self::State;
|
||||
fn build(self, el: &R::Element) -> Self::State;
|
||||
|
||||
/// Updates the value.
|
||||
fn rebuild(self, state: &mut Self::State);
|
||||
@@ -170,9 +176,12 @@ pub trait IntoStyle: Send {
|
||||
fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;
|
||||
}
|
||||
|
||||
impl<'a> IntoStyle for &'a str {
|
||||
impl<'a, R> IntoStyle<R> for &'a str
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::Element, &'a str);
|
||||
type State = (R::Element, &'a str);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Arc<str>;
|
||||
|
||||
@@ -181,22 +190,19 @@ impl<'a> IntoStyle for &'a str {
|
||||
style.push(';');
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
Rndr::set_attribute(el, "style", self);
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
R::set_attribute(el, "style", self);
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let (el, prev) = state;
|
||||
if self != *prev {
|
||||
Rndr::set_attribute(el, "style", self);
|
||||
R::set_attribute(el, "style", self);
|
||||
}
|
||||
*prev = self;
|
||||
}
|
||||
@@ -216,9 +222,12 @@ impl<'a> IntoStyle for &'a str {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoStyle for Arc<str> {
|
||||
impl<R> IntoStyle<R> for Arc<str>
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::Element, Arc<str>);
|
||||
type State = (R::Element, Arc<str>);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
@@ -227,22 +236,19 @@ impl IntoStyle for Arc<str> {
|
||||
style.push(';');
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
Rndr::set_attribute(el, "style", &self);
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
R::set_attribute(el, "style", &self);
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let (el, prev) = state;
|
||||
if self != *prev {
|
||||
Rndr::set_attribute(el, "style", &self);
|
||||
R::set_attribute(el, "style", &self);
|
||||
}
|
||||
*prev = self;
|
||||
}
|
||||
@@ -262,9 +268,12 @@ impl IntoStyle for Arc<str> {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoStyle for String {
|
||||
impl<R> IntoStyle<R> for String
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::Element, String);
|
||||
type State = (R::Element, String);
|
||||
type Cloneable = Arc<str>;
|
||||
type CloneableOwned = Arc<str>;
|
||||
|
||||
@@ -273,22 +282,19 @@ impl IntoStyle for String {
|
||||
style.push(';');
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
Rndr::set_attribute(el, "style", &self);
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
R::set_attribute(el, "style", &self);
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let (el, prev) = state;
|
||||
if self != *prev {
|
||||
Rndr::set_attribute(el, "style", &self);
|
||||
R::set_attribute(el, "style", &self);
|
||||
}
|
||||
*prev = self;
|
||||
}
|
||||
@@ -308,9 +314,12 @@ impl IntoStyle for String {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoStyle for (Arc<str>, Arc<str>) {
|
||||
impl<R> IntoStyle<R> for (Arc<str>, Arc<str>)
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::CssStyleDeclaration, Arc<str>);
|
||||
type State = (R::CssStyleDeclaration, Arc<str>);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
@@ -322,18 +331,15 @@ impl IntoStyle for (Arc<str>, Arc<str>) {
|
||||
style.push(';');
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
let style = Rndr::style(el);
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
let style = R::style(el);
|
||||
(style, self.1)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
let (name, value) = self;
|
||||
let style = Rndr::style(el);
|
||||
Rndr::set_css_property(&style, &name, &value);
|
||||
let style = R::style(el);
|
||||
R::set_css_property(&style, &name, &value);
|
||||
(style, value)
|
||||
}
|
||||
|
||||
@@ -341,7 +347,7 @@ impl IntoStyle for (Arc<str>, Arc<str>) {
|
||||
let (name, value) = self;
|
||||
let (style, prev) = state;
|
||||
if value != *prev {
|
||||
Rndr::set_css_property(style, &name, &value);
|
||||
R::set_css_property(style, &name, &value);
|
||||
}
|
||||
*prev = value;
|
||||
}
|
||||
@@ -361,9 +367,12 @@ impl IntoStyle for (Arc<str>, Arc<str>) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoStyle for (&'a str, &'a str) {
|
||||
impl<'a, R> IntoStyle<R> for (&'a str, &'a str)
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::CssStyleDeclaration, &'a str);
|
||||
type State = (R::CssStyleDeclaration, &'a str);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = (Arc<str>, Arc<str>);
|
||||
|
||||
@@ -375,18 +384,15 @@ impl<'a> IntoStyle for (&'a str, &'a str) {
|
||||
style.push(';');
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
let style = Rndr::style(el);
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
let style = R::style(el);
|
||||
(style, self.1)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
let (name, value) = self;
|
||||
let style = Rndr::style(el);
|
||||
Rndr::set_css_property(&style, name, value);
|
||||
let style = R::style(el);
|
||||
R::set_css_property(&style, name, value);
|
||||
(style, self.1)
|
||||
}
|
||||
|
||||
@@ -394,7 +400,7 @@ impl<'a> IntoStyle for (&'a str, &'a str) {
|
||||
let (name, value) = self;
|
||||
let (style, prev) = state;
|
||||
if value != *prev {
|
||||
Rndr::set_css_property(style, name, value);
|
||||
R::set_css_property(style, name, value);
|
||||
}
|
||||
*prev = value;
|
||||
}
|
||||
@@ -414,9 +420,12 @@ impl<'a> IntoStyle for (&'a str, &'a str) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoStyle for (&'a str, String) {
|
||||
impl<'a, R> IntoStyle<R> for (&'a str, String)
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::CssStyleDeclaration, String);
|
||||
type State = (R::CssStyleDeclaration, String);
|
||||
type Cloneable = (Arc<str>, Arc<str>);
|
||||
type CloneableOwned = (Arc<str>, Arc<str>);
|
||||
|
||||
@@ -428,18 +437,15 @@ impl<'a> IntoStyle for (&'a str, String) {
|
||||
style.push(';');
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
let style = Rndr::style(el);
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
let style = R::style(el);
|
||||
(style, self.1)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
let (name, value) = &self;
|
||||
let style = Rndr::style(el);
|
||||
Rndr::set_css_property(&style, name, value);
|
||||
let style = R::style(el);
|
||||
R::set_css_property(&style, name, value);
|
||||
(style, self.1)
|
||||
}
|
||||
|
||||
@@ -447,7 +453,7 @@ impl<'a> IntoStyle for (&'a str, String) {
|
||||
let (name, value) = self;
|
||||
let (style, prev) = state;
|
||||
if value != *prev {
|
||||
Rndr::set_css_property(style, name, &value);
|
||||
R::set_css_property(style, name, &value);
|
||||
}
|
||||
*prev = value;
|
||||
}
|
||||
@@ -468,7 +474,10 @@ impl<'a> IntoStyle for (&'a str, String) {
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<'a, const V: &'static str> IntoStyle for (&'a str, Static<V>) {
|
||||
impl<'a, const V: &'static str, R> IntoStyle<R> for (&'a str, Static<V>)
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = ();
|
||||
type Cloneable = (Arc<str>, Static<V>);
|
||||
@@ -482,16 +491,13 @@ impl<'a, const V: &'static str> IntoStyle for (&'a str, Static<V>) {
|
||||
style.push(';');
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
_el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, _el: &R::Element) -> Self::State {
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
let (name, _) = &self;
|
||||
let style = Rndr::style(el);
|
||||
Rndr::set_css_property(&style, name, V);
|
||||
let style = R::style(el);
|
||||
R::set_css_property(&style, name, V);
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
@@ -512,7 +518,10 @@ impl<'a, const V: &'static str> IntoStyle for (&'a str, Static<V>) {
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<const V: &'static str> IntoStyle for (Arc<str>, Static<V>) {
|
||||
impl<const V: &'static str, R> IntoStyle<R> for (Arc<str>, Static<V>)
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = ();
|
||||
type Cloneable = (Arc<str>, Static<V>);
|
||||
@@ -526,16 +535,13 @@ impl<const V: &'static str> IntoStyle for (Arc<str>, Static<V>) {
|
||||
style.push(';');
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
_el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, _el: &R::Element) -> Self::State {
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
let (name, _) = &self;
|
||||
let style = Rndr::style(el);
|
||||
Rndr::set_css_property(&style, name, V);
|
||||
let style = R::style(el);
|
||||
R::set_css_property(&style, name, V);
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
@@ -556,7 +562,11 @@ impl<const V: &'static str> IntoStyle for (Arc<str>, Static<V>) {
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<const V: &'static str> IntoStyle for crate::view::static_types::Static<V> {
|
||||
impl<const V: &'static str, R> IntoStyle<R>
|
||||
for crate::view::static_types::Static<V>
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = ();
|
||||
type Cloneable = Self;
|
||||
@@ -569,12 +579,12 @@ impl<const V: &'static str> IntoStyle for crate::view::static_types::Static<V> {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
_el: &crate::renderer::types::Element,
|
||||
_el: &<R>::Element,
|
||||
) -> Self::State {
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
Rndr::set_attribute(el, "style", V);
|
||||
fn build(self, el: &<R>::Element) -> Self::State {
|
||||
R::set_attribute(el, "style", V);
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
renderer::{CastFrom, Rndr},
|
||||
renderer::{CastFrom, Renderer},
|
||||
view::{Position, PositionState},
|
||||
};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
@@ -10,29 +10,27 @@ use std::{cell::RefCell, rc::Rc};
|
||||
/// implements [`RenderHtml`](crate::view::RenderHtml) knows how to advance the cursor to access
|
||||
/// the nodes it needs.
|
||||
#[derive(Debug)]
|
||||
pub struct Cursor(Rc<RefCell<crate::renderer::types::Node>>);
|
||||
pub struct Cursor<R: Renderer>(Rc<RefCell<R::Node>>);
|
||||
|
||||
impl Clone for Cursor {
|
||||
impl<R: Renderer> Clone for Cursor<R> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(Rc::clone(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl Cursor
|
||||
impl<R> Cursor<R>
|
||||
where
|
||||
crate::renderer::types::Element: AsRef<crate::renderer::types::Node>,
|
||||
R: Renderer,
|
||||
|
||||
R::Element: AsRef<R::Node>,
|
||||
{
|
||||
/// Creates a new cursor starting at the root element.
|
||||
pub fn new(root: crate::renderer::types::Element) -> Self {
|
||||
let root = <crate::renderer::types::Element as AsRef<
|
||||
crate::renderer::types::Node,
|
||||
>>::as_ref(&root)
|
||||
.clone();
|
||||
Self(Rc::new(RefCell::new(root)))
|
||||
pub fn new(root: R::Element) -> Self {
|
||||
Self(Rc::new(RefCell::new(root.as_ref().clone())))
|
||||
}
|
||||
|
||||
/// Returns the node at which the cursor is currently located.
|
||||
pub fn current(&self) -> crate::renderer::types::Node {
|
||||
pub fn current(&self) -> R::Node {
|
||||
self.0.borrow().clone()
|
||||
}
|
||||
|
||||
@@ -41,14 +39,14 @@ where
|
||||
/// Does nothing if there is no child.
|
||||
pub fn child(&self) {
|
||||
//crate::log("advancing to next child of ");
|
||||
//Rndr::log_node(&self.current());
|
||||
//R::log_node(&self.current());
|
||||
let mut inner = self.0.borrow_mut();
|
||||
if let Some(node) = Rndr::first_child(&inner) {
|
||||
if let Some(node) = R::first_child(&*inner) {
|
||||
*inner = node;
|
||||
}
|
||||
//drop(inner);
|
||||
//crate::log(">> which is ");
|
||||
//Rndr::log_node(&self.current());
|
||||
//R::log_node(&self.current());
|
||||
}
|
||||
|
||||
/// Advances to the next sibling of the node at which the cursor is located.
|
||||
@@ -56,14 +54,14 @@ where
|
||||
/// Does nothing if there is no sibling.
|
||||
pub fn sibling(&self) {
|
||||
//crate::log("advancing to next sibling of ");
|
||||
//Rndr::log_node(&self.current());
|
||||
//R::log_node(&self.current());
|
||||
let mut inner = self.0.borrow_mut();
|
||||
if let Some(node) = Rndr::next_sibling(&inner) {
|
||||
if let Some(node) = R::next_sibling(&*inner) {
|
||||
*inner = node;
|
||||
}
|
||||
//drop(inner);
|
||||
//crate::log(">> which is ");
|
||||
//Rndr::log_node(&self.current());
|
||||
//R::log_node(&self.current());
|
||||
}
|
||||
|
||||
/// Moves to the parent of the node at which the cursor is located.
|
||||
@@ -71,23 +69,20 @@ where
|
||||
/// Does nothing if there is no parent.
|
||||
pub fn parent(&self) {
|
||||
let mut inner = self.0.borrow_mut();
|
||||
if let Some(node) = Rndr::get_parent(&inner) {
|
||||
if let Some(node) = R::get_parent(&*inner) {
|
||||
*inner = node;
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the cursor to some node.
|
||||
pub fn set(&self, node: crate::renderer::types::Node) {
|
||||
pub fn set(&self, node: R::Node) {
|
||||
*self.0.borrow_mut() = node;
|
||||
}
|
||||
|
||||
/// Advances to the next placeholder node.
|
||||
pub fn next_placeholder(
|
||||
&self,
|
||||
position: &PositionState,
|
||||
) -> crate::renderer::types::Placeholder {
|
||||
pub fn next_placeholder(&self, position: &PositionState) -> R::Placeholder {
|
||||
//crate::dom::log("looking for placeholder after");
|
||||
//Rndr::log_node(&self.current());
|
||||
//R::log_node(&self.current());
|
||||
if position.get() == Position::FirstChild {
|
||||
self.child();
|
||||
} else {
|
||||
@@ -95,12 +90,12 @@ where
|
||||
}
|
||||
let marker = self.current();
|
||||
position.set(Position::NextChild);
|
||||
crate::renderer::types::Placeholder::cast_from(marker)
|
||||
R::Placeholder::cast_from(marker)
|
||||
.expect("could not convert current node into marker node")
|
||||
/*let marker2 = marker.clone();
|
||||
Rndr::Placeholder::cast_from(marker).unwrap_or_else(|| {
|
||||
R::Placeholder::cast_from(marker).unwrap_or_else(|| {
|
||||
crate::dom::log("expecting to find a marker. instead, found");
|
||||
Rndr::log_node(&marker2);
|
||||
R::log_node(&marker2);
|
||||
panic!("oops.");
|
||||
})*/
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#![allow(incomplete_features)] // yolo
|
||||
#![cfg_attr(feature = "nightly", feature(unsized_const_params))]
|
||||
//#![deny(missing_docs)]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
/// Commonly-used traits.
|
||||
pub mod prelude {
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
use crate::{
|
||||
html::{
|
||||
attribute::{Attr, Attribute, AttributeValue},
|
||||
element::{ElementType, ElementWithChildren, HtmlElement},
|
||||
element::{
|
||||
CreateElement, ElementType, ElementWithChildren, HtmlElement,
|
||||
},
|
||||
},
|
||||
renderer::{dom::Dom, Renderer},
|
||||
view::Render,
|
||||
};
|
||||
use next_tuple::NextTuple;
|
||||
use std::fmt::Debug;
|
||||
use once_cell::unsync::Lazy;
|
||||
use std::{fmt::Debug, marker::PhantomData};
|
||||
|
||||
macro_rules! mathml_global {
|
||||
($tag:ty, $attr:ty) => {
|
||||
@@ -14,18 +18,18 @@ macro_rules! mathml_global {
|
||||
/// A MathML attribute.
|
||||
pub fn $attr<V>(self, value: V) -> HtmlElement <
|
||||
[<$tag:camel>],
|
||||
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>,
|
||||
Ch
|
||||
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>,
|
||||
Ch, Rndr
|
||||
>
|
||||
where
|
||||
V: AttributeValue,
|
||||
V: AttributeValue<Rndr>,
|
||||
At: NextTuple,
|
||||
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>: Attribute,
|
||||
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>: Attribute<Rndr>,
|
||||
{
|
||||
let HtmlElement { tag, children, attributes } = self;
|
||||
let HtmlElement { tag, rndr, children, attributes } = self;
|
||||
HtmlElement {
|
||||
tag,
|
||||
|
||||
rndr,
|
||||
children,
|
||||
attributes: attributes.next_tuple($crate::html::attribute::$attr(value)),
|
||||
}
|
||||
@@ -41,15 +45,15 @@ macro_rules! mathml_elements {
|
||||
// `tag()` function
|
||||
/// A MathML element.
|
||||
#[track_caller]
|
||||
pub fn $tag() -> HtmlElement<[<$tag:camel>], (), ()>
|
||||
pub fn $tag<Rndr>() -> HtmlElement<[<$tag:camel>], (), (), Rndr>
|
||||
where
|
||||
|
||||
Rndr: Renderer
|
||||
{
|
||||
HtmlElement {
|
||||
tag: [<$tag:camel>],
|
||||
attributes: (),
|
||||
children: (),
|
||||
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,11 +61,11 @@ macro_rules! mathml_elements {
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct [<$tag:camel>];
|
||||
|
||||
impl<At, Ch> HtmlElement<[<$tag:camel>], At, Ch>
|
||||
impl<At, Ch, Rndr> HtmlElement<[<$tag:camel>], At, Ch, Rndr>
|
||||
where
|
||||
At: Attribute,
|
||||
Ch: Render,
|
||||
|
||||
At: Attribute<Rndr>,
|
||||
Ch: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
mathml_global!($tag, displaystyle);
|
||||
mathml_global!($tag, href);
|
||||
@@ -76,18 +80,18 @@ macro_rules! mathml_elements {
|
||||
/// A MathML attribute.
|
||||
pub fn $attr<V>(self, value: V) -> HtmlElement <
|
||||
[<$tag:camel>],
|
||||
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>,
|
||||
Ch
|
||||
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>,
|
||||
Ch, Rndr
|
||||
>
|
||||
where
|
||||
V: AttributeValue,
|
||||
V: AttributeValue<Rndr>,
|
||||
At: NextTuple,
|
||||
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V>>: Attribute,
|
||||
<At as NextTuple>::Output<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>: Attribute<Rndr>,
|
||||
{
|
||||
let HtmlElement { tag, children, attributes } = self;
|
||||
let HtmlElement { tag, rndr, children, attributes } = self;
|
||||
HtmlElement {
|
||||
tag,
|
||||
|
||||
rndr,
|
||||
children,
|
||||
attributes: attributes.next_tuple($crate::html::attribute::$attr(value)),
|
||||
}
|
||||
@@ -109,6 +113,22 @@ macro_rules! mathml_elements {
|
||||
}
|
||||
|
||||
impl ElementWithChildren for [<$tag:camel>] {}
|
||||
|
||||
impl CreateElement<Dom> for [<$tag:camel>] {
|
||||
fn create_element(&self) -> <Dom as Renderer>::Element {
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
thread_local! {
|
||||
static ELEMENT: Lazy<<Dom as Renderer>::Element> = Lazy::new(|| {
|
||||
crate::dom::document().create_element_ns(
|
||||
Some(wasm_bindgen::intern("http://www.w3.org/1998/Math/MathML")),
|
||||
stringify!($tag)
|
||||
).unwrap()
|
||||
});
|
||||
}
|
||||
ELEMENT.with(|e| e.clone_node()).unwrap().unchecked_into()
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,29 +3,29 @@ use crate::{
|
||||
hydration::Cursor,
|
||||
no_attrs,
|
||||
prelude::{Mountable, Render, RenderHtml},
|
||||
renderer::Rndr,
|
||||
renderer::{DomRenderer, Renderer},
|
||||
view::{strings::StrState, Position, PositionState, ToTemplate},
|
||||
};
|
||||
use oco_ref::Oco;
|
||||
|
||||
/// Retained view state for [`Oco`].
|
||||
pub struct OcoStrState {
|
||||
node: crate::renderer::types::Text,
|
||||
pub struct OcoStrState<R: Renderer> {
|
||||
node: R::Text,
|
||||
str: Oco<'static, str>,
|
||||
}
|
||||
|
||||
impl Render for Oco<'static, str> {
|
||||
type State = OcoStrState;
|
||||
impl<R: Renderer> Render<R> for Oco<'static, str> {
|
||||
type State = OcoStrState<R>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let node = Rndr::create_text_node(&self);
|
||||
let node = R::create_text_node(&self);
|
||||
OcoStrState { node, str: self }
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let OcoStrState { node, str } = state;
|
||||
if &self == str {
|
||||
Rndr::set_text(node, &self);
|
||||
R::set_text(node, &self);
|
||||
*str = self;
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,10 @@ impl Render for Oco<'static, str> {
|
||||
|
||||
no_attrs!(Oco<'static, str>);
|
||||
|
||||
impl RenderHtml for Oco<'static, str> {
|
||||
impl<R> RenderHtml<R> for Oco<'static, str>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
const MIN_LENGTH: usize = 0;
|
||||
@@ -51,7 +54,7 @@ impl RenderHtml for Oco<'static, str> {
|
||||
escape: bool,
|
||||
mark_branches: bool,
|
||||
) {
|
||||
<&str as RenderHtml>::to_html_with_buf(
|
||||
<&str as RenderHtml<R>>::to_html_with_buf(
|
||||
&self,
|
||||
buf,
|
||||
position,
|
||||
@@ -62,13 +65,13 @@ impl RenderHtml for Oco<'static, str> {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let this: &str = self.as_ref();
|
||||
let StrState { node, .. } = <&str as RenderHtml>::hydrate::<FROM_SERVER>(
|
||||
this, cursor, position,
|
||||
);
|
||||
let StrState { node, .. } = <&str as RenderHtml<R>>::hydrate::<
|
||||
FROM_SERVER,
|
||||
>(this, cursor, position);
|
||||
OcoStrState { node, str: self }
|
||||
}
|
||||
}
|
||||
@@ -89,27 +92,30 @@ impl ToTemplate for Oco<'static, str> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Mountable for OcoStrState {
|
||||
impl<R: Renderer> Mountable<R> for OcoStrState<R> {
|
||||
fn unmount(&mut self) {
|
||||
self.node.unmount()
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
parent: &<R as Renderer>::Element,
|
||||
marker: Option<&<R as Renderer>::Node>,
|
||||
) {
|
||||
Rndr::insert_node(parent, self.node.as_ref(), marker);
|
||||
R::insert_node(parent, self.node.as_ref(), marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
self.node.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
impl AttributeValue for Oco<'static, str> {
|
||||
impl<R> AttributeValue<R> for Oco<'static, str>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::Element, Oco<'static, str>);
|
||||
type State = (R::Element, Oco<'static, str>);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
@@ -118,7 +124,7 @@ impl AttributeValue for Oco<'static, str> {
|
||||
}
|
||||
|
||||
fn to_html(self, key: &str, buf: &mut String) {
|
||||
<&str as AttributeValue>::to_html(self.as_str(), key, buf);
|
||||
<&str as AttributeValue<R>>::to_html(self.as_str(), key, buf);
|
||||
}
|
||||
|
||||
fn to_template(_key: &str, _buf: &mut String) {}
|
||||
@@ -126,9 +132,9 @@ impl AttributeValue for Oco<'static, str> {
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
key: &str,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
let (el, _) = <&str as AttributeValue>::hydrate::<FROM_SERVER>(
|
||||
let (el, _) = <&str as AttributeValue<R>>::hydrate::<FROM_SERVER>(
|
||||
self.as_str(),
|
||||
key,
|
||||
el,
|
||||
@@ -136,19 +142,15 @@ impl AttributeValue for Oco<'static, str> {
|
||||
(el, self)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
Rndr::set_attribute(el, key, &self);
|
||||
fn build(self, el: &R::Element, key: &str) -> Self::State {
|
||||
R::set_attribute(el, key, &self);
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn rebuild(self, key: &str, state: &mut Self::State) {
|
||||
let (el, prev_value) = state;
|
||||
if self != *prev_value {
|
||||
Rndr::set_attribute(el, key, &self);
|
||||
R::set_attribute(el, key, &self);
|
||||
}
|
||||
*prev_value = self;
|
||||
}
|
||||
@@ -172,9 +174,12 @@ impl AttributeValue for Oco<'static, str> {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoClass for Oco<'static, str> {
|
||||
impl<R> IntoClass<R> for Oco<'static, str>
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = (crate::renderer::types::Element, Self);
|
||||
type State = (R::Element, Self);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
@@ -183,28 +188,25 @@ impl IntoClass for Oco<'static, str> {
|
||||
}
|
||||
|
||||
fn to_html(self, class: &mut String) {
|
||||
IntoClass::to_html(self.as_str(), class);
|
||||
IntoClass::<R>::to_html(self.as_str(), class);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
if !FROM_SERVER {
|
||||
Rndr::set_attribute(el, "class", &self);
|
||||
R::set_attribute(el, "class", &self);
|
||||
}
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
Rndr::set_attribute(el, "class", &self);
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
R::set_attribute(el, "class", &self);
|
||||
(el.clone(), self)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let (el, prev) = state;
|
||||
if self != *prev {
|
||||
Rndr::set_attribute(el, "class", &self);
|
||||
R::set_attribute(el, "class", &self);
|
||||
}
|
||||
*prev = self;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::{ReactiveFunction, SharedReactiveFunction, Suspend};
|
||||
use crate::{html::class::IntoClass, renderer::Rndr};
|
||||
use crate::{html::class::IntoClass, renderer::DomRenderer};
|
||||
use any_spawner::Executor;
|
||||
use futures::FutureExt;
|
||||
use reactive_graph::{effect::RenderEffect, signal::guards::ReadGuard};
|
||||
@@ -8,11 +8,12 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
impl<F, C> IntoClass for F
|
||||
impl<F, C, R> IntoClass<R> for F
|
||||
where
|
||||
F: ReactiveFunction<Output = C>,
|
||||
C: IntoClass + 'static,
|
||||
C: IntoClass<R> + 'static,
|
||||
C::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = C::AsyncOutput;
|
||||
type State = RenderEffect<C::State>;
|
||||
@@ -30,7 +31,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
mut self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
// TODO FROM_SERVER vs template
|
||||
let el = el.clone();
|
||||
@@ -45,7 +46,7 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
fn build(mut self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(mut self, el: &R::Element) -> Self::State {
|
||||
let el = el.to_owned();
|
||||
RenderEffect::new(move |prev| {
|
||||
let value = self.invoke();
|
||||
@@ -91,13 +92,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T> IntoClass for (&'static str, F)
|
||||
impl<F, T, R> IntoClass<R> for (&'static str, F)
|
||||
where
|
||||
F: ReactiveFunction<Output = T>,
|
||||
T: Borrow<bool> + Send + 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = (&'static str, bool);
|
||||
type State = RenderEffect<(crate::renderer::types::ClassList, bool)>;
|
||||
type State = RenderEffect<(R::ClassList, bool)>;
|
||||
type Cloneable = (&'static str, SharedReactiveFunction<T>);
|
||||
type CloneableOwned = (&'static str, SharedReactiveFunction<T>);
|
||||
|
||||
@@ -109,63 +111,56 @@ where
|
||||
let (name, mut f) = self;
|
||||
let include = *f.invoke().borrow();
|
||||
if include {
|
||||
<&str as IntoClass>::to_html(name, class);
|
||||
<&str as IntoClass<R>>::to_html(name, class);
|
||||
}
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
// TODO FROM_SERVER vs template
|
||||
let (name, mut f) = self;
|
||||
let class_list = Rndr::class_list(el);
|
||||
let name = Rndr::intern(name);
|
||||
let class_list = R::class_list(el);
|
||||
let name = R::intern(name);
|
||||
|
||||
RenderEffect::new(
|
||||
move |prev: Option<(crate::renderer::types::ClassList, bool)>| {
|
||||
let include = *f.invoke().borrow();
|
||||
if let Some((class_list, prev)) = prev {
|
||||
if include {
|
||||
if !prev {
|
||||
Rndr::add_class(&class_list, name);
|
||||
}
|
||||
} else if prev {
|
||||
Rndr::remove_class(&class_list, name);
|
||||
RenderEffect::new(move |prev: Option<(R::ClassList, bool)>| {
|
||||
let include = *f.invoke().borrow();
|
||||
if let Some((class_list, prev)) = prev {
|
||||
if include {
|
||||
if !prev {
|
||||
R::add_class(&class_list, name);
|
||||
}
|
||||
} else if prev {
|
||||
R::remove_class(&class_list, name);
|
||||
}
|
||||
(class_list.clone(), include)
|
||||
},
|
||||
)
|
||||
}
|
||||
(class_list.clone(), include)
|
||||
})
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
let (name, mut f) = self;
|
||||
let class_list = Rndr::class_list(el);
|
||||
let name = Rndr::intern(name);
|
||||
let class_list = R::class_list(el);
|
||||
let name = R::intern(name);
|
||||
|
||||
RenderEffect::new(
|
||||
move |prev: Option<(crate::renderer::types::ClassList, bool)>| {
|
||||
let include = *f.invoke().borrow();
|
||||
match prev {
|
||||
Some((class_list, prev)) => {
|
||||
if include {
|
||||
if !prev {
|
||||
Rndr::add_class(&class_list, name);
|
||||
}
|
||||
} else if prev {
|
||||
Rndr::remove_class(&class_list, name);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if include {
|
||||
Rndr::add_class(&class_list, name);
|
||||
RenderEffect::new(move |prev: Option<(R::ClassList, bool)>| {
|
||||
let include = *f.invoke().borrow();
|
||||
match prev {
|
||||
Some((class_list, prev)) => {
|
||||
if include {
|
||||
if !prev {
|
||||
R::add_class(&class_list, name);
|
||||
}
|
||||
} else if prev {
|
||||
R::remove_class(&class_list, name);
|
||||
}
|
||||
}
|
||||
(class_list.clone(), include)
|
||||
},
|
||||
)
|
||||
None => {
|
||||
if include {
|
||||
R::add_class(&class_list, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
(class_list.clone(), include)
|
||||
})
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
@@ -178,10 +173,10 @@ where
|
||||
Some((class_list, prev)) => {
|
||||
if include {
|
||||
if !prev {
|
||||
Rndr::add_class(&class_list, name);
|
||||
R::add_class(&class_list, name);
|
||||
}
|
||||
} else if prev {
|
||||
Rndr::remove_class(&class_list, name);
|
||||
R::remove_class(&class_list, name);
|
||||
}
|
||||
(class_list.clone(), include)
|
||||
}
|
||||
@@ -213,14 +208,14 @@ where
|
||||
|
||||
// TODO this needs a non-reactive form too to be restored
|
||||
/*
|
||||
impl<F, T> IntoClass for (Vec<Cow<'static, str>>, F)
|
||||
impl<F, T, R> IntoClass<R> for (Vec<Cow<'static, str>>, F)
|
||||
where
|
||||
F: ReactiveFunction<Output = T>,
|
||||
T: Borrow<bool> + Send + 'static,
|
||||
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = (Vec<Cow<'static, str>>, bool);
|
||||
type State = RenderEffect<(crate::renderer::types::ClassList, bool)>;
|
||||
type State = RenderEffect<(R::ClassList, bool)>;
|
||||
type Cloneable = (Vec<Cow<'static, str>>, SharedReactiveFunction<T>);
|
||||
type CloneableOwned = (Vec<Cow<'static, str>>, SharedReactiveFunction<T>);
|
||||
|
||||
@@ -233,29 +228,29 @@ where
|
||||
let include = *f.invoke().borrow();
|
||||
if include {
|
||||
for name in names {
|
||||
<&str as IntoClass>::to_html(&name, class);
|
||||
<&str as IntoClass<R>>::to_html(&name, class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
// TODO FROM_SERVER vs template
|
||||
let (names, mut f) = self;
|
||||
let class_list = Rndr::class_list(el);
|
||||
let class_list = R::class_list(el);
|
||||
|
||||
RenderEffect::new(move |prev: Option<(crate::renderer::types::ClassList, bool)>| {
|
||||
RenderEffect::new(move |prev: Option<(R::ClassList, bool)>| {
|
||||
let include = *f.invoke().borrow();
|
||||
if let Some((class_list, prev)) = prev {
|
||||
if include {
|
||||
if !prev {
|
||||
for name in &names {
|
||||
// TODO multi-class optimizations here
|
||||
Rndr::add_class(&class_list, name);
|
||||
R::add_class(&class_list, name);
|
||||
}
|
||||
}
|
||||
} else if prev {
|
||||
for name in &names {
|
||||
Rndr::remove_class(&class_list, name);
|
||||
R::remove_class(&class_list, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -263,30 +258,30 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
let (names, mut f) = self;
|
||||
let class_list = Rndr::class_list(el);
|
||||
let class_list = R::class_list(el);
|
||||
|
||||
RenderEffect::new(move |prev: Option<(crate::renderer::types::ClassList, bool)>| {
|
||||
RenderEffect::new(move |prev: Option<(R::ClassList, bool)>| {
|
||||
let include = *f.invoke().borrow();
|
||||
match prev {
|
||||
Some((class_list, prev)) => {
|
||||
if include {
|
||||
for name in &names {
|
||||
if !prev {
|
||||
Rndr::add_class(&class_list, name);
|
||||
R::add_class(&class_list, name);
|
||||
}
|
||||
}
|
||||
} else if prev {
|
||||
for name in &names {
|
||||
Rndr::remove_class(&class_list, name);
|
||||
R::remove_class(&class_list, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if include {
|
||||
for name in &names {
|
||||
Rndr::add_class(&class_list, name);
|
||||
R::add_class(&class_list, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -300,19 +295,19 @@ where
|
||||
let prev_value = state.take_value();
|
||||
|
||||
*state = RenderEffect::new_with_value(
|
||||
move |prev: Option<(crate::renderer::types::ClassList, bool)>| {
|
||||
move |prev: Option<(R::ClassList, bool)>| {
|
||||
let include = *f.invoke().borrow();
|
||||
match prev {
|
||||
Some((class_list, prev)) => {
|
||||
if include {
|
||||
for name in &names {
|
||||
if !prev {
|
||||
Rndr::add_class(&class_list, name);
|
||||
R::add_class(&class_list, name);
|
||||
}
|
||||
}
|
||||
} else if prev {
|
||||
for name in &names {
|
||||
Rndr::remove_class(&class_list, name);
|
||||
R::remove_class(&class_list, name);
|
||||
}
|
||||
}
|
||||
(class_list.clone(), include)
|
||||
@@ -344,12 +339,13 @@ where
|
||||
}
|
||||
*/
|
||||
|
||||
impl<G> IntoClass for ReadGuard<String, G>
|
||||
impl<G, R> IntoClass<R> for ReadGuard<String, G>
|
||||
where
|
||||
G: Deref<Target = String> + Send,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = <String as IntoClass>::State;
|
||||
type State = <String as IntoClass<R>>::State;
|
||||
type Cloneable = Arc<str>;
|
||||
type CloneableOwned = Arc<str>;
|
||||
|
||||
@@ -358,25 +354,25 @@ where
|
||||
}
|
||||
|
||||
fn to_html(self, class: &mut String) {
|
||||
<&str as IntoClass>::to_html(self.deref().as_str(), class);
|
||||
<&str as IntoClass<R>>::to_html(self.deref().as_str(), class);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R>::Element,
|
||||
) -> Self::State {
|
||||
<String as IntoClass>::hydrate::<FROM_SERVER>(
|
||||
<String as IntoClass<R>>::hydrate::<FROM_SERVER>(
|
||||
self.deref().to_owned(),
|
||||
el,
|
||||
)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
<String as IntoClass>::build(self.deref().to_owned(), el)
|
||||
fn build(self, el: &<R>::Element) -> Self::State {
|
||||
<String as IntoClass<R>>::build(self.deref().to_owned(), el)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
<String as IntoClass>::rebuild(self.deref().to_owned(), state)
|
||||
<String as IntoClass<R>>::rebuild(self.deref().to_owned(), state)
|
||||
}
|
||||
|
||||
fn into_cloneable(self) -> Self::Cloneable {
|
||||
@@ -394,12 +390,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<G> IntoClass for (&'static str, ReadGuard<bool, G>)
|
||||
impl<G, R> IntoClass<R> for (&'static str, ReadGuard<bool, G>)
|
||||
where
|
||||
G: Deref<Target = bool> + Send,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = <(&'static str, bool) as IntoClass>::State;
|
||||
type State = <(&'static str, bool) as IntoClass<R>>::State;
|
||||
type Cloneable = (&'static str, bool);
|
||||
type CloneableOwned = (&'static str, bool);
|
||||
|
||||
@@ -408,7 +405,7 @@ where
|
||||
}
|
||||
|
||||
fn to_html(self, class: &mut String) {
|
||||
<(&'static str, bool) as IntoClass>::to_html(
|
||||
<(&'static str, bool) as IntoClass<R>>::to_html(
|
||||
(self.0, *self.1.deref()),
|
||||
class,
|
||||
);
|
||||
@@ -416,23 +413,23 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R>::Element,
|
||||
) -> Self::State {
|
||||
<(&'static str, bool) as IntoClass>::hydrate::<FROM_SERVER>(
|
||||
<(&'static str, bool) as IntoClass<R>>::hydrate::<FROM_SERVER>(
|
||||
(self.0, *self.1.deref()),
|
||||
el,
|
||||
)
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
<(&'static str, bool) as IntoClass>::build(
|
||||
fn build(self, el: &<R>::Element) -> Self::State {
|
||||
<(&'static str, bool) as IntoClass<R>>::build(
|
||||
(self.0, *self.1.deref()),
|
||||
el,
|
||||
)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
<(&'static str, bool) as IntoClass>::rebuild(
|
||||
<(&'static str, bool) as IntoClass<R>>::rebuild(
|
||||
(self.0, *self.1.deref()),
|
||||
state,
|
||||
)
|
||||
@@ -457,13 +454,14 @@ where
|
||||
mod stable {
|
||||
macro_rules! class_signal_arena {
|
||||
($sig:ident) => {
|
||||
impl<C, S> IntoClass for $sig<C, S>
|
||||
impl<C, R, S> IntoClass<R> for $sig<C, S>
|
||||
where
|
||||
$sig<C, S>: Get<Value = C>,
|
||||
S: Send + Sync + 'static,
|
||||
S: Storage<C> + Storage<Option<C>>,
|
||||
C: IntoClass + Send + Sync + Clone + 'static,
|
||||
C: IntoClass<R> + Send + Sync + Clone + 'static,
|
||||
C::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = RenderEffect<C::State>;
|
||||
@@ -481,15 +479,12 @@ mod stable {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(el)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
(move || self.get()).build(el)
|
||||
}
|
||||
|
||||
@@ -512,15 +507,15 @@ mod stable {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> IntoClass for (&'static str, $sig<bool, S>)
|
||||
impl<R, S> IntoClass<R> for (&'static str, $sig<bool, S>)
|
||||
where
|
||||
$sig<bool, S>: Get<Value = bool>,
|
||||
S: Send + 'static,
|
||||
S: Storage<bool>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State =
|
||||
RenderEffect<(crate::renderer::types::ClassList, bool)>;
|
||||
type State = RenderEffect<(R::ClassList, bool)>;
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
@@ -532,29 +527,29 @@ mod stable {
|
||||
let (name, f) = self;
|
||||
let include = f.get();
|
||||
if include {
|
||||
<&str as IntoClass>::to_html(name, class);
|
||||
<&str as IntoClass<R>>::to_html(name, class);
|
||||
}
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
IntoClass::hydrate::<FROM_SERVER>(
|
||||
IntoClass::<R>::hydrate::<FROM_SERVER>(
|
||||
(self.0, move || self.1.get()),
|
||||
el,
|
||||
)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
IntoClass::build((self.0, move || self.1.get()), el)
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
IntoClass::<R>::build((self.0, move || self.1.get()), el)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
IntoClass::rebuild((self.0, move || self.1.get()), state)
|
||||
IntoClass::<R>::rebuild(
|
||||
(self.0, move || self.1.get()),
|
||||
state,
|
||||
)
|
||||
}
|
||||
|
||||
fn into_cloneable(self) -> Self::Cloneable {
|
||||
@@ -576,11 +571,12 @@ mod stable {
|
||||
|
||||
macro_rules! class_signal {
|
||||
($sig:ident) => {
|
||||
impl<C> IntoClass for $sig<C>
|
||||
impl<C, R> IntoClass<R> for $sig<C>
|
||||
where
|
||||
$sig<C>: Get<Value = C>,
|
||||
C: IntoClass + Send + Sync + Clone + 'static,
|
||||
C: IntoClass<R> + Send + Sync + Clone + 'static,
|
||||
C::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = RenderEffect<C::State>;
|
||||
@@ -598,15 +594,12 @@ mod stable {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(el)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
(move || self.get()).build(el)
|
||||
}
|
||||
|
||||
@@ -629,13 +622,13 @@ mod stable {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoClass for (&'static str, $sig<bool>)
|
||||
impl<R> IntoClass<R> for (&'static str, $sig<bool>)
|
||||
where
|
||||
$sig<bool>: Get<Value = bool>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State =
|
||||
RenderEffect<(crate::renderer::types::ClassList, bool)>;
|
||||
type State = RenderEffect<(R::ClassList, bool)>;
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
@@ -647,29 +640,29 @@ mod stable {
|
||||
let (name, f) = self;
|
||||
let include = f.get();
|
||||
if include {
|
||||
<&str as IntoClass>::to_html(name, class);
|
||||
<&str as IntoClass<R>>::to_html(name, class);
|
||||
}
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
IntoClass::hydrate::<FROM_SERVER>(
|
||||
IntoClass::<R>::hydrate::<FROM_SERVER>(
|
||||
(self.0, move || self.1.get()),
|
||||
el,
|
||||
)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
IntoClass::build((self.0, move || self.1.get()), el)
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
IntoClass::<R>::build((self.0, move || self.1.get()), el)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
IntoClass::rebuild((self.0, move || self.1.get()), state)
|
||||
IntoClass::<R>::rebuild(
|
||||
(self.0, move || self.1.get()),
|
||||
state,
|
||||
)
|
||||
}
|
||||
|
||||
fn into_cloneable(self) -> Self::Cloneable {
|
||||
@@ -690,7 +683,7 @@ mod stable {
|
||||
}
|
||||
|
||||
use super::RenderEffect;
|
||||
use crate::html::class::IntoClass;
|
||||
use crate::{html::class::IntoClass, renderer::DomRenderer};
|
||||
use reactive_graph::{
|
||||
computed::{ArcMemo, Memo},
|
||||
owner::Storage,
|
||||
@@ -710,13 +703,14 @@ mod stable {
|
||||
class_signal!(ArcSignal);
|
||||
}
|
||||
|
||||
impl<Fut> IntoClass for Suspend<Fut>
|
||||
impl<Fut, Rndr> IntoClass<Rndr> for Suspend<Fut>
|
||||
where
|
||||
Fut: Clone + Future + Send + 'static,
|
||||
Fut::Output: IntoClass,
|
||||
Fut::Output: IntoClass<Rndr>,
|
||||
Rndr: DomRenderer + 'static,
|
||||
{
|
||||
type AsyncOutput = Fut::Output;
|
||||
type State = Rc<RefCell<Option<<Fut::Output as IntoClass>::State>>>;
|
||||
type State = Rc<RefCell<Option<<Fut::Output as IntoClass<Rndr>>::State>>>;
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
@@ -734,7 +728,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<Rndr>::Element,
|
||||
) -> Self::State {
|
||||
let el = el.to_owned();
|
||||
let state = Rc::new(RefCell::new(None));
|
||||
@@ -749,7 +743,7 @@ where
|
||||
state
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &<Rndr>::Element) -> Self::State {
|
||||
let el = el.to_owned();
|
||||
let state = Rc::new(RefCell::new(None));
|
||||
Executor::spawn_local({
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
use super::{ReactiveFunction, SharedReactiveFunction};
|
||||
use crate::html::element::InnerHtmlValue;
|
||||
use crate::{
|
||||
html::element::InnerHtmlValue,
|
||||
renderer::{DomRenderer, Renderer},
|
||||
};
|
||||
use reactive_graph::effect::RenderEffect;
|
||||
|
||||
impl<F, V> InnerHtmlValue for F
|
||||
impl<F, V, R> InnerHtmlValue<R> for F
|
||||
where
|
||||
F: ReactiveFunction<Output = V>,
|
||||
V: InnerHtmlValue + 'static,
|
||||
V: InnerHtmlValue<R> + 'static,
|
||||
V::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = V::AsyncOutput;
|
||||
type State = RenderEffect<V::State>;
|
||||
@@ -26,7 +30,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
mut self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
let el = el.to_owned();
|
||||
RenderEffect::new(move |prev| {
|
||||
@@ -40,7 +44,7 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
fn build(mut self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(mut self, el: &<R as Renderer>::Element) -> Self::State {
|
||||
let el = el.to_owned();
|
||||
RenderEffect::new(move |prev| {
|
||||
let value = self.invoke();
|
||||
@@ -88,7 +92,10 @@ where
|
||||
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
mod stable {
|
||||
use crate::html::element::InnerHtmlValue;
|
||||
use crate::{
|
||||
html::element::InnerHtmlValue,
|
||||
renderer::{DomRenderer, Renderer},
|
||||
};
|
||||
use reactive_graph::{
|
||||
computed::{ArcMemo, Memo},
|
||||
effect::RenderEffect,
|
||||
@@ -100,11 +107,12 @@ mod stable {
|
||||
|
||||
macro_rules! inner_html_signal {
|
||||
($sig:ident) => {
|
||||
impl<V> InnerHtmlValue for $sig<V>
|
||||
impl<V, R> InnerHtmlValue<R> for $sig<V>
|
||||
where
|
||||
$sig<V>: Get<Value = V>,
|
||||
V: InnerHtmlValue + Send + Sync + Clone + 'static,
|
||||
V: InnerHtmlValue<R> + Send + Sync + Clone + 'static,
|
||||
V::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = RenderEffect<V::State>;
|
||||
@@ -124,15 +132,12 @@ mod stable {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(el)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn build(self, el: &<R as Renderer>::Element) -> Self::State {
|
||||
(move || self.get()).build(el)
|
||||
}
|
||||
|
||||
@@ -159,13 +164,14 @@ mod stable {
|
||||
|
||||
macro_rules! inner_html_signal_arena {
|
||||
($sig:ident) => {
|
||||
impl<V, S> InnerHtmlValue for $sig<V, S>
|
||||
impl<V, R, S> InnerHtmlValue<R> for $sig<V, S>
|
||||
where
|
||||
$sig<V, S>: Get<Value = V>,
|
||||
S: Send + Sync + 'static,
|
||||
S: Storage<V>,
|
||||
V: InnerHtmlValue + Send + Sync + Clone + 'static,
|
||||
V: InnerHtmlValue<R> + Send + Sync + Clone + 'static,
|
||||
V::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = RenderEffect<V::State>;
|
||||
@@ -185,15 +191,12 @@ mod stable {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(el)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn build(self, el: &<R as Renderer>::Element) -> Self::State {
|
||||
(move || self.get()).build(el)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
html::attribute::{Attribute, AttributeValue},
|
||||
hydration::Cursor,
|
||||
renderer::Rndr,
|
||||
renderer::Renderer,
|
||||
ssr::StreamBuilder,
|
||||
view::{
|
||||
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
|
||||
@@ -47,11 +47,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, V> Render for F
|
||||
impl<F, V, R> Render<R> for F
|
||||
where
|
||||
F: ReactiveFunction<Output = V>,
|
||||
V: Render,
|
||||
V: Render<R>,
|
||||
V::State: 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
type State = RenderEffectState<V::State>;
|
||||
|
||||
@@ -87,9 +88,10 @@ impl<T> From<RenderEffect<T>> for RenderEffectState<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Mountable for RenderEffectState<T>
|
||||
impl<T, R> Mountable<R> for RenderEffectState<T>
|
||||
where
|
||||
T: Mountable,
|
||||
T: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
if let Some(ref mut inner) = self.0 {
|
||||
@@ -97,17 +99,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
) {
|
||||
fn mount(&mut self, parent: &R::Element, marker: Option<&R::Node>) {
|
||||
if let Some(ref mut inner) = self.0 {
|
||||
inner.mount(parent, marker);
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
if let Some(inner) = &self.0 {
|
||||
inner.insert_before_this(child)
|
||||
} else {
|
||||
@@ -116,11 +114,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, V> RenderHtml for F
|
||||
impl<F, V, R> RenderHtml<R> for F
|
||||
where
|
||||
F: ReactiveFunction<Output = V>,
|
||||
V: RenderHtml + 'static,
|
||||
V: RenderHtml<R> + 'static,
|
||||
V::State: 'static,
|
||||
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type AsyncOutput = V::AsyncOutput;
|
||||
|
||||
@@ -169,7 +169,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
mut self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let cursor = cursor.clone();
|
||||
@@ -187,29 +187,31 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, V> AddAnyAttr for F
|
||||
impl<F, V, R> AddAnyAttr<R> for F
|
||||
where
|
||||
F: ReactiveFunction<Output = V>,
|
||||
V: RenderHtml + 'static,
|
||||
V: RenderHtml<R> + 'static,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> =
|
||||
type Output<SomeNewAttr: Attribute<R>> =
|
||||
Box<dyn FnMut() -> V::Output<SomeNewAttr::CloneableOwned> + Send>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
mut self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<R>,
|
||||
{
|
||||
let attr = attr.into_cloneable_owned();
|
||||
Box::new(move || self.invoke().add_any_attr(attr.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<M> Mountable for RenderEffect<M>
|
||||
impl<M, R> Mountable<R> for RenderEffect<M>
|
||||
where
|
||||
M: Mountable + 'static,
|
||||
M: Mountable<R> + 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
self.with_value_mut(|state| state.unmount());
|
||||
@@ -217,23 +219,24 @@ where
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
parent: &<R as Renderer>::Element,
|
||||
marker: Option<&<R as Renderer>::Node>,
|
||||
) {
|
||||
self.with_value_mut(|state| {
|
||||
state.mount(parent, marker);
|
||||
});
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
self.with_value_mut(|value| value.insert_before_this(child))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M, E> Mountable for Result<M, E>
|
||||
impl<M, E, R> Mountable<R> for Result<M, E>
|
||||
where
|
||||
M: Mountable,
|
||||
M: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
if let Ok(ref mut inner) = self {
|
||||
@@ -243,15 +246,15 @@ where
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
parent: &<R as Renderer>::Element,
|
||||
marker: Option<&<R as Renderer>::Node>,
|
||||
) {
|
||||
if let Ok(ref mut inner) = self {
|
||||
inner.mount(parent, marker);
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
if let Ok(inner) = &self {
|
||||
inner.insert_before_this(child)
|
||||
} else {
|
||||
@@ -261,11 +264,12 @@ where
|
||||
}
|
||||
|
||||
// Dynamic attributes
|
||||
impl<F, V> AttributeValue for F
|
||||
impl<F, V, R> AttributeValue<R> for F
|
||||
where
|
||||
F: ReactiveFunction<Output = V>,
|
||||
V: AttributeValue + 'static,
|
||||
V: AttributeValue<R> + 'static,
|
||||
V::State: 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = V::AsyncOutput;
|
||||
type State = RenderEffect<V::State>;
|
||||
@@ -286,9 +290,9 @@ where
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
mut self,
|
||||
key: &str,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
let key = Rndr::intern(key);
|
||||
let key = R::intern(key);
|
||||
let key = key.to_owned();
|
||||
let el = el.to_owned();
|
||||
|
||||
@@ -305,10 +309,10 @@ where
|
||||
|
||||
fn build(
|
||||
mut self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
let key = Rndr::intern(key);
|
||||
let key = R::intern(key);
|
||||
let key = key.to_owned();
|
||||
let el = el.to_owned();
|
||||
|
||||
@@ -324,7 +328,7 @@ where
|
||||
}
|
||||
|
||||
fn rebuild(mut self, key: &str, state: &mut Self::State) {
|
||||
let key = Rndr::intern(key);
|
||||
let key = R::intern(key);
|
||||
let key = key.to_owned();
|
||||
let prev_value = state.take_value();
|
||||
|
||||
@@ -359,11 +363,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Fut, V> AttributeValue for Suspend<Fut>
|
||||
impl<Fut, V, R> AttributeValue<R> for Suspend<Fut>
|
||||
where
|
||||
Fut: Future<Output = V> + Send + 'static,
|
||||
V: AttributeValue + 'static,
|
||||
V: AttributeValue<R> + 'static,
|
||||
V::State: 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
type State = Rc<RefCell<Option<V::State>>>;
|
||||
type AsyncOutput = V;
|
||||
@@ -386,7 +391,7 @@ where
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
key: &str,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
let key = key.to_owned();
|
||||
let el = el.to_owned();
|
||||
@@ -402,11 +407,7 @@ where
|
||||
state
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
fn build(self, el: &<R as Renderer>::Element, key: &str) -> Self::State {
|
||||
let key = key.to_owned();
|
||||
let el = el.to_owned();
|
||||
let state = Rc::new(RefCell::new(None));
|
||||
@@ -501,10 +502,11 @@ mod stable {
|
||||
use crate::{
|
||||
html::attribute::{Attribute, AttributeValue},
|
||||
hydration::Cursor,
|
||||
renderer::Renderer,
|
||||
ssr::StreamBuilder,
|
||||
view::{
|
||||
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
|
||||
RenderHtml,
|
||||
RenderHtml, WrappedView,
|
||||
},
|
||||
};
|
||||
use reactive_graph::{
|
||||
@@ -518,11 +520,13 @@ mod stable {
|
||||
|
||||
macro_rules! signal_impl {
|
||||
($sig:ident $dry_resolve:literal) => {
|
||||
impl<V> Render for $sig<V>
|
||||
impl<V, R> Render<R> for $sig<V>
|
||||
where
|
||||
$sig<V>: Get<Value = V>,
|
||||
V: Render + Clone + Send + Sync + 'static,
|
||||
V: Render<R> + Clone + Send + Sync + 'static,
|
||||
V::State: 'static,
|
||||
|
||||
R: Renderer,
|
||||
{
|
||||
type State = RenderEffectState<V::State>;
|
||||
|
||||
@@ -540,30 +544,34 @@ mod stable {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> AddAnyAttr for $sig<V>
|
||||
impl<V, R> AddAnyAttr<R> for $sig<V>
|
||||
where
|
||||
$sig<V>: Get<Value = V>,
|
||||
V: RenderHtml + Clone + Send + Sync + 'static,
|
||||
V: RenderHtml<R> + Clone + Send + Sync + 'static,
|
||||
V::State: 'static,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> = Self;
|
||||
// WrappedView is necessary here for compiler reasons I don't completely understand
|
||||
type Output<SomeNewAttr: Attribute<R>> = WrappedView<$sig<V>>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<R>,
|
||||
{
|
||||
todo!()
|
||||
WrappedView::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> RenderHtml for $sig<V>
|
||||
impl<V, R> RenderHtml<R> for $sig<V>
|
||||
where
|
||||
$sig<V>: Get<Value = V>,
|
||||
V: RenderHtml + Clone + Send + Sync + 'static,
|
||||
V: RenderHtml<R> + Clone + Send + Sync + 'static,
|
||||
V::State: 'static,
|
||||
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
@@ -614,7 +622,7 @@ mod stable {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
(move || self.get())
|
||||
@@ -622,11 +630,12 @@ mod stable {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> AttributeValue for $sig<V>
|
||||
impl<V, R> AttributeValue<R> for $sig<V>
|
||||
where
|
||||
$sig<V>: Get<Value = V>,
|
||||
V: AttributeValue + Clone + Send + Sync + 'static,
|
||||
V: AttributeValue<R> + Clone + Send + Sync + 'static,
|
||||
V::State: 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = RenderEffect<V::State>;
|
||||
@@ -647,14 +656,14 @@ mod stable {
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
key: &str,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(key, el)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
(move || self.get()).build(el, key)
|
||||
@@ -683,13 +692,15 @@ mod stable {
|
||||
|
||||
macro_rules! signal_impl_arena {
|
||||
($sig:ident $dry_resolve:literal) => {
|
||||
impl<V, S> Render for $sig<V, S>
|
||||
impl<V, R, S> Render<R> for $sig<V, S>
|
||||
where
|
||||
$sig<V, S>: Get<Value = V>,
|
||||
S: Send + Sync + 'static,
|
||||
S: Storage<V> + Storage<Option<V>>,
|
||||
V: Render + Send + Sync + Clone + 'static,
|
||||
V: Render<R> + Send + Sync + Clone + 'static,
|
||||
V::State: 'static,
|
||||
|
||||
R: Renderer,
|
||||
{
|
||||
type State = RenderEffectState<V::State>;
|
||||
|
||||
@@ -707,34 +718,38 @@ mod stable {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, S> AddAnyAttr for $sig<V, S>
|
||||
impl<V, R, S> AddAnyAttr<R> for $sig<V, S>
|
||||
where
|
||||
$sig<V, S>: Get<Value = V>,
|
||||
S: Send + Sync + 'static,
|
||||
S: Storage<V> + Storage<Option<V>>,
|
||||
V: RenderHtml + Clone + Send + Sync + 'static,
|
||||
V: RenderHtml<R> + Clone + Send + Sync + 'static,
|
||||
V::State: 'static,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> = $sig<V, S>;
|
||||
type Output<SomeNewAttr: Attribute<R>> =
|
||||
WrappedView<$sig<V, S>>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<R>,
|
||||
{
|
||||
todo!()
|
||||
WrappedView::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, S> RenderHtml for $sig<V, S>
|
||||
impl<V, R, S> RenderHtml<R> for $sig<V, S>
|
||||
where
|
||||
$sig<V, S>: Get<Value = V>,
|
||||
S: Send + Sync + 'static,
|
||||
S: Storage<V> + Storage<Option<V>>,
|
||||
V: RenderHtml + Clone + Send + Sync + 'static,
|
||||
V: RenderHtml<R> + Clone + Send + Sync + 'static,
|
||||
V::State: 'static,
|
||||
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
@@ -785,7 +800,7 @@ mod stable {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
(move || self.get())
|
||||
@@ -793,13 +808,14 @@ mod stable {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, S> AttributeValue for $sig<V, S>
|
||||
impl<V, R, S> AttributeValue<R> for $sig<V, S>
|
||||
where
|
||||
$sig<V, S>: Get<Value = V>,
|
||||
S: Storage<V> + Storage<Option<V>>,
|
||||
S: Send + Sync + 'static,
|
||||
V: AttributeValue + Send + Sync + Clone + 'static,
|
||||
V: AttributeValue<R> + Send + Sync + Clone + 'static,
|
||||
V::State: 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = RenderEffect<V::State>;
|
||||
@@ -820,14 +836,14 @@ mod stable {
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
key: &str,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(key, el)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
(move || self.get()).build(el, key)
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::html::{element::ElementType, node_ref::NodeRefContainer};
|
||||
use crate::{
|
||||
html::{element::ElementType, node_ref::NodeRefContainer},
|
||||
renderer::{dom::Dom, Renderer},
|
||||
};
|
||||
use reactive_graph::{
|
||||
signal::RwSignal,
|
||||
traits::{DefinedAt, Set, Track, WithUntracked},
|
||||
@@ -52,12 +55,12 @@ where
|
||||
{
|
||||
}
|
||||
|
||||
impl<E> NodeRefContainer<E> for NodeRef<E>
|
||||
impl<E> NodeRefContainer<E, Dom> for NodeRef<E>
|
||||
where
|
||||
E: ElementType,
|
||||
E::Output: JsCast + 'static,
|
||||
{
|
||||
fn load(self, el: &crate::renderer::types::Element) {
|
||||
fn load(self, el: &<Dom as Renderer>::Element) {
|
||||
// safe to construct SendWrapper here, because it will only run in the browser
|
||||
// so it will always be accessed or dropped from the main thread
|
||||
self.0
|
||||
|
||||
@@ -2,56 +2,75 @@ use crate::{
|
||||
html::attribute::Attribute,
|
||||
hydration::Cursor,
|
||||
prelude::Mountable,
|
||||
renderer::Renderer,
|
||||
ssr::StreamBuilder,
|
||||
view::{add_attr::AddAnyAttr, Position, PositionState, Render, RenderHtml},
|
||||
};
|
||||
use reactive_graph::{computed::ScopedFuture, owner::Owner};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// A view wrapper that sets the reactive [`Owner`] to a particular owner whenever it is rendered.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OwnedView<T> {
|
||||
pub struct OwnedView<T, R> {
|
||||
owner: Owner,
|
||||
view: T,
|
||||
rndr: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<T> OwnedView<T> {
|
||||
impl<T, R> OwnedView<T, R> {
|
||||
/// Wraps a view with the current owner.
|
||||
pub fn new(view: T) -> Self {
|
||||
let owner = Owner::current().expect("no reactive owner");
|
||||
Self { owner, view }
|
||||
Self {
|
||||
owner,
|
||||
view,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps a view with the given owner.
|
||||
pub fn new_with_owner(view: T, owner: Owner) -> Self {
|
||||
Self { owner, view }
|
||||
Self {
|
||||
owner,
|
||||
view,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retained view state for an [`OwnedView`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OwnedViewState<T>
|
||||
pub struct OwnedViewState<T, R>
|
||||
where
|
||||
T: Mountable,
|
||||
T: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
owner: Owner,
|
||||
state: T,
|
||||
rndr: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<T> OwnedViewState<T>
|
||||
impl<T, R> OwnedViewState<T, R>
|
||||
where
|
||||
T: Mountable,
|
||||
T: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
/// Wraps a state with the given owner.
|
||||
fn new(state: T, owner: Owner) -> Self {
|
||||
Self { owner, state }
|
||||
Self {
|
||||
owner,
|
||||
state,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Render for OwnedView<T>
|
||||
impl<T, R> Render<R> for OwnedView<T, R>
|
||||
where
|
||||
T: Render,
|
||||
T: Render<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
type State = OwnedViewState<T::State>;
|
||||
type State = OwnedViewState<T::State, R>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let state = self.owner.with(|| self.view.build());
|
||||
@@ -65,33 +84,37 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AddAnyAttr for OwnedView<T>
|
||||
impl<T, R> AddAnyAttr<R> for OwnedView<T, R>
|
||||
where
|
||||
T: AddAnyAttr,
|
||||
T: AddAnyAttr<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> = OwnedView<T::Output<SomeNewAttr>>;
|
||||
type Output<SomeNewAttr: Attribute<R>> =
|
||||
OwnedView<T::Output<SomeNewAttr>, R>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<R>,
|
||||
{
|
||||
let OwnedView { owner, view } = self;
|
||||
let OwnedView { owner, view, rndr } = self;
|
||||
OwnedView {
|
||||
owner,
|
||||
view: view.add_any_attr(attr),
|
||||
rndr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RenderHtml for OwnedView<T>
|
||||
impl<T, R> RenderHtml<R> for OwnedView<T, R>
|
||||
where
|
||||
T: RenderHtml,
|
||||
T: RenderHtml<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
// TODO
|
||||
type AsyncOutput = OwnedView<T::AsyncOutput>;
|
||||
type AsyncOutput = OwnedView<T::AsyncOutput, R>;
|
||||
|
||||
const MIN_LENGTH: usize = T::MIN_LENGTH;
|
||||
|
||||
@@ -135,7 +158,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let state = self
|
||||
@@ -145,11 +168,11 @@ where
|
||||
}
|
||||
|
||||
async fn resolve(self) -> Self::AsyncOutput {
|
||||
let OwnedView { owner, view } = self;
|
||||
let OwnedView { owner, view, rndr } = self;
|
||||
let view = owner
|
||||
.with(|| ScopedFuture::new(async move { view.resolve().await }))
|
||||
.await;
|
||||
OwnedView { owner, view }
|
||||
OwnedView { owner, view, rndr }
|
||||
}
|
||||
|
||||
fn dry_resolve(&mut self) {
|
||||
@@ -157,9 +180,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Mountable for OwnedViewState<T>
|
||||
impl<T, R> Mountable<R> for OwnedViewState<T, R>
|
||||
where
|
||||
T: Mountable,
|
||||
T: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
self.state.unmount();
|
||||
@@ -167,13 +191,13 @@ where
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
parent: &<R as Renderer>::Element,
|
||||
marker: Option<&<R as Renderer>::Node>,
|
||||
) {
|
||||
self.state.mount(parent, marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
self.state.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
use super::{ReactiveFunction, SharedReactiveFunction};
|
||||
use crate::{html::property::IntoProperty, renderer::Rndr};
|
||||
use crate::{
|
||||
html::property::IntoProperty,
|
||||
renderer::{DomRenderer, Renderer},
|
||||
};
|
||||
use reactive_graph::effect::RenderEffect;
|
||||
|
||||
// These do update during hydration because properties don't exist in the DOM
|
||||
impl<F, V> IntoProperty for F
|
||||
impl<F, V, R> IntoProperty<R> for F
|
||||
where
|
||||
F: ReactiveFunction<Output = V>,
|
||||
V: IntoProperty + 'static,
|
||||
V: IntoProperty<R> + 'static,
|
||||
V::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = RenderEffect<V::State>;
|
||||
type Cloneable = SharedReactiveFunction<V>;
|
||||
@@ -15,10 +19,10 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
mut self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
let key = Rndr::intern(key);
|
||||
let key = R::intern(key);
|
||||
let key = key.to_owned();
|
||||
let el = el.to_owned();
|
||||
|
||||
@@ -35,10 +39,10 @@ where
|
||||
|
||||
fn build(
|
||||
mut self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
let key = Rndr::intern(key);
|
||||
let key = R::intern(key);
|
||||
let key = key.to_owned();
|
||||
let el = el.to_owned();
|
||||
|
||||
@@ -81,7 +85,10 @@ where
|
||||
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
mod stable {
|
||||
use crate::html::property::IntoProperty;
|
||||
use crate::{
|
||||
html::property::IntoProperty,
|
||||
renderer::{DomRenderer, Renderer},
|
||||
};
|
||||
use reactive_graph::{
|
||||
computed::{ArcMemo, Memo},
|
||||
effect::RenderEffect,
|
||||
@@ -93,11 +100,12 @@ mod stable {
|
||||
|
||||
macro_rules! property_signal {
|
||||
($sig:ident) => {
|
||||
impl<V> IntoProperty for $sig<V>
|
||||
impl<V, R> IntoProperty<R> for $sig<V>
|
||||
where
|
||||
$sig<V>: Get<Value = V>,
|
||||
V: IntoProperty + Send + Sync + Clone + 'static,
|
||||
V: IntoProperty<R> + Send + Sync + Clone + 'static,
|
||||
V::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = RenderEffect<V::State>;
|
||||
type Cloneable = Self;
|
||||
@@ -105,7 +113,7 @@ mod stable {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(el, key)
|
||||
@@ -113,7 +121,7 @@ mod stable {
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
(move || self.get()).build(el, key)
|
||||
@@ -136,13 +144,14 @@ mod stable {
|
||||
|
||||
macro_rules! property_signal_arena {
|
||||
($sig:ident) => {
|
||||
impl<V, S> IntoProperty for $sig<V, S>
|
||||
impl<V, R, S> IntoProperty<R> for $sig<V, S>
|
||||
where
|
||||
$sig<V, S>: Get<Value = V>,
|
||||
S: Send + Sync + 'static,
|
||||
S: Storage<V> + Storage<Option<V>>,
|
||||
V: IntoProperty + Send + Sync + Clone + 'static,
|
||||
V: IntoProperty<R> + Send + Sync + Clone + 'static,
|
||||
V::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = RenderEffect<V::State>;
|
||||
type Cloneable = Self;
|
||||
@@ -150,7 +159,7 @@ mod stable {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(el, key)
|
||||
@@ -158,7 +167,7 @@ mod stable {
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
(move || self.get()).build(el, key)
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
use super::{ReactiveFunction, SharedReactiveFunction, Suspend};
|
||||
use crate::{html::style::IntoStyle, renderer::Rndr};
|
||||
use crate::{html::style::IntoStyle, renderer::DomRenderer};
|
||||
use any_spawner::Executor;
|
||||
use futures::FutureExt;
|
||||
use reactive_graph::effect::RenderEffect;
|
||||
use std::{borrow::Cow, cell::RefCell, future::Future, rc::Rc};
|
||||
|
||||
impl<F, S> IntoStyle for (&'static str, F)
|
||||
impl<F, S, R> IntoStyle<R> for (&'static str, F)
|
||||
where
|
||||
F: ReactiveFunction<Output = S>,
|
||||
S: Into<Cow<'static, str>> + 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = RenderEffect<(
|
||||
crate::renderer::types::CssStyleDeclaration,
|
||||
Cow<'static, str>,
|
||||
)>;
|
||||
type State = RenderEffect<(R::CssStyleDeclaration, Cow<'static, str>)>;
|
||||
type Cloneable = (&'static str, SharedReactiveFunction<S>);
|
||||
type CloneableOwned = (&'static str, SharedReactiveFunction<S>);
|
||||
|
||||
@@ -27,23 +25,20 @@ where
|
||||
style.push(';');
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
let (name, mut f) = self;
|
||||
let name = Rndr::intern(name);
|
||||
let name = R::intern(name);
|
||||
// TODO FROM_SERVER vs template
|
||||
let style = Rndr::style(el);
|
||||
let style = R::style(el);
|
||||
RenderEffect::new(move |prev| {
|
||||
let value = f.invoke().into();
|
||||
if let Some(mut state) = prev {
|
||||
let (style, prev): &mut (
|
||||
crate::renderer::types::CssStyleDeclaration,
|
||||
R::CssStyleDeclaration,
|
||||
Cow<'static, str>,
|
||||
) = &mut state;
|
||||
if &value != prev {
|
||||
Rndr::set_css_property(style, name, &value);
|
||||
R::set_css_property(style, name, &value);
|
||||
}
|
||||
*prev = value;
|
||||
state
|
||||
@@ -51,32 +46,32 @@ where
|
||||
// only set the style in template mode
|
||||
// in server mode, it's already been set
|
||||
if !FROM_SERVER {
|
||||
Rndr::set_css_property(&style, name, &value);
|
||||
R::set_css_property(&style, name, &value);
|
||||
}
|
||||
(style.clone(), value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
let (name, mut f) = self;
|
||||
let name = Rndr::intern(name);
|
||||
let style = Rndr::style(el);
|
||||
let name = R::intern(name);
|
||||
let style = R::style(el);
|
||||
RenderEffect::new(move |prev| {
|
||||
let value = f.invoke().into();
|
||||
if let Some(mut state) = prev {
|
||||
let (style, prev): &mut (
|
||||
crate::renderer::types::CssStyleDeclaration,
|
||||
R::CssStyleDeclaration,
|
||||
Cow<'static, str>,
|
||||
) = &mut state;
|
||||
if &value != prev {
|
||||
Rndr::set_css_property(style, name, &value);
|
||||
R::set_css_property(style, name, &value);
|
||||
}
|
||||
*prev = value;
|
||||
state
|
||||
} else {
|
||||
// always set the style initially without checking
|
||||
Rndr::set_css_property(&style, name, &value);
|
||||
R::set_css_property(&style, name, &value);
|
||||
(style.clone(), value)
|
||||
}
|
||||
})
|
||||
@@ -91,7 +86,7 @@ where
|
||||
if let Some(mut state) = prev {
|
||||
let (style, prev) = &mut state;
|
||||
if &value != prev {
|
||||
Rndr::set_css_property(style, name, &value);
|
||||
R::set_css_property(style, name, &value);
|
||||
}
|
||||
*prev = value;
|
||||
state
|
||||
@@ -120,11 +115,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, C> IntoStyle for F
|
||||
impl<F, C, R> IntoStyle<R> for F
|
||||
where
|
||||
F: ReactiveFunction<Output = C>,
|
||||
C: IntoStyle + 'static,
|
||||
C: IntoStyle<R> + 'static,
|
||||
C::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = C::AsyncOutput;
|
||||
type State = RenderEffect<C::State>;
|
||||
@@ -138,7 +134,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
mut self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
// TODO FROM_SERVER vs template
|
||||
let el = el.clone();
|
||||
@@ -153,7 +149,7 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
fn build(mut self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(mut self, el: &R::Element) -> Self::State {
|
||||
let el = el.clone();
|
||||
RenderEffect::new(move |prev| {
|
||||
let value = self.invoke();
|
||||
@@ -203,11 +199,12 @@ where
|
||||
mod stable {
|
||||
macro_rules! style_signal {
|
||||
($sig:ident) => {
|
||||
impl<C> IntoStyle for $sig<C>
|
||||
impl<C, R> IntoStyle<R> for $sig<C>
|
||||
where
|
||||
$sig<C>: Get<Value = C>,
|
||||
C: IntoStyle + Clone + Send + Sync + 'static,
|
||||
C: IntoStyle<R> + Clone + Send + Sync + 'static,
|
||||
C::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = RenderEffect<C::State>;
|
||||
@@ -221,15 +218,12 @@ mod stable {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(el)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
(move || self.get()).build(el)
|
||||
}
|
||||
|
||||
@@ -252,42 +246,44 @@ mod stable {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> IntoStyle for (&'static str, $sig<S>)
|
||||
impl<R, S> IntoStyle<R> for (&'static str, $sig<S>)
|
||||
where
|
||||
$sig<S>: Get<Value = S>,
|
||||
S: Into<Cow<'static, str>> + Send + Sync + Clone + 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = RenderEffect<(
|
||||
crate::renderer::types::CssStyleDeclaration,
|
||||
Cow<'static, str>,
|
||||
)>;
|
||||
type State =
|
||||
RenderEffect<(R::CssStyleDeclaration, Cow<'static, str>)>;
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
fn to_html(self, style: &mut String) {
|
||||
IntoStyle::to_html((self.0, move || self.1.get()), style)
|
||||
IntoStyle::<R>::to_html(
|
||||
(self.0, move || self.1.get()),
|
||||
style,
|
||||
)
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
IntoStyle::hydrate::<FROM_SERVER>(
|
||||
IntoStyle::<R>::hydrate::<FROM_SERVER>(
|
||||
(self.0, move || self.1.get()),
|
||||
el,
|
||||
)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
IntoStyle::build((self.0, move || self.1.get()), el)
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
IntoStyle::<R>::build((self.0, move || self.1.get()), el)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
IntoStyle::rebuild((self.0, move || self.1.get()), state)
|
||||
IntoStyle::<R>::rebuild(
|
||||
(self.0, move || self.1.get()),
|
||||
state,
|
||||
)
|
||||
}
|
||||
|
||||
fn into_cloneable(self) -> Self::Cloneable {
|
||||
@@ -309,13 +305,14 @@ mod stable {
|
||||
|
||||
macro_rules! style_signal_arena {
|
||||
($sig:ident) => {
|
||||
impl<C, S> IntoStyle for $sig<C, S>
|
||||
impl<C, R, S> IntoStyle<R> for $sig<C, S>
|
||||
where
|
||||
$sig<C, S>: Get<Value = C>,
|
||||
S: Storage<C> + Storage<Option<C>>,
|
||||
S: Send + Sync + 'static,
|
||||
C: IntoStyle + Send + Sync + Clone + 'static,
|
||||
C: IntoStyle<R> + Send + Sync + Clone + 'static,
|
||||
C::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = RenderEffect<C::State>;
|
||||
@@ -329,15 +326,12 @@ mod stable {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(el)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
(move || self.get()).build(el)
|
||||
}
|
||||
|
||||
@@ -360,44 +354,46 @@ mod stable {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, St> IntoStyle for (&'static str, $sig<S, St>)
|
||||
impl<R, S, St> IntoStyle<R> for (&'static str, $sig<S, St>)
|
||||
where
|
||||
$sig<S, St>: Get<Value = S>,
|
||||
St: Send + Sync + 'static,
|
||||
St: Storage<S> + Storage<Option<S>>,
|
||||
S: Into<Cow<'static, str>> + Send + Sync + Clone + 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
type State = RenderEffect<(
|
||||
crate::renderer::types::CssStyleDeclaration,
|
||||
Cow<'static, str>,
|
||||
)>;
|
||||
type State =
|
||||
RenderEffect<(R::CssStyleDeclaration, Cow<'static, str>)>;
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
fn to_html(self, style: &mut String) {
|
||||
IntoStyle::to_html((self.0, move || self.1.get()), style)
|
||||
IntoStyle::<R>::to_html(
|
||||
(self.0, move || self.1.get()),
|
||||
style,
|
||||
)
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
IntoStyle::hydrate::<FROM_SERVER>(
|
||||
IntoStyle::<R>::hydrate::<FROM_SERVER>(
|
||||
(self.0, move || self.1.get()),
|
||||
el,
|
||||
)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
IntoStyle::build((self.0, move || self.1.get()), el)
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
IntoStyle::<R>::build((self.0, move || self.1.get()), el)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
IntoStyle::rebuild((self.0, move || self.1.get()), state)
|
||||
IntoStyle::<R>::rebuild(
|
||||
(self.0, move || self.1.get()),
|
||||
state,
|
||||
)
|
||||
}
|
||||
|
||||
fn into_cloneable(self) -> Self::Cloneable {
|
||||
@@ -418,7 +414,7 @@ mod stable {
|
||||
}
|
||||
|
||||
use super::RenderEffect;
|
||||
use crate::html::style::IntoStyle;
|
||||
use crate::{html::style::IntoStyle, renderer::DomRenderer};
|
||||
use reactive_graph::{
|
||||
computed::{ArcMemo, Memo},
|
||||
owner::Storage,
|
||||
@@ -439,13 +435,14 @@ mod stable {
|
||||
style_signal!(ArcSignal);
|
||||
}
|
||||
|
||||
impl<Fut> IntoStyle for Suspend<Fut>
|
||||
impl<Fut, Rndr> IntoStyle<Rndr> for Suspend<Fut>
|
||||
where
|
||||
Fut: Clone + Future + Send + 'static,
|
||||
Fut::Output: IntoStyle,
|
||||
Fut::Output: IntoStyle<Rndr>,
|
||||
Rndr: DomRenderer + 'static,
|
||||
{
|
||||
type AsyncOutput = Fut::Output;
|
||||
type State = Rc<RefCell<Option<<Fut::Output as IntoStyle>::State>>>;
|
||||
type State = Rc<RefCell<Option<<Fut::Output as IntoStyle<Rndr>>::State>>>;
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
@@ -459,7 +456,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &<Rndr>::Element,
|
||||
) -> Self::State {
|
||||
let el = el.to_owned();
|
||||
let state = Rc::new(RefCell::new(None));
|
||||
@@ -474,7 +471,7 @@ where
|
||||
state
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
fn build(self, el: &<Rndr>::Element) -> Self::State {
|
||||
let el = el.to_owned();
|
||||
let state = Rc::new(RefCell::new(None));
|
||||
Executor::spawn_local({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
html::attribute::Attribute,
|
||||
hydration::Cursor,
|
||||
renderer::Renderer,
|
||||
ssr::StreamBuilder,
|
||||
view::{
|
||||
add_attr::AddAnyAttr, iterators::OptionState, Mountable, Position,
|
||||
@@ -129,40 +130,39 @@ impl<Fut> Debug for Suspend<Fut> {
|
||||
}
|
||||
|
||||
/// Retained view state for [`Suspend`].
|
||||
pub struct SuspendState<T>
|
||||
pub struct SuspendState<T, Rndr>
|
||||
where
|
||||
T: Render,
|
||||
T: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
inner: Rc<RefCell<OptionState<T>>>,
|
||||
inner: Rc<RefCell<OptionState<T, Rndr>>>,
|
||||
}
|
||||
|
||||
impl<T> Mountable for SuspendState<T>
|
||||
impl<T, Rndr> Mountable<Rndr> for SuspendState<T, Rndr>
|
||||
where
|
||||
T: Render,
|
||||
T: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
self.inner.borrow_mut().unmount();
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
) {
|
||||
fn mount(&mut self, parent: &Rndr::Element, marker: Option<&Rndr::Node>) {
|
||||
self.inner.borrow_mut().mount(parent, marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<Rndr>) -> bool {
|
||||
self.inner.borrow_mut().insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Fut> Render for Suspend<Fut>
|
||||
impl<Fut, Rndr> Render<Rndr> for Suspend<Fut>
|
||||
where
|
||||
Fut: Future + 'static,
|
||||
Fut::Output: Render,
|
||||
Fut::Output: Render<Rndr>,
|
||||
Rndr: Renderer + 'static,
|
||||
{
|
||||
type State = SuspendState<Fut::Output>;
|
||||
type State = SuspendState<Fut::Output, Rndr>;
|
||||
|
||||
// TODO cancelation if it fires multiple times
|
||||
fn build(self) -> Self::State {
|
||||
@@ -235,16 +235,17 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Fut> AddAnyAttr for Suspend<Fut>
|
||||
impl<Fut, Rndr> AddAnyAttr<Rndr> for Suspend<Fut>
|
||||
where
|
||||
Fut: Future + Send + 'static,
|
||||
Fut::Output: AddAnyAttr,
|
||||
Fut::Output: AddAnyAttr<Rndr>,
|
||||
Rndr: Renderer + 'static,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> = Suspend<
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> = Suspend<
|
||||
Pin<
|
||||
Box<
|
||||
dyn Future<
|
||||
Output = <Fut::Output as AddAnyAttr>::Output<
|
||||
Output = <Fut::Output as AddAnyAttr<Rndr>>::Output<
|
||||
SomeNewAttr::CloneableOwned,
|
||||
>,
|
||||
> + Send,
|
||||
@@ -252,12 +253,12 @@ where
|
||||
>,
|
||||
>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
let attr = attr.into_cloneable_owned();
|
||||
Suspend::new(Box::pin(async move {
|
||||
@@ -267,10 +268,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Fut> RenderHtml for Suspend<Fut>
|
||||
impl<Fut, Rndr> RenderHtml<Rndr> for Suspend<Fut>
|
||||
where
|
||||
Fut: Future + Send + 'static,
|
||||
Fut::Output: RenderHtml,
|
||||
Fut::Output: RenderHtml<Rndr>,
|
||||
Rndr: Renderer + 'static,
|
||||
{
|
||||
type AsyncOutput = Option<Fut::Output>;
|
||||
|
||||
@@ -327,7 +329,7 @@ where
|
||||
// wrapped by suspense markers
|
||||
if OUT_OF_ORDER {
|
||||
let mut fallback_position = *position;
|
||||
buf.push_fallback::<()>(
|
||||
buf.push_fallback::<(), Rndr>(
|
||||
(),
|
||||
&mut fallback_position,
|
||||
mark_branches,
|
||||
@@ -362,7 +364,7 @@ where
|
||||
// TODO cancellation
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let Self { subscriber, inner } = self;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::{CastFrom, RemoveEventHandler};
|
||||
use super::{CastFrom, DomRenderer, RemoveEventHandler, Renderer};
|
||||
use crate::{
|
||||
dom::{document, window},
|
||||
ok_or_debug, or_debug,
|
||||
@@ -9,7 +9,10 @@ use once_cell::unsync::Lazy;
|
||||
use rustc_hash::FxHashSet;
|
||||
use std::{any::TypeId, borrow::Cow, cell::RefCell};
|
||||
use wasm_bindgen::{intern, prelude::Closure, JsCast, JsValue};
|
||||
use web_sys::{Comment, HtmlTemplateElement};
|
||||
use web_sys::{
|
||||
Comment, CssStyleDeclaration, DomTokenList, Element, Event, HtmlElement,
|
||||
HtmlTemplateElement, Node, Text,
|
||||
};
|
||||
|
||||
/// A [`Renderer`] that uses `web-sys` to manipulate DOM elements in the browser.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
@@ -19,30 +22,22 @@ thread_local! {
|
||||
pub(crate) static GLOBAL_EVENTS: RefCell<FxHashSet<Cow<'static, str>>> = Default::default();
|
||||
}
|
||||
|
||||
pub type Node = web_sys::Node;
|
||||
pub type Text = web_sys::Text;
|
||||
pub type Element = web_sys::Element;
|
||||
pub type Placeholder = web_sys::Comment;
|
||||
pub type Event = wasm_bindgen::JsValue;
|
||||
pub type ClassList = web_sys::DomTokenList;
|
||||
pub type CssStyleDeclaration = web_sys::CssStyleDeclaration;
|
||||
pub type TemplateElement = web_sys::HtmlTemplateElement;
|
||||
impl Renderer for Dom {
|
||||
type Node = Node;
|
||||
type Text = Text;
|
||||
type Element = Element;
|
||||
type Placeholder = Comment;
|
||||
|
||||
impl Dom {
|
||||
pub fn intern(text: &str) -> &str {
|
||||
fn intern(text: &str) -> &str {
|
||||
intern(text)
|
||||
}
|
||||
|
||||
pub fn create_element(tag: &str) -> Element {
|
||||
document().create_element(tag).unwrap()
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))]
|
||||
pub fn create_text_node(text: &str) -> Text {
|
||||
fn create_text_node(text: &str) -> Self::Text {
|
||||
document().create_text_node(text)
|
||||
}
|
||||
|
||||
pub fn create_placeholder() -> Placeholder {
|
||||
fn create_placeholder() -> Self::Placeholder {
|
||||
thread_local! {
|
||||
static COMMENT: Lazy<Comment> = Lazy::new(|| {
|
||||
document().create_comment("")
|
||||
@@ -52,25 +47,25 @@ impl Dom {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))]
|
||||
pub fn set_text(node: &Text, text: &str) {
|
||||
fn set_text(node: &Self::Text, text: &str) {
|
||||
node.set_node_value(Some(text));
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))]
|
||||
pub fn set_attribute(node: &Element, name: &str, value: &str) {
|
||||
fn set_attribute(node: &Self::Element, name: &str, value: &str) {
|
||||
or_debug!(node.set_attribute(name, value), node, "setAttribute");
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))]
|
||||
pub fn remove_attribute(node: &Element, name: &str) {
|
||||
fn remove_attribute(node: &Self::Element, name: &str) {
|
||||
or_debug!(node.remove_attribute(name), node, "removeAttribute");
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))]
|
||||
pub fn insert_node(
|
||||
parent: &Element,
|
||||
new_child: &Node,
|
||||
anchor: Option<&Node>,
|
||||
fn insert_node(
|
||||
parent: &Self::Element,
|
||||
new_child: &Self::Node,
|
||||
anchor: Option<&Self::Node>,
|
||||
) {
|
||||
ok_or_debug!(
|
||||
parent.insert_before(new_child, anchor),
|
||||
@@ -80,20 +75,23 @@ impl Dom {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))]
|
||||
pub fn remove_node(parent: &Element, child: &Node) -> Option<Node> {
|
||||
fn remove_node(
|
||||
parent: &Self::Element,
|
||||
child: &Self::Node,
|
||||
) -> Option<Self::Node> {
|
||||
ok_or_debug!(parent.remove_child(child), parent, "removeNode")
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))]
|
||||
pub fn remove(node: &Node) {
|
||||
fn remove(node: &Self::Node) {
|
||||
node.unchecked_ref::<Element>().remove();
|
||||
}
|
||||
|
||||
pub fn get_parent(node: &Node) -> Option<Node> {
|
||||
fn get_parent(node: &Self::Node) -> Option<Self::Node> {
|
||||
node.parent_node()
|
||||
}
|
||||
|
||||
pub fn first_child(node: &Node) -> Option<Node> {
|
||||
fn first_child(node: &Self::Node) -> Option<Self::Node> {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let node = node.first_child();
|
||||
@@ -118,7 +116,7 @@ impl Dom {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_sibling(node: &Node) -> Option<Node> {
|
||||
fn next_sibling(node: &Self::Node) -> Option<Self::Node> {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let node = node.next_sibling();
|
||||
@@ -143,49 +141,23 @@ impl Dom {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log_node(node: &Node) {
|
||||
fn log_node(node: &Self::Node) {
|
||||
web_sys::console::log_1(node);
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))]
|
||||
pub fn clear_children(parent: &Element) {
|
||||
fn clear_children(parent: &Self::Element) {
|
||||
parent.set_text_content(Some(""));
|
||||
}
|
||||
}
|
||||
|
||||
/// Mounts the new child before the marker as its sibling.
|
||||
///
|
||||
/// ## Panics
|
||||
/// The default implementation panics if `before` does not have a parent [`crate::renderer::types::Element`].
|
||||
pub fn mount_before<M>(new_child: &mut M, before: &Node)
|
||||
where
|
||||
M: Mountable,
|
||||
{
|
||||
let parent = Element::cast_from(
|
||||
Self::get_parent(before).expect("could not find parent element"),
|
||||
)
|
||||
.expect("placeholder parent should be Element");
|
||||
new_child.mount(&parent, Some(before));
|
||||
}
|
||||
impl DomRenderer for Dom {
|
||||
type Event = JsValue;
|
||||
type ClassList = DomTokenList;
|
||||
type CssStyleDeclaration = CssStyleDeclaration;
|
||||
type TemplateElement = HtmlTemplateElement;
|
||||
|
||||
/// Tries to mount the new child before the marker as its sibling.
|
||||
///
|
||||
/// Returns `false` if the child did not have a valid parent.
|
||||
#[track_caller]
|
||||
pub fn try_mount_before<M>(new_child: &mut M, before: &Node) -> bool
|
||||
where
|
||||
M: Mountable,
|
||||
{
|
||||
if let Some(parent) =
|
||||
Self::get_parent(before).and_then(Element::cast_from)
|
||||
{
|
||||
new_child.mount(&parent, Some(before));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_property(el: &Element, key: &str, value: &JsValue) {
|
||||
fn set_property(el: &Self::Element, key: &str, value: &JsValue) {
|
||||
or_debug!(
|
||||
js_sys::Reflect::set(
|
||||
el,
|
||||
@@ -197,11 +169,11 @@ impl Dom {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn add_event_listener(
|
||||
el: &Element,
|
||||
fn add_event_listener(
|
||||
el: &Self::Element,
|
||||
name: &str,
|
||||
cb: Box<dyn FnMut(Event)>,
|
||||
) -> RemoveEventHandler<Element> {
|
||||
cb: Box<dyn FnMut(Self::Event)>,
|
||||
) -> RemoveEventHandler<Self::Element> {
|
||||
let cb = wasm_bindgen::closure::Closure::wrap(cb);
|
||||
let name = intern(name);
|
||||
or_debug!(
|
||||
@@ -219,7 +191,7 @@ impl Dom {
|
||||
// safe to construct this here, because it will only run in the browser
|
||||
// so it will always be accessed or dropped from the main thread
|
||||
let cb = send_wrapper::SendWrapper::new(cb);
|
||||
move |el: &Element| {
|
||||
move |el: &Self::Element| {
|
||||
or_debug!(
|
||||
el.remove_event_listener_with_callback(
|
||||
intern(&name),
|
||||
@@ -232,24 +204,24 @@ impl Dom {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn event_target<T>(ev: &Event) -> T
|
||||
fn event_target<T>(ev: &Self::Event) -> T
|
||||
where
|
||||
T: CastFrom<Element>,
|
||||
T: CastFrom<Self::Element>,
|
||||
{
|
||||
let el = ev
|
||||
.unchecked_ref::<web_sys::Event>()
|
||||
.unchecked_ref::<Event>()
|
||||
.target()
|
||||
.expect("event.target not found")
|
||||
.unchecked_into::<Element>();
|
||||
T::cast_from(el).expect("incorrect element type")
|
||||
}
|
||||
|
||||
pub fn add_event_listener_delegated(
|
||||
el: &Element,
|
||||
fn add_event_listener_delegated(
|
||||
el: &Self::Element,
|
||||
name: Cow<'static, str>,
|
||||
delegation_key: Cow<'static, str>,
|
||||
cb: Box<dyn FnMut(Event)>,
|
||||
) -> RemoveEventHandler<Element> {
|
||||
cb: Box<dyn FnMut(Self::Event)>,
|
||||
) -> RemoveEventHandler<Self::Element> {
|
||||
let cb = Closure::wrap(cb);
|
||||
let key = intern(&delegation_key);
|
||||
or_debug!(
|
||||
@@ -332,7 +304,7 @@ impl Dom {
|
||||
// safe to construct this here, because it will only run in the browser
|
||||
// so it will always be accessed or dropped from the main thread
|
||||
let cb = send_wrapper::SendWrapper::new(cb);
|
||||
move |el: &Element| {
|
||||
move |el: &Self::Element| {
|
||||
drop(cb.take());
|
||||
or_debug!(
|
||||
js_sys::Reflect::delete_property(
|
||||
@@ -346,24 +318,24 @@ impl Dom {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn class_list(el: &Element) -> ClassList {
|
||||
fn class_list(el: &Self::Element) -> Self::ClassList {
|
||||
el.class_list()
|
||||
}
|
||||
|
||||
pub fn add_class(list: &ClassList, name: &str) {
|
||||
fn add_class(list: &Self::ClassList, name: &str) {
|
||||
or_debug!(list.add_1(name), list.unchecked_ref(), "add()");
|
||||
}
|
||||
|
||||
pub fn remove_class(list: &ClassList, name: &str) {
|
||||
fn remove_class(list: &Self::ClassList, name: &str) {
|
||||
or_debug!(list.remove_1(name), list.unchecked_ref(), "remove()");
|
||||
}
|
||||
|
||||
pub fn style(el: &Element) -> CssStyleDeclaration {
|
||||
el.unchecked_ref::<web_sys::HtmlElement>().style()
|
||||
fn style(el: &Self::Element) -> Self::CssStyleDeclaration {
|
||||
el.unchecked_ref::<HtmlElement>().style()
|
||||
}
|
||||
|
||||
pub fn set_css_property(
|
||||
style: &CssStyleDeclaration,
|
||||
fn set_css_property(
|
||||
style: &Self::CssStyleDeclaration,
|
||||
name: &str,
|
||||
value: &str,
|
||||
) {
|
||||
@@ -374,11 +346,11 @@ impl Dom {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn set_inner_html(el: &Element, html: &str) {
|
||||
fn set_inner_html(el: &Self::Element, html: &str) {
|
||||
el.set_inner_html(html);
|
||||
}
|
||||
|
||||
pub fn get_template<V>() -> TemplateElement
|
||||
fn get_template<V>() -> Self::TemplateElement
|
||||
where
|
||||
V: ToTemplate + 'static,
|
||||
{
|
||||
@@ -412,14 +384,14 @@ impl Dom {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn clone_template(tpl: &TemplateElement) -> Element {
|
||||
fn clone_template(tpl: &Self::TemplateElement) -> Self::Element {
|
||||
tpl.content()
|
||||
.clone_node_with_deep(true)
|
||||
.unwrap()
|
||||
.unchecked_into()
|
||||
}
|
||||
|
||||
pub fn create_element_from_html(html: &str) -> Element {
|
||||
fn create_element_from_html(html: &str) -> Self::Element {
|
||||
// TODO can be optimized to cache HTML strings or cache <template>?
|
||||
let tpl = document().create_element("template").unwrap();
|
||||
tpl.set_inner_html(html);
|
||||
@@ -427,7 +399,7 @@ impl Dom {
|
||||
}
|
||||
}
|
||||
|
||||
impl Mountable for Node {
|
||||
impl Mountable<Dom> for Node {
|
||||
fn unmount(&mut self) {
|
||||
todo!()
|
||||
}
|
||||
@@ -436,7 +408,7 @@ impl Mountable for Node {
|
||||
Dom::insert_node(parent, self, marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<Dom>) -> bool {
|
||||
let parent = Dom::get_parent(self).and_then(Element::cast_from);
|
||||
if let Some(parent) = parent {
|
||||
child.mount(&parent, Some(self));
|
||||
@@ -446,7 +418,7 @@ impl Mountable for Node {
|
||||
}
|
||||
}
|
||||
|
||||
impl Mountable for Text {
|
||||
impl Mountable<Dom> for Text {
|
||||
fn unmount(&mut self) {
|
||||
self.remove();
|
||||
}
|
||||
@@ -455,7 +427,7 @@ impl Mountable for Text {
|
||||
Dom::insert_node(parent, self, marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<Dom>) -> bool {
|
||||
let parent =
|
||||
Dom::get_parent(self.as_ref()).and_then(Element::cast_from);
|
||||
if let Some(parent) = parent {
|
||||
@@ -466,7 +438,7 @@ impl Mountable for Text {
|
||||
}
|
||||
}
|
||||
|
||||
impl Mountable for Comment {
|
||||
impl Mountable<Dom> for Comment {
|
||||
fn unmount(&mut self) {
|
||||
self.remove();
|
||||
}
|
||||
@@ -475,7 +447,7 @@ impl Mountable for Comment {
|
||||
Dom::insert_node(parent, self, marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<Dom>) -> bool {
|
||||
let parent =
|
||||
Dom::get_parent(self.as_ref()).and_then(Element::cast_from);
|
||||
if let Some(parent) = parent {
|
||||
@@ -486,7 +458,7 @@ impl Mountable for Comment {
|
||||
}
|
||||
}
|
||||
|
||||
impl Mountable for Element {
|
||||
impl Mountable<Dom> for Element {
|
||||
fn unmount(&mut self) {
|
||||
self.remove();
|
||||
}
|
||||
@@ -495,7 +467,7 @@ impl Mountable for Element {
|
||||
Dom::insert_node(parent, self, marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<Dom>) -> bool {
|
||||
let parent =
|
||||
Dom::get_parent(self.as_ref()).and_then(Element::cast_from);
|
||||
if let Some(parent) = parent {
|
||||
|
||||
@@ -420,7 +420,7 @@ impl Mountable<MockDom> for Placeholder {
|
||||
}
|
||||
|
||||
impl<E: ElementType> CreateElement<MockDom> for E {
|
||||
fn create_element(&self) -> crate::renderer::types::Element {
|
||||
fn create_element(&self) -> <MockDom as Renderer>::Element {
|
||||
document().create_element(E::TAG)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +1,55 @@
|
||||
use crate::view::{Mountable, ToTemplate};
|
||||
use crate::{
|
||||
html::element::CreateElement,
|
||||
view::{Mountable, ToTemplate},
|
||||
};
|
||||
use std::{borrow::Cow, fmt::Debug};
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
/// A DOM renderer.
|
||||
pub mod dom;
|
||||
|
||||
pub type Rndr = dom::Dom;
|
||||
pub mod types {
|
||||
pub use super::dom::{
|
||||
ClassList, CssStyleDeclaration, Element, Event, Node, Placeholder,
|
||||
TemplateElement, Text,
|
||||
};
|
||||
}
|
||||
|
||||
/* #[cfg(feature = "testing")]
|
||||
#[cfg(feature = "testing")]
|
||||
/// A renderer based on a mock DOM.
|
||||
pub mod mock_dom;
|
||||
/// A DOM renderer optimized for element creation.
|
||||
#[cfg(feature = "sledgehammer")]
|
||||
pub mod sledgehammer; */
|
||||
pub mod sledgehammer;
|
||||
|
||||
/// Implements the instructions necessary to render an interface on some platform.
|
||||
///
|
||||
/// By default, this is implemented for the Document Object Model (DOM) in a Web
|
||||
/// browser, but implementing this trait for some other platform allows you to use
|
||||
/// the library to render any tree-based UI.
|
||||
pub trait Renderer: Send + Sized + Debug + 'static {
|
||||
/// The basic type of node in the view tree.
|
||||
type Node: Mountable + Clone + 'static;
|
||||
type Node: Mountable<Self> + Clone + 'static;
|
||||
/// A visible element in the view tree.
|
||||
type Element: AsRef<Self::Node>
|
||||
+ CastFrom<Self::Node>
|
||||
+ Mountable
|
||||
+ Mountable<Self>
|
||||
+ Clone
|
||||
+ 'static;
|
||||
/// A text node in the view tree.
|
||||
type Text: AsRef<Self::Node>
|
||||
+ CastFrom<Self::Node>
|
||||
+ Mountable
|
||||
+ Mountable<Self>
|
||||
+ Clone
|
||||
+ 'static;
|
||||
/// A placeholder node, which can be inserted into the tree but does not
|
||||
/// appear (e.g., a comment node in the DOM).
|
||||
type Placeholder: AsRef<Self::Node>
|
||||
+ CastFrom<Self::Node>
|
||||
+ Mountable
|
||||
+ Mountable<Self>
|
||||
+ Clone
|
||||
+ 'static;
|
||||
|
||||
/// Interns a string slice, if that is available on this platform and useful as an optimization.
|
||||
fn intern(text: &str) -> &str;
|
||||
|
||||
/// Creates a new element node.
|
||||
#[track_caller]
|
||||
fn create_element<E: CreateElement<Self>>(tag: E) -> Self::Element {
|
||||
tag.create_element()
|
||||
}
|
||||
|
||||
/// Creates a new text node.
|
||||
fn create_text_node(text: &str) -> Self::Text;
|
||||
|
||||
@@ -74,6 +73,39 @@ pub trait Renderer: Send + Sized + Debug + 'static {
|
||||
marker: Option<&Self::Node>,
|
||||
);
|
||||
|
||||
/// Mounts the new child before the marker as its sibling.
|
||||
///
|
||||
/// ## Panics
|
||||
/// The default implementation panics if `before` does not have a parent [`R::Element`].
|
||||
fn mount_before<M>(new_child: &mut M, before: &Self::Node)
|
||||
where
|
||||
M: Mountable<Self>,
|
||||
{
|
||||
let parent = Self::Element::cast_from(
|
||||
Self::get_parent(before).expect("could not find parent element"),
|
||||
)
|
||||
.expect("placeholder parent should be Element");
|
||||
new_child.mount(&parent, Some(before));
|
||||
}
|
||||
|
||||
/// Tries to mount the new child before the marker as its sibling.
|
||||
///
|
||||
/// Returns `false` if the child did not have a valid parent.
|
||||
#[track_caller]
|
||||
fn try_mount_before<M>(new_child: &mut M, before: &Self::Node) -> bool
|
||||
where
|
||||
M: Mountable<Self>,
|
||||
{
|
||||
if let Some(parent) =
|
||||
Self::get_parent(before).and_then(Self::Element::cast_from)
|
||||
{
|
||||
new_child.mount(&parent, Some(before));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the child node from the parents, and returns the removed node.
|
||||
fn remove_node(
|
||||
parent: &Self::Element,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::view::{Position, RenderHtml};
|
||||
use crate::{
|
||||
renderer::Renderer,
|
||||
view::{Position, RenderHtml},
|
||||
};
|
||||
use futures::Stream;
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
@@ -97,13 +100,14 @@ impl StreamBuilder {
|
||||
|
||||
// Out-of-Order Streaming
|
||||
/// Pushes a fallback for out-of-order streaming.
|
||||
pub fn push_fallback<View>(
|
||||
pub fn push_fallback<View, Rndr>(
|
||||
&mut self,
|
||||
fallback: View,
|
||||
position: &mut Position,
|
||||
mark_branches: bool,
|
||||
) where
|
||||
View: RenderHtml,
|
||||
View: RenderHtml<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
self.write_chunk_marker(true);
|
||||
fallback.to_html_with_buf(
|
||||
@@ -154,13 +158,14 @@ impl StreamBuilder {
|
||||
}
|
||||
|
||||
/// Injects an out-of-order chunk into the stream.
|
||||
pub fn push_async_out_of_order<View>(
|
||||
pub fn push_async_out_of_order<View, Rndr>(
|
||||
&mut self,
|
||||
view: impl Future<Output = Option<View>> + Send + 'static,
|
||||
position: &mut Position,
|
||||
mark_branches: bool,
|
||||
) where
|
||||
View: RenderHtml,
|
||||
View: RenderHtml<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
let id = self.clone_id();
|
||||
// copy so it's not updated by additional iterations
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
use crate::{
|
||||
html::{
|
||||
attribute::Attribute,
|
||||
element::{ElementType, ElementWithChildren, HtmlElement},
|
||||
element::{
|
||||
CreateElement, ElementType, ElementWithChildren, HtmlElement,
|
||||
},
|
||||
},
|
||||
renderer::{dom::Dom, Renderer},
|
||||
view::Render,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
use once_cell::unsync::Lazy;
|
||||
use std::{fmt::Debug, marker::PhantomData};
|
||||
|
||||
macro_rules! svg_elements {
|
||||
($($tag:ident [$($attr:ty),*]),* $(,)?) => {
|
||||
@@ -14,15 +18,15 @@ macro_rules! svg_elements {
|
||||
/// An SVG attribute.
|
||||
// `tag()` function
|
||||
#[allow(non_snake_case)]
|
||||
pub fn $tag() -> HtmlElement<[<$tag:camel>], (), ()>
|
||||
pub fn $tag<Rndr>() -> HtmlElement<[<$tag:camel>], (), (), Rndr>
|
||||
where
|
||||
|
||||
Rndr: Renderer
|
||||
{
|
||||
HtmlElement {
|
||||
tag: [<$tag:camel>],
|
||||
attributes: (),
|
||||
children: (),
|
||||
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,30 +34,30 @@ macro_rules! svg_elements {
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct [<$tag:camel>];
|
||||
|
||||
impl<At, Ch> HtmlElement<[<$tag:camel>], At, Ch>
|
||||
impl<At, Ch, Rndr> HtmlElement<[<$tag:camel>], At, Ch, Rndr>
|
||||
where
|
||||
At: Attribute,
|
||||
Ch: Render,
|
||||
|
||||
At: Attribute<Rndr>,
|
||||
Ch: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
$(
|
||||
pub fn $attr<V>(self, value: V) -> HtmlElement <
|
||||
[<$tag:camel>],
|
||||
<At as NextTuple<Attr<$crate::html::attribute::[<$attr:camel>], V>>>::Output,
|
||||
Ch
|
||||
<At as NextTuple<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>>::Output,
|
||||
Ch, Rndr
|
||||
>
|
||||
where
|
||||
V: AttributeValue,
|
||||
At: NextTuple<Attr<$crate::html::attribute::[<$attr:camel>], V>>,
|
||||
<At as NextTuple<Attr<$crate::html::attribute::[<$attr:camel>], V>>>::Output: Attribute,
|
||||
V: AttributeValue<Rndr>,
|
||||
At: NextTuple<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>,
|
||||
<At as NextTuple<Attr<$crate::html::attribute::[<$attr:camel>], V, Rndr>>>::Output: Attribute<Rndr>,
|
||||
{
|
||||
let HtmlElement { tag, children, attributes,
|
||||
let HtmlElement { tag, rndr, children, attributes,
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at
|
||||
} = self;
|
||||
HtmlElement {
|
||||
tag,
|
||||
|
||||
rndr,
|
||||
children,
|
||||
attributes: attributes.next_tuple($crate::html::attribute::$attr(value)),
|
||||
#[cfg(debug_assertions)]
|
||||
@@ -77,6 +81,22 @@ macro_rules! svg_elements {
|
||||
}
|
||||
|
||||
impl ElementWithChildren for [<$tag:camel>] {}
|
||||
|
||||
impl CreateElement<Dom> for [<$tag:camel>] {
|
||||
fn create_element(&self) -> <Dom as Renderer>::Element {
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
thread_local! {
|
||||
static ELEMENT: Lazy<<Dom as Renderer>::Element> = Lazy::new(|| {
|
||||
crate::dom::document().create_element_ns(
|
||||
Some(wasm_bindgen::intern("http://www.w3.org/2000/svg")),
|
||||
stringify!($tag)
|
||||
).unwrap()
|
||||
});
|
||||
}
|
||||
ELEMENT.with(|e| e.clone_node()).unwrap().unchecked_into()
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::RenderHtml;
|
||||
use crate::html::attribute::Attribute;
|
||||
use super::{BoxedView, RenderHtml, WrappedView};
|
||||
use crate::{html::attribute::Attribute, renderer::Renderer};
|
||||
|
||||
/// Allows adding a new attribute to some type, before it is rendered.
|
||||
/// This takes place at compile time as part of the builder syntax for creating a statically typed
|
||||
@@ -8,28 +8,34 @@ use crate::html::attribute::Attribute;
|
||||
/// Normally, this is used to add an attribute to an HTML element. But it is required to be
|
||||
/// implemented for all types that implement [`RenderHtml`], so that attributes can be spread onto
|
||||
/// other structures like the return type of a component.
|
||||
pub trait AddAnyAttr {
|
||||
pub trait AddAnyAttr<Rndr>
|
||||
where
|
||||
Rndr: Renderer,
|
||||
{
|
||||
/// The new type once the attribute has been added.
|
||||
type Output<SomeNewAttr: Attribute>: RenderHtml;
|
||||
type Output<SomeNewAttr: Attribute<Rndr>>: RenderHtml<Rndr>;
|
||||
|
||||
/// Adds an attribute to the view.
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml;
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>;
|
||||
}
|
||||
|
||||
/// Declares that spreading attributes onto a particular type has no effect.
|
||||
#[macro_export]
|
||||
macro_rules! no_attrs {
|
||||
($ty_name:ty) => {
|
||||
impl<'a> $crate::view::add_attr::AddAnyAttr for $ty_name {
|
||||
type Output<SomeNewAttr: $crate::html::attribute::Attribute> =
|
||||
impl<'a, R> $crate::view::add_attr::AddAnyAttr<R> for $ty_name
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: $crate::html::attribute::Attribute<R>> =
|
||||
$ty_name;
|
||||
|
||||
fn add_any_attr<NewAttr: $crate::html::attribute::Attribute>(
|
||||
fn add_any_attr<NewAttr: $crate::html::attribute::Attribute<R>>(
|
||||
self,
|
||||
_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr> {
|
||||
@@ -38,3 +44,41 @@ macro_rules! no_attrs {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<T, Rndr> AddAnyAttr<Rndr> for BoxedView<T>
|
||||
where
|
||||
T: AddAnyAttr<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> =
|
||||
BoxedView<T::Output<SomeNewAttr>>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
BoxedView::new(self.into_inner().add_any_attr(attr))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Rndr> AddAnyAttr<Rndr> for WrappedView<T>
|
||||
where
|
||||
T: AddAnyAttr<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> =
|
||||
WrappedView<T::Output<SomeNewAttr>>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
WrappedView::new(self.into_inner().add_any_attr(attr))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
#[cfg(feature = "ssr")]
|
||||
use super::MarkBranch;
|
||||
use super::{
|
||||
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
|
||||
RenderHtml,
|
||||
add_attr::AddAnyAttr, BoxedView, Mountable, Position, PositionState,
|
||||
Render, RenderHtml,
|
||||
};
|
||||
use crate::{
|
||||
html::attribute::Attribute, hydration::Cursor, ssr::StreamBuilder,
|
||||
html::attribute::Attribute, hydration::Cursor, renderer::Renderer,
|
||||
ssr::StreamBuilder,
|
||||
};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
fmt::Debug,
|
||||
marker::PhantomData,
|
||||
};
|
||||
#[cfg(feature = "ssr")]
|
||||
use std::{future::Future, pin::Pin};
|
||||
@@ -24,7 +26,10 @@ use std::{future::Future, pin::Pin};
|
||||
/// 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 {
|
||||
pub struct AnyView<R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type_id: TypeId,
|
||||
value: Box<dyn Any + Send>,
|
||||
|
||||
@@ -42,34 +47,38 @@ pub struct AnyView {
|
||||
#[cfg(feature = "ssr")]
|
||||
to_html_async_ooo:
|
||||
fn(Box<dyn Any>, &mut StreamBuilder, &mut Position, bool, bool),
|
||||
build: fn(Box<dyn Any>) -> AnyViewState,
|
||||
rebuild: fn(TypeId, Box<dyn Any>, &mut AnyViewState),
|
||||
build: fn(Box<dyn Any>) -> AnyViewState<R>,
|
||||
rebuild: fn(TypeId, Box<dyn Any>, &mut AnyViewState<R>),
|
||||
#[cfg(feature = "ssr")]
|
||||
#[allow(clippy::type_complexity)]
|
||||
resolve: fn(Box<dyn Any>) -> Pin<Box<dyn Future<Output = AnyView> + Send>>,
|
||||
resolve:
|
||||
fn(Box<dyn Any>) -> Pin<Box<dyn Future<Output = AnyView<R>> + Send>>,
|
||||
#[cfg(feature = "ssr")]
|
||||
dry_resolve: fn(&mut Box<dyn Any + Send>),
|
||||
#[cfg(feature = "hydrate")]
|
||||
#[cfg(feature = "hydrate")]
|
||||
#[allow(clippy::type_complexity)]
|
||||
hydrate_from_server:
|
||||
fn(Box<dyn Any>, &Cursor, &PositionState) -> AnyViewState,
|
||||
fn(Box<dyn Any>, &Cursor<R>, &PositionState) -> AnyViewState<R>,
|
||||
}
|
||||
|
||||
/// Retained view state for [`AnyView`].
|
||||
pub struct AnyViewState {
|
||||
pub struct AnyViewState<R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type_id: TypeId,
|
||||
state: Box<dyn Any>,
|
||||
unmount: fn(&mut dyn Any),
|
||||
mount: fn(
|
||||
&mut dyn Any,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
),
|
||||
insert_before_this: fn(&dyn Any, child: &mut dyn Mountable) -> bool,
|
||||
mount: fn(&mut dyn Any, parent: &R::Element, marker: Option<&R::Node>),
|
||||
insert_before_this: fn(&dyn Any, child: &mut dyn Mountable<R>) -> bool,
|
||||
rndr: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl Debug for AnyViewState {
|
||||
impl<R> Debug for AnyViewState<R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("AnyViewState")
|
||||
.field("type_id", &self.type_id)
|
||||
@@ -77,23 +86,28 @@ impl Debug for AnyViewState {
|
||||
.field("unmount", &self.unmount)
|
||||
.field("mount", &self.mount)
|
||||
.field("insert_before_this", &self.insert_before_this)
|
||||
.field("rndr", &self.rndr)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows converting some view into [`AnyView`].
|
||||
pub trait IntoAny {
|
||||
pub trait IntoAny<R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
/// Converts the view into a type-erased [`AnyView`].
|
||||
fn into_any(self) -> AnyView;
|
||||
fn into_any(self) -> AnyView<R>;
|
||||
}
|
||||
|
||||
fn mount_any<T>(
|
||||
fn mount_any<R, T>(
|
||||
state: &mut dyn Any,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
parent: &R::Element,
|
||||
marker: Option<&R::Node>,
|
||||
) where
|
||||
T: Render,
|
||||
T: Render<R>,
|
||||
T::State: 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
let state = state
|
||||
.downcast_mut::<T::State>()
|
||||
@@ -101,10 +115,11 @@ fn mount_any<T>(
|
||||
state.mount(parent, marker)
|
||||
}
|
||||
|
||||
fn unmount_any<T>(state: &mut dyn Any)
|
||||
fn unmount_any<R, T>(state: &mut dyn Any)
|
||||
where
|
||||
T: Render,
|
||||
T: Render<R>,
|
||||
T::State: 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
let state = state
|
||||
.downcast_mut::<T::State>()
|
||||
@@ -112,10 +127,14 @@ where
|
||||
state.unmount();
|
||||
}
|
||||
|
||||
fn insert_before_this<T>(state: &dyn Any, child: &mut dyn Mountable) -> bool
|
||||
fn insert_before_this<R, T>(
|
||||
state: &dyn Any,
|
||||
child: &mut dyn Mountable<R>,
|
||||
) -> bool
|
||||
where
|
||||
T: Render,
|
||||
T: Render<R>,
|
||||
T::State: 'static,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
let state = state
|
||||
.downcast_ref::<T::State>()
|
||||
@@ -123,16 +142,17 @@ where
|
||||
state.insert_before_this(child)
|
||||
}
|
||||
|
||||
impl<T> IntoAny for T
|
||||
impl<T, R> IntoAny<R> for T
|
||||
where
|
||||
T: Send,
|
||||
T: RenderHtml + 'static,
|
||||
T: RenderHtml<R> + 'static,
|
||||
T::State: 'static,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
// inlining allows the compiler to remove the unused functions
|
||||
// i.e., doesn't ship HTML-generating code that isn't used
|
||||
#[inline(always)]
|
||||
fn into_any(self) -> AnyView {
|
||||
fn into_any(self) -> AnyView<R> {
|
||||
#[cfg(feature = "ssr")]
|
||||
let html_len = self.html_len();
|
||||
|
||||
@@ -152,7 +172,7 @@ where
|
||||
.downcast::<T>()
|
||||
.expect("AnyView::resolve could not be downcast");
|
||||
Box::pin(async move { value.resolve().await.into_any() })
|
||||
as Pin<Box<dyn Future<Output = AnyView> + Send>>
|
||||
as Pin<Box<dyn Future<Output = AnyView<R>> + Send>>
|
||||
};
|
||||
#[cfg(feature = "ssr")]
|
||||
let to_html = |value: Box<dyn Any>,
|
||||
@@ -225,15 +245,17 @@ where
|
||||
AnyViewState {
|
||||
type_id: TypeId::of::<T>(),
|
||||
state,
|
||||
|
||||
mount: mount_any::<T>,
|
||||
unmount: unmount_any::<T>,
|
||||
insert_before_this: insert_before_this::<T>,
|
||||
rndr: PhantomData,
|
||||
mount: mount_any::<R, T>,
|
||||
unmount: unmount_any::<R, T>,
|
||||
insert_before_this: insert_before_this::<R, T>,
|
||||
}
|
||||
};
|
||||
#[cfg(feature = "hydrate")]
|
||||
let hydrate_from_server =
|
||||
|value: Box<dyn Any>, cursor: &Cursor, position: &PositionState| {
|
||||
|value: Box<dyn Any>,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState| {
|
||||
let value = value
|
||||
.downcast::<T>()
|
||||
.expect("AnyView::hydrate_from_server couldn't downcast");
|
||||
@@ -242,16 +264,16 @@ where
|
||||
AnyViewState {
|
||||
type_id: TypeId::of::<T>(),
|
||||
state,
|
||||
|
||||
mount: mount_any::<T>,
|
||||
unmount: unmount_any::<T>,
|
||||
insert_before_this: insert_before_this::<T>,
|
||||
rndr: PhantomData,
|
||||
mount: mount_any::<R, T>,
|
||||
unmount: unmount_any::<R, T>,
|
||||
insert_before_this: insert_before_this::<R, T>,
|
||||
}
|
||||
};
|
||||
|
||||
let rebuild = |new_type_id: TypeId,
|
||||
value: Box<dyn Any>,
|
||||
state: &mut AnyViewState| {
|
||||
state: &mut AnyViewState<R>| {
|
||||
let value = value
|
||||
.downcast::<T>()
|
||||
.expect("AnyView::rebuild couldn't downcast value");
|
||||
@@ -268,7 +290,6 @@ where
|
||||
*state = new;
|
||||
}
|
||||
};
|
||||
|
||||
AnyView {
|
||||
type_id: TypeId::of::<T>(),
|
||||
value,
|
||||
@@ -292,8 +313,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for AnyView {
|
||||
type State = AnyViewState;
|
||||
impl<R> Render<R> for AnyView<R>
|
||||
where
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type State = AnyViewState<R>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
(self.build)(self.value)
|
||||
@@ -304,21 +328,27 @@ impl Render for AnyView {
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAnyAttr for AnyView {
|
||||
type Output<SomeNewAttr: Attribute> = Self;
|
||||
impl<R> AddAnyAttr<R> for AnyView<R>
|
||||
where
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute<R>> = Self;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<R>,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderHtml for AnyView {
|
||||
impl<R> RenderHtml<R> for AnyView<R>
|
||||
where
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
fn dry_resolve(&mut self) {
|
||||
@@ -411,7 +441,7 @@ impl RenderHtml for AnyView {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
#[cfg(feature = "hydrate")]
|
||||
@@ -446,20 +476,19 @@ impl RenderHtml for AnyView {
|
||||
}
|
||||
}
|
||||
|
||||
impl Mountable for AnyViewState {
|
||||
impl<R> Mountable<R> for AnyViewState<R>
|
||||
where
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
(self.unmount)(&mut *self.state)
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
) {
|
||||
fn mount(&mut self, parent: &R::Element, marker: Option<&R::Node>) {
|
||||
(self.mount)(&mut *self.state, parent, marker)
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
(self.insert_before_this)(&*self.state, child)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,18 @@ use super::{
|
||||
Render, RenderHtml,
|
||||
};
|
||||
use crate::{
|
||||
html::attribute::Attribute, hydration::Cursor, ssr::StreamBuilder,
|
||||
html::attribute::Attribute, hydration::Cursor, renderer::Renderer,
|
||||
ssr::StreamBuilder,
|
||||
};
|
||||
use either_of::*;
|
||||
use futures::future::join;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
impl<A, B> Render for Either<A, B>
|
||||
impl<A, B, Rndr> Render<Rndr> for Either<A, B>
|
||||
where
|
||||
A: Render,
|
||||
B: Render,
|
||||
A: Render<Rndr>,
|
||||
B: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type State = Either<A::State, B::State>;
|
||||
|
||||
@@ -46,10 +49,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Mountable for Either<A, B>
|
||||
impl<A, B, Rndr> Mountable<Rndr> for Either<A, B>
|
||||
where
|
||||
A: Mountable,
|
||||
B: Mountable,
|
||||
A: Mountable<Rndr>,
|
||||
B: Mountable<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
match self {
|
||||
@@ -60,8 +64,8 @@ where
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
parent: &<Rndr as Renderer>::Element,
|
||||
marker: Option<&<Rndr as Renderer>::Node>,
|
||||
) {
|
||||
match self {
|
||||
Either::Left(left) => left.mount(parent, marker),
|
||||
@@ -69,7 +73,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<Rndr>) -> bool {
|
||||
match &self {
|
||||
Either::Left(left) => left.insert_before_this(child),
|
||||
Either::Right(right) => right.insert_before_this(child),
|
||||
@@ -77,22 +81,23 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> AddAnyAttr for Either<A, B>
|
||||
impl<A, B, Rndr> AddAnyAttr<Rndr> for Either<A, B>
|
||||
where
|
||||
A: RenderHtml,
|
||||
B: RenderHtml,
|
||||
A: RenderHtml<Rndr>,
|
||||
B: RenderHtml<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> = Either<
|
||||
<A as AddAnyAttr>::Output<SomeNewAttr>,
|
||||
<B as AddAnyAttr>::Output<SomeNewAttr>,
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> = Either<
|
||||
<A as AddAnyAttr<Rndr>>::Output<SomeNewAttr>,
|
||||
<B as AddAnyAttr<Rndr>>::Output<SomeNewAttr>,
|
||||
>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
match self {
|
||||
Either::Left(i) => Either::Left(i.add_any_attr(attr)),
|
||||
@@ -114,10 +119,11 @@ const fn max_usize(vals: &[usize]) -> usize {
|
||||
max
|
||||
}
|
||||
|
||||
impl<A, B> RenderHtml for Either<A, B>
|
||||
impl<A, B, Rndr> RenderHtml<Rndr> for Either<A, B>
|
||||
where
|
||||
A: RenderHtml,
|
||||
B: RenderHtml,
|
||||
A: RenderHtml<Rndr>,
|
||||
B: RenderHtml<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type AsyncOutput = Either<A::AsyncOutput, B::AsyncOutput>;
|
||||
|
||||
@@ -217,7 +223,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
match self {
|
||||
@@ -248,10 +254,11 @@ pub struct EitherKeepAliveState<A, B> {
|
||||
showing_b: bool,
|
||||
}
|
||||
|
||||
impl<A, B> Render for EitherKeepAlive<A, B>
|
||||
impl<A, B, Rndr> Render<Rndr> for EitherKeepAlive<A, B>
|
||||
where
|
||||
A: Render,
|
||||
B: Render,
|
||||
A: Render<Rndr>,
|
||||
B: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type State = EitherKeepAliveState<A::State, B::State>;
|
||||
|
||||
@@ -300,22 +307,23 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> AddAnyAttr for EitherKeepAlive<A, B>
|
||||
impl<A, B, Rndr> AddAnyAttr<Rndr> for EitherKeepAlive<A, B>
|
||||
where
|
||||
A: RenderHtml,
|
||||
B: RenderHtml,
|
||||
A: RenderHtml<Rndr>,
|
||||
B: RenderHtml<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> = EitherKeepAlive<
|
||||
<A as AddAnyAttr>::Output<SomeNewAttr::Cloneable>,
|
||||
<B as AddAnyAttr>::Output<SomeNewAttr::Cloneable>,
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> = EitherKeepAlive<
|
||||
<A as AddAnyAttr<Rndr>>::Output<SomeNewAttr::Cloneable>,
|
||||
<B as AddAnyAttr<Rndr>>::Output<SomeNewAttr::Cloneable>,
|
||||
>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
let EitherKeepAlive { a, b, show_b } = self;
|
||||
let attr = attr.into_cloneable();
|
||||
@@ -327,10 +335,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> RenderHtml for EitherKeepAlive<A, B>
|
||||
impl<A, B, Rndr> RenderHtml<Rndr> for EitherKeepAlive<A, B>
|
||||
where
|
||||
A: RenderHtml,
|
||||
B: RenderHtml,
|
||||
A: RenderHtml<Rndr>,
|
||||
B: RenderHtml<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type AsyncOutput = EitherKeepAlive<A::AsyncOutput, B::AsyncOutput>;
|
||||
|
||||
@@ -415,7 +424,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let showing_b = self.show_b;
|
||||
@@ -438,10 +447,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Mountable for EitherKeepAliveState<A, B>
|
||||
impl<A, B, Rndr> Mountable<Rndr> for EitherKeepAliveState<A, B>
|
||||
where
|
||||
A: Mountable,
|
||||
B: Mountable,
|
||||
A: Mountable<Rndr>,
|
||||
B: Mountable<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
if self.showing_b {
|
||||
@@ -453,8 +463,8 @@ where
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
parent: &<Rndr as Renderer>::Element,
|
||||
marker: Option<&<Rndr as Renderer>::Node>,
|
||||
) {
|
||||
if self.showing_b {
|
||||
self.b
|
||||
@@ -469,7 +479,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<Rndr>) -> bool {
|
||||
if self.showing_b {
|
||||
self.b
|
||||
.as_ref()
|
||||
@@ -488,19 +498,21 @@ macro_rules! tuples {
|
||||
($num:literal => $($ty:ident),*) => {
|
||||
paste::paste! {
|
||||
#[doc = concat!("Retained view state for ", stringify!([<EitherOf $num>]), ".")]
|
||||
pub struct [<EitherOf $num State>]<$($ty,)*>
|
||||
pub struct [<EitherOf $num State>]<$($ty,)* Rndr>
|
||||
where
|
||||
$($ty: Render,)*
|
||||
|
||||
$($ty: Render<Rndr>,)*
|
||||
Rndr: Renderer
|
||||
{
|
||||
/// Which child view state is being displayed.
|
||||
pub state: [<EitherOf $num>]<$($ty::State,)*>,
|
||||
/// The renderer.
|
||||
pub rndr: PhantomData<Rndr>
|
||||
}
|
||||
|
||||
impl<$($ty,)*> Mountable for [<EitherOf $num State>]<$($ty,)*>
|
||||
impl<$($ty,)* Rndr> Mountable<Rndr> for [<EitherOf $num State>]<$($ty,)* Rndr>
|
||||
where
|
||||
$($ty: Render,)*
|
||||
|
||||
$($ty: Render<Rndr>,)*
|
||||
Rndr: Renderer
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
match &mut self.state {
|
||||
@@ -510,8 +522,8 @@ macro_rules! tuples {
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
parent: &<Rndr as Renderer>::Element,
|
||||
marker: Option<&<Rndr as Renderer>::Node>,
|
||||
) {
|
||||
match &mut self.state {
|
||||
$([<EitherOf $num>]::$ty(this) => [<EitherOf $num>]::$ty(this.mount(parent, marker)),)*
|
||||
@@ -519,7 +531,7 @@ macro_rules! tuples {
|
||||
}
|
||||
|
||||
fn insert_before_this(&self,
|
||||
child: &mut dyn Mountable,
|
||||
child: &mut dyn Mountable<Rndr>,
|
||||
) -> bool {
|
||||
match &self.state {
|
||||
$([<EitherOf $num>]::$ty(this) =>this.insert_before_this(child),)*
|
||||
@@ -527,19 +539,19 @@ macro_rules! tuples {
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($ty,)*> Render for [<EitherOf $num>]<$($ty,)*>
|
||||
impl<Rndr, $($ty,)*> Render<Rndr> for [<EitherOf $num>]<$($ty,)*>
|
||||
where
|
||||
$($ty: Render,)*
|
||||
|
||||
$($ty: Render<Rndr>,)*
|
||||
Rndr: Renderer
|
||||
{
|
||||
type State = [<EitherOf $num State>]<$($ty,)*>;
|
||||
type State = [<EitherOf $num State>]<$($ty,)* Rndr>;
|
||||
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let state = match self {
|
||||
$([<EitherOf $num>]::$ty(this) => [<EitherOf $num>]::$ty(this.build()),)*
|
||||
};
|
||||
Self::State { state }
|
||||
Self::State { state, rndr: PhantomData }
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
@@ -564,21 +576,21 @@ macro_rules! tuples {
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($ty,)*> AddAnyAttr for [<EitherOf $num>]<$($ty,)*>
|
||||
impl<Rndr, $($ty,)*> AddAnyAttr<Rndr> for [<EitherOf $num>]<$($ty,)*>
|
||||
where
|
||||
$($ty: RenderHtml,)*
|
||||
|
||||
$($ty: RenderHtml<Rndr>,)*
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> = [<EitherOf $num>]<
|
||||
$(<$ty as AddAnyAttr>::Output<SomeNewAttr>,)*
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> = [<EitherOf $num>]<
|
||||
$(<$ty as AddAnyAttr<Rndr>>::Output<SomeNewAttr>,)*
|
||||
>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
match self {
|
||||
$([<EitherOf $num>]::$ty(this) => [<EitherOf $num>]::$ty(this.add_any_attr(attr)),)*
|
||||
@@ -586,10 +598,10 @@ macro_rules! tuples {
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($ty,)*> RenderHtml for [<EitherOf $num>]<$($ty,)*>
|
||||
impl<Rndr, $($ty,)*> RenderHtml<Rndr> for [<EitherOf $num>]<$($ty,)*>
|
||||
where
|
||||
$($ty: RenderHtml,)*
|
||||
|
||||
$($ty: RenderHtml<Rndr>,)*
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type AsyncOutput = [<EitherOf $num>]<$($ty::AsyncOutput,)*>;
|
||||
|
||||
@@ -651,7 +663,7 @@ macro_rules! tuples {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let state = match self {
|
||||
@@ -660,7 +672,7 @@ macro_rules! tuples {
|
||||
})*
|
||||
};
|
||||
|
||||
Self::State { state }
|
||||
Self::State { state, rndr: PhantomData }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,25 +3,26 @@ use crate::{
|
||||
html::attribute::Attribute,
|
||||
hydration::Cursor,
|
||||
ssr::StreamBuilder,
|
||||
view::{iterators::OptionState, Mountable, Render},
|
||||
view::{iterators::OptionState, Mountable, Render, Renderer},
|
||||
};
|
||||
use either_of::Either;
|
||||
use std::sync::Arc;
|
||||
use throw_error::{Error as AnyError, ErrorHook};
|
||||
|
||||
impl<T, E> Render for Result<T, E>
|
||||
impl<R, T, E> Render<R> for Result<T, E>
|
||||
where
|
||||
T: Render,
|
||||
T: Render<R>,
|
||||
R: Renderer,
|
||||
E: Into<AnyError> + 'static,
|
||||
{
|
||||
type State = ResultState<T>;
|
||||
type State = ResultState<T, R>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let hook = throw_error::get_error_hook();
|
||||
let (state, error) = match self {
|
||||
Ok(view) => (Either::Left(view.build()), None),
|
||||
Err(e) => (
|
||||
Either::Right(Render::build(())),
|
||||
Either::Right(Render::<R>::build(())),
|
||||
Some(throw_error::throw(e.into())),
|
||||
),
|
||||
};
|
||||
@@ -41,7 +42,7 @@ where
|
||||
}
|
||||
// Ok => Err: unmount, replace with marker, and throw
|
||||
(Either::Left(old), Err(err)) => {
|
||||
let mut new_state = Render::build(());
|
||||
let mut new_state = Render::<R>::build(());
|
||||
old.insert_before_this(&mut new_state);
|
||||
old.unmount();
|
||||
state.state = Either::Right(new_state);
|
||||
@@ -62,19 +63,21 @@ where
|
||||
}
|
||||
|
||||
/// View state for a `Result<_, _>` view.
|
||||
pub struct ResultState<T>
|
||||
pub struct ResultState<T, R>
|
||||
where
|
||||
T: Render,
|
||||
T: Render<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
/// The view state.
|
||||
state: OptionState<T>,
|
||||
state: OptionState<T, R>,
|
||||
error: Option<throw_error::ErrorId>,
|
||||
hook: Option<Arc<dyn ErrorHook>>,
|
||||
}
|
||||
|
||||
impl<T> Drop for ResultState<T>
|
||||
impl<T, R> Drop for ResultState<T, R>
|
||||
where
|
||||
T: Render,
|
||||
T: Render<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
// when the state is cleared, unregister this error; this item is being dropped and its
|
||||
@@ -85,50 +88,48 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Mountable for ResultState<T>
|
||||
impl<T, R> Mountable<R> for ResultState<T, R>
|
||||
where
|
||||
T: Render,
|
||||
T: Render<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
self.state.unmount();
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
) {
|
||||
fn mount(&mut self, parent: &R::Element, marker: Option<&R::Node>) {
|
||||
self.state.mount(parent, marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
self.state.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> AddAnyAttr for Result<T, E>
|
||||
impl<R, T, E> AddAnyAttr<R> for Result<T, E>
|
||||
where
|
||||
T: AddAnyAttr,
|
||||
|
||||
T: AddAnyAttr<R>,
|
||||
R: Renderer,
|
||||
E: Into<AnyError> + Send + 'static,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> =
|
||||
Result<<T as AddAnyAttr>::Output<SomeNewAttr>, E>;
|
||||
type Output<SomeNewAttr: Attribute<R>> =
|
||||
Result<<T as AddAnyAttr<R>>::Output<SomeNewAttr>, E>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<R>,
|
||||
{
|
||||
self.map(|inner| inner.add_any_attr(attr))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> RenderHtml for Result<T, E>
|
||||
impl<R, T, E> RenderHtml<R> for Result<T, E>
|
||||
where
|
||||
T: RenderHtml,
|
||||
T: RenderHtml<R>,
|
||||
R: Renderer,
|
||||
E: Into<AnyError> + Send + 'static,
|
||||
{
|
||||
type AsyncOutput = Result<T::AsyncOutput, E>;
|
||||
@@ -198,7 +199,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let hook = throw_error::get_error_hook();
|
||||
@@ -208,8 +209,11 @@ where
|
||||
None,
|
||||
),
|
||||
Err(e) => {
|
||||
let state =
|
||||
RenderHtml::hydrate::<FROM_SERVER>((), cursor, position);
|
||||
let state = RenderHtml::<R>::hydrate::<FROM_SERVER>(
|
||||
(),
|
||||
cursor,
|
||||
position,
|
||||
);
|
||||
(Either::Right(state), Some(throw_error::throw(e.into())))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,69 +1,72 @@
|
||||
use super::any_view::{AnyView, IntoAny};
|
||||
use crate::renderer::Renderer;
|
||||
|
||||
/// A typed-erased collection of different views.
|
||||
pub struct Fragment {
|
||||
pub struct Fragment<R: Renderer> {
|
||||
/// The nodes contained in the fragment.
|
||||
pub nodes: Vec<AnyView>,
|
||||
pub nodes: Vec<AnyView<R>>,
|
||||
}
|
||||
|
||||
/// Converts some view into a type-erased collection of views.
|
||||
pub trait IntoFragment {
|
||||
pub trait IntoFragment<R: Renderer> {
|
||||
/// Converts some view into a type-erased collection of views.
|
||||
fn into_fragment(self) -> Fragment;
|
||||
fn into_fragment(self) -> Fragment<R>;
|
||||
}
|
||||
|
||||
impl FromIterator<AnyView> for Fragment {
|
||||
fn from_iter<T: IntoIterator<Item = AnyView>>(iter: T) -> Self {
|
||||
impl<R: Renderer> FromIterator<AnyView<R>> for Fragment<R> {
|
||||
fn from_iter<T: IntoIterator<Item = AnyView<R>>>(iter: T) -> Self {
|
||||
Fragment::new(iter.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AnyView> for Fragment {
|
||||
fn from(view: AnyView) -> Self {
|
||||
impl<R: Renderer> From<AnyView<R>> for Fragment<R> {
|
||||
fn from(view: AnyView<R>) -> Self {
|
||||
Fragment::new(vec![view])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fragment> for AnyView {
|
||||
fn from(value: Fragment) -> Self {
|
||||
impl<R: Renderer> From<Fragment<R>> for AnyView<R> {
|
||||
fn from(value: Fragment<R>) -> Self {
|
||||
value.nodes.into_any()
|
||||
}
|
||||
}
|
||||
|
||||
impl Fragment {
|
||||
impl<R: Renderer> Fragment<R> {
|
||||
/// Creates a new [`Fragment`].
|
||||
#[inline(always)]
|
||||
pub fn new(nodes: Vec<AnyView>) -> Self {
|
||||
pub fn new(nodes: Vec<AnyView<R>>) -> Self {
|
||||
Self { nodes }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoFragment for Vec<T>
|
||||
impl<T, R> IntoFragment<R> for Vec<T>
|
||||
where
|
||||
T: IntoAny,
|
||||
T: IntoAny<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
fn into_fragment(self) -> Fragment {
|
||||
fn into_fragment(self) -> Fragment<R> {
|
||||
Fragment::new(self.into_iter().map(IntoAny::into_any).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, T> IntoFragment for [T; N]
|
||||
impl<const N: usize, T, R> IntoFragment<R> for [T; N]
|
||||
where
|
||||
T: IntoAny,
|
||||
T: IntoAny<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
fn into_fragment(self) -> Fragment {
|
||||
fn into_fragment(self) -> Fragment<R> {
|
||||
Fragment::new(self.into_iter().map(IntoAny::into_any).collect())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! tuples {
|
||||
($($ty:ident),*) => {
|
||||
impl<$($ty),*> IntoFragment for ($($ty,)*)
|
||||
impl<$($ty),*, Rndr> IntoFragment<Rndr> for ($($ty,)*)
|
||||
where
|
||||
$($ty: IntoAny),*,
|
||||
|
||||
$($ty: IntoAny<Rndr>),*,
|
||||
Rndr: Renderer
|
||||
{
|
||||
fn into_fragment(self) -> Fragment {
|
||||
fn into_fragment(self) -> Fragment<Rndr> {
|
||||
#[allow(non_snake_case)]
|
||||
let ($($ty,)*) = self;
|
||||
Fragment::new(vec![$($ty.into_any(),)*])
|
||||
|
||||
@@ -3,20 +3,22 @@ use super::{
|
||||
RenderHtml,
|
||||
};
|
||||
use crate::{
|
||||
html::attribute::Attribute, hydration::Cursor, renderer::Rndr,
|
||||
html::attribute::Attribute, hydration::Cursor, renderer::Renderer,
|
||||
ssr::StreamBuilder,
|
||||
};
|
||||
use either_of::Either;
|
||||
use itertools::Itertools;
|
||||
|
||||
/// Retained view state for an `Option`.
|
||||
pub type OptionState<T> = Either<<T as Render>::State, <() as Render>::State>;
|
||||
pub type OptionState<T, R> =
|
||||
Either<<T as Render<R>>::State, <() as Render<R>>::State>;
|
||||
|
||||
impl<T> Render for Option<T>
|
||||
impl<T, R> Render<R> for Option<T>
|
||||
where
|
||||
T: Render,
|
||||
T: Render<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
type State = OptionState<T>;
|
||||
type State = OptionState<T, R>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
match self {
|
||||
@@ -35,27 +37,29 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AddAnyAttr for Option<T>
|
||||
impl<T, R> AddAnyAttr<R> for Option<T>
|
||||
where
|
||||
T: AddAnyAttr,
|
||||
T: AddAnyAttr<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> =
|
||||
Option<<T as AddAnyAttr>::Output<SomeNewAttr>>;
|
||||
type Output<SomeNewAttr: Attribute<R>> =
|
||||
Option<<T as AddAnyAttr<R>>::Output<SomeNewAttr>>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<R>,
|
||||
{
|
||||
self.map(|n| n.add_any_attr(attr))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RenderHtml for Option<T>
|
||||
impl<T, R> RenderHtml<R> for Option<T>
|
||||
where
|
||||
T: RenderHtml,
|
||||
T: RenderHtml<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Option<T::AsyncOutput>;
|
||||
|
||||
@@ -119,7 +123,7 @@ where
|
||||
#[track_caller]
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
match self {
|
||||
@@ -130,14 +134,15 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Render for Vec<T>
|
||||
impl<T, R> Render<R> for Vec<T>
|
||||
where
|
||||
T: Render,
|
||||
T: Render<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
type State = VecState<T::State>;
|
||||
type State = VecState<T::State, R>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let marker = Rndr::create_placeholder();
|
||||
let marker = R::create_placeholder();
|
||||
VecState {
|
||||
states: self.into_iter().map(T::build).collect(),
|
||||
marker,
|
||||
@@ -151,7 +156,7 @@ where
|
||||
if old.is_empty() {
|
||||
let mut new = self.build().states;
|
||||
for item in new.iter_mut() {
|
||||
Rndr::mount_before(item, marker.as_ref());
|
||||
R::mount_before(item, marker.as_ref());
|
||||
}
|
||||
*old = new;
|
||||
} else if self.is_empty() {
|
||||
@@ -170,7 +175,7 @@ where
|
||||
}
|
||||
itertools::EitherOrBoth::Left(new) => {
|
||||
let mut new_state = new.build();
|
||||
Rndr::mount_before(&mut new_state, marker.as_ref());
|
||||
R::mount_before(&mut new_state, marker.as_ref());
|
||||
adds.push(new_state);
|
||||
}
|
||||
itertools::EitherOrBoth::Right(old) => {
|
||||
@@ -186,21 +191,23 @@ where
|
||||
}
|
||||
|
||||
/// Retained view state for a `Vec<_>`.
|
||||
pub struct VecState<T>
|
||||
pub struct VecState<T, R>
|
||||
where
|
||||
T: Mountable,
|
||||
T: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
states: Vec<T>,
|
||||
// Vecs keep a placeholder because they have the potential to add additional items,
|
||||
// after their own items but before the next neighbor. It is much easier to add an
|
||||
// item before a known placeholder than to add it after the last known item, so we
|
||||
// just leave a placeholder here unlike zero-or-one iterators (Option, Result, etc.)
|
||||
marker: crate::renderer::types::Placeholder,
|
||||
marker: R::Placeholder,
|
||||
}
|
||||
|
||||
impl<T> Mountable for VecState<T>
|
||||
impl<T, R> Mountable<R> for VecState<T, R>
|
||||
where
|
||||
T: Mountable,
|
||||
T: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
for state in self.states.iter_mut() {
|
||||
@@ -211,8 +218,8 @@ where
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
parent: &<R as Renderer>::Element,
|
||||
marker: Option<&<R as Renderer>::Node>,
|
||||
) {
|
||||
for state in self.states.iter_mut() {
|
||||
state.mount(parent, marker);
|
||||
@@ -220,7 +227,7 @@ where
|
||||
self.marker.mount(parent, marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
if let Some(first) = self.states.first() {
|
||||
first.insert_before_this(child)
|
||||
} else {
|
||||
@@ -229,19 +236,20 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AddAnyAttr for Vec<T>
|
||||
impl<T, R> AddAnyAttr<R> for Vec<T>
|
||||
where
|
||||
T: AddAnyAttr,
|
||||
T: AddAnyAttr<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> =
|
||||
Vec<<T as AddAnyAttr>::Output<SomeNewAttr::Cloneable>>;
|
||||
type Output<SomeNewAttr: Attribute<R>> =
|
||||
Vec<<T as AddAnyAttr<R>>::Output<SomeNewAttr::Cloneable>>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<R>,
|
||||
{
|
||||
let attr = attr.into_cloneable();
|
||||
self.into_iter()
|
||||
@@ -250,9 +258,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RenderHtml for Vec<T>
|
||||
impl<T, R> RenderHtml<R> for Vec<T>
|
||||
where
|
||||
T: RenderHtml,
|
||||
T: RenderHtml<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Vec<T::AsyncOutput>;
|
||||
|
||||
@@ -323,7 +332,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let states = self
|
||||
|
||||
@@ -5,74 +5,84 @@ use super::{
|
||||
use crate::{
|
||||
html::attribute::Attribute,
|
||||
hydration::Cursor,
|
||||
renderer::{CastFrom, Rndr},
|
||||
renderer::{CastFrom, Renderer},
|
||||
ssr::StreamBuilder,
|
||||
};
|
||||
use drain_filter_polyfill::VecExt as VecDrainFilterExt;
|
||||
use indexmap::IndexSet;
|
||||
use rustc_hash::FxHasher;
|
||||
use std::hash::{BuildHasherDefault, Hash};
|
||||
use std::{
|
||||
hash::{BuildHasherDefault, Hash},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
type FxIndexSet<T> = IndexSet<T, BuildHasherDefault<FxHasher>>;
|
||||
|
||||
/// Creates a keyed list of views.
|
||||
pub fn keyed<T, I, K, KF, VF, VFS, V>(
|
||||
pub fn keyed<T, I, K, KF, VF, VFS, V, Rndr>(
|
||||
items: I,
|
||||
key_fn: KF,
|
||||
view_fn: VF,
|
||||
) -> Keyed<T, I, K, KF, VF, VFS, V>
|
||||
) -> Keyed<T, I, K, KF, VF, VFS, V, Rndr>
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
K: Eq + Hash + 'static,
|
||||
KF: Fn(&T) -> K,
|
||||
V: Render,
|
||||
V: Render<Rndr>,
|
||||
VF: Fn(usize, T) -> (VFS, V),
|
||||
VFS: Fn(usize),
|
||||
Rndr: Renderer,
|
||||
{
|
||||
Keyed {
|
||||
items,
|
||||
key_fn,
|
||||
view_fn,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// A keyed list of views.
|
||||
pub struct Keyed<T, I, K, KF, VF, VFS, V>
|
||||
pub struct Keyed<T, I, K, KF, VF, VFS, V, Rndr>
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
K: Eq + Hash + 'static,
|
||||
KF: Fn(&T) -> K,
|
||||
VF: Fn(usize, T) -> (VFS, V),
|
||||
VFS: Fn(usize),
|
||||
Rndr: Renderer,
|
||||
{
|
||||
items: I,
|
||||
key_fn: KF,
|
||||
view_fn: VF,
|
||||
rndr: PhantomData<Rndr>,
|
||||
}
|
||||
|
||||
/// Retained view state for a keyed list.
|
||||
pub struct KeyedState<K, VFS, V>
|
||||
pub struct KeyedState<K, VFS, V, Rndr>
|
||||
where
|
||||
K: Eq + Hash + 'static,
|
||||
VFS: Fn(usize),
|
||||
V: Render,
|
||||
V: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
parent: Option<crate::renderer::types::Element>,
|
||||
marker: crate::renderer::types::Placeholder,
|
||||
parent: Option<Rndr::Element>,
|
||||
marker: Rndr::Placeholder,
|
||||
hashed_items: IndexSet<K, BuildHasherDefault<FxHasher>>,
|
||||
rendered_items: Vec<Option<(VFS, V::State)>>,
|
||||
}
|
||||
|
||||
impl<T, I, K, KF, VF, VFS, V> Render for Keyed<T, I, K, KF, VF, VFS, V>
|
||||
impl<T, I, K, KF, VF, VFS, V, Rndr> Render<Rndr>
|
||||
for Keyed<T, I, K, KF, VF, VFS, V, Rndr>
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
K: Eq + Hash + 'static,
|
||||
KF: Fn(&T) -> K,
|
||||
V: Render,
|
||||
V: Render<Rndr>,
|
||||
VF: Fn(usize, T) -> (VFS, V),
|
||||
VFS: Fn(usize),
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type State = KeyedState<K, VFS, V>;
|
||||
type State = KeyedState<K, VFS, V, Rndr>;
|
||||
// TODO fallible state and try_build()/try_rebuild() here
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
@@ -129,18 +139,20 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I, K, KF, VF, VFS, V> AddAnyAttr for Keyed<T, I, K, KF, VF, VFS, V>
|
||||
impl<T, I, K, KF, VF, VFS, V, Rndr> AddAnyAttr<Rndr>
|
||||
for Keyed<T, I, K, KF, VF, VFS, V, Rndr>
|
||||
where
|
||||
I: IntoIterator<Item = T> + Send,
|
||||
K: Eq + Hash + 'static,
|
||||
KF: Fn(&T) -> K + Send,
|
||||
V: RenderHtml,
|
||||
V: RenderHtml<Rndr>,
|
||||
V: 'static,
|
||||
VF: Fn(usize, T) -> (VFS, V) + Send + 'static,
|
||||
VFS: Fn(usize) + 'static,
|
||||
T: 'static,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> = Keyed<
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> = Keyed<
|
||||
T,
|
||||
I,
|
||||
K,
|
||||
@@ -151,24 +163,28 @@ where
|
||||
T,
|
||||
) -> (
|
||||
VFS,
|
||||
<V as AddAnyAttr>::Output<SomeNewAttr::CloneableOwned>,
|
||||
<V as AddAnyAttr<Rndr>>::Output<
|
||||
SomeNewAttr::CloneableOwned,
|
||||
>,
|
||||
) + Send,
|
||||
>,
|
||||
VFS,
|
||||
V::Output<SomeNewAttr::CloneableOwned>,
|
||||
Rndr,
|
||||
>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
let Keyed {
|
||||
items,
|
||||
key_fn,
|
||||
view_fn,
|
||||
rndr,
|
||||
} = self;
|
||||
let attr = attr.into_cloneable_owned();
|
||||
Keyed {
|
||||
@@ -178,19 +194,22 @@ where
|
||||
let (index, view) = view_fn(index, item);
|
||||
(index, view.add_any_attr(attr.clone()))
|
||||
}),
|
||||
rndr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I, K, KF, VF, VFS, V> RenderHtml for Keyed<T, I, K, KF, VF, VFS, V>
|
||||
impl<T, I, K, KF, VF, VFS, V, Rndr> RenderHtml<Rndr>
|
||||
for Keyed<T, I, K, KF, VF, VFS, V, Rndr>
|
||||
where
|
||||
I: IntoIterator<Item = T> + Send,
|
||||
K: Eq + Hash + 'static,
|
||||
KF: Fn(&T) -> K + Send,
|
||||
V: RenderHtml + 'static,
|
||||
V: RenderHtml<Rndr> + 'static,
|
||||
VF: Fn(usize, T) -> (VFS, V) + Send + 'static,
|
||||
VFS: Fn(usize) + 'static,
|
||||
T: 'static,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type AsyncOutput = Vec<V::AsyncOutput>; // TODO
|
||||
|
||||
@@ -249,7 +268,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
// get parent and position
|
||||
@@ -260,7 +279,7 @@ where
|
||||
Rndr::get_parent(¤t)
|
||||
.expect("first child of keyed list has no parent")
|
||||
};
|
||||
let parent = crate::renderer::types::Element::cast_from(parent)
|
||||
let parent = Rndr::Element::cast_from(parent)
|
||||
.expect("parent of keyed list should be an element");
|
||||
|
||||
// build list
|
||||
@@ -285,17 +304,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, VFS, V> Mountable for KeyedState<K, VFS, V>
|
||||
impl<K, VFS, V, Rndr> Mountable<Rndr> for KeyedState<K, VFS, V, Rndr>
|
||||
where
|
||||
K: Eq + Hash + 'static,
|
||||
VFS: Fn(usize),
|
||||
V: Render,
|
||||
V: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
) {
|
||||
fn mount(&mut self, parent: &Rndr::Element, marker: Option<&Rndr::Node>) {
|
||||
self.parent = Some(parent.clone());
|
||||
for (_, item) in self.rendered_items.iter_mut().flatten() {
|
||||
item.mount(parent, marker);
|
||||
@@ -310,7 +326,7 @@ where
|
||||
self.marker.unmount();
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<Rndr>) -> bool {
|
||||
self.rendered_items
|
||||
.first()
|
||||
.map(|item| {
|
||||
@@ -503,16 +519,17 @@ impl Default for DiffOpAddMode {
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_diff<T, VFS, V>(
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: &crate::renderer::types::Placeholder,
|
||||
fn apply_diff<T, VFS, V, Rndr>(
|
||||
parent: &Rndr::Element,
|
||||
marker: &Rndr::Placeholder,
|
||||
diff: Diff,
|
||||
children: &mut Vec<Option<(VFS, V::State)>>,
|
||||
view_fn: impl Fn(usize, T) -> (VFS, V),
|
||||
mut items: Vec<Option<T>>,
|
||||
) where
|
||||
VFS: Fn(usize),
|
||||
V: Render,
|
||||
V: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
// The order of cmds needs to be:
|
||||
// 1. Clear
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
use self::add_attr::AddAnyAttr;
|
||||
use crate::{hydration::Cursor, ssr::StreamBuilder};
|
||||
use crate::{hydration::Cursor, renderer::Renderer, ssr::StreamBuilder};
|
||||
use parking_lot::RwLock;
|
||||
use std::{cell::RefCell, future::Future, rc::Rc, sync::Arc};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
future::Future,
|
||||
ops::{Deref, DerefMut},
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
/// Add attributes to typed views.
|
||||
pub mod add_attr;
|
||||
@@ -32,12 +38,12 @@ pub mod tuples;
|
||||
///
|
||||
/// It is generic over the renderer itself, as long as that implements the [`Renderer`]
|
||||
/// trait.
|
||||
pub trait Render: Sized {
|
||||
pub trait Render<R: Renderer>: Sized {
|
||||
/// The “view state” for this type, which can be retained between updates.
|
||||
///
|
||||
/// For example, for a text node, `State` might be the actual DOM text node
|
||||
/// and the previous string, to allow for diffing between updates.
|
||||
type State: Mountable;
|
||||
type State: Mountable<R>;
|
||||
|
||||
/// Creates the view for the first time, without hydrating from existing HTML.
|
||||
fn build(self) -> Self::State;
|
||||
@@ -92,12 +98,12 @@ impl MarkBranch for StreamBuilder {
|
||||
/// can be transformed into some HTML that is used to create a `<template>` node, which
|
||||
/// can be cloned many times and “hydrated,” which is more efficient than creating the
|
||||
/// whole view piece by piece.
|
||||
pub trait RenderHtml
|
||||
pub trait RenderHtml<R: Renderer>
|
||||
where
|
||||
Self: Render + AddAnyAttr + Send,
|
||||
Self: Render<R> + AddAnyAttr<R> + Send,
|
||||
{
|
||||
/// The type of the view after waiting for all asynchronous data to load.
|
||||
type AsyncOutput: RenderHtml;
|
||||
type AsyncOutput: RenderHtml<R>;
|
||||
|
||||
/// The minimum length of HTML created when this view is rendered.
|
||||
const MIN_LENGTH: usize;
|
||||
@@ -247,14 +253,14 @@ where
|
||||
/// (e.g., into a `<template>` element).
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State;
|
||||
|
||||
/// Hydrates using [`RenderHtml::hydrate`], beginning at the given element.
|
||||
fn hydrate_from<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
) -> Self::State
|
||||
where
|
||||
Self: Sized,
|
||||
@@ -265,7 +271,7 @@ where
|
||||
/// Hydrates using [`RenderHtml::hydrate`], beginning at the given element and position.
|
||||
fn hydrate_from_position<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &crate::renderer::types::Element,
|
||||
el: &R::Element,
|
||||
position: Position,
|
||||
) -> Self::State
|
||||
where
|
||||
@@ -278,28 +284,24 @@ where
|
||||
}
|
||||
|
||||
/// Allows a type to be mounted to the DOM.
|
||||
pub trait Mountable {
|
||||
pub trait Mountable<R: Renderer> {
|
||||
/// Detaches the view from the DOM.
|
||||
fn unmount(&mut self);
|
||||
|
||||
/// Mounts a node to the interface.
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
);
|
||||
fn mount(&mut self, parent: &R::Element, marker: Option<&R::Node>);
|
||||
|
||||
/// Inserts another `Mountable` type before this one. Returns `false` if
|
||||
/// this does not actually exist in the UI (for example, `()`).
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool;
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool;
|
||||
|
||||
/// Inserts another `Mountable` type before this one, or before the marker
|
||||
/// if this one doesn't exist in the UI (for example, `()`).
|
||||
fn insert_before_this_or_marker(
|
||||
&self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
child: &mut dyn Mountable,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
parent: &R::Element,
|
||||
child: &mut dyn Mountable<R>,
|
||||
marker: Option<&R::Node>,
|
||||
) {
|
||||
if !self.insert_before_this(child) {
|
||||
child.mount(parent, marker);
|
||||
@@ -308,16 +310,20 @@ pub trait Mountable {
|
||||
}
|
||||
|
||||
/// Indicates where a node should be mounted to its parent.
|
||||
pub enum MountKind {
|
||||
pub enum MountKind<R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
/// Node should be mounted before this marker node.
|
||||
Before(crate::renderer::types::Node),
|
||||
Before(R::Node),
|
||||
/// Node should be appended to the parent’s children.
|
||||
Append,
|
||||
}
|
||||
|
||||
impl<T> Mountable for Option<T>
|
||||
impl<T, R> Mountable<R> for Option<T>
|
||||
where
|
||||
T: Mountable,
|
||||
T: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
if let Some(ref mut mounted) = self {
|
||||
@@ -325,40 +331,33 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
) {
|
||||
fn mount(&mut self, parent: &R::Element, marker: Option<&R::Node>) {
|
||||
if let Some(ref mut inner) = self {
|
||||
inner.mount(parent, marker);
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
self.as_ref()
|
||||
.map(|inner| inner.insert_before_this(child))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Mountable for Rc<RefCell<T>>
|
||||
impl<T, R> Mountable<R> for Rc<RefCell<T>>
|
||||
where
|
||||
T: Mountable,
|
||||
T: Mountable<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
self.borrow_mut().unmount()
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
) {
|
||||
fn mount(&mut self, parent: &R::Element, marker: Option<&R::Node>) {
|
||||
self.borrow_mut().mount(parent, marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
self.borrow().insert_before_this(child)
|
||||
}
|
||||
}
|
||||
@@ -432,3 +431,229 @@ pub enum Position {
|
||||
/// This is the last child of its parent.
|
||||
LastChild,
|
||||
}
|
||||
|
||||
/// A view stored on the heap.
|
||||
///
|
||||
/// This is a newtype around `Box<_>` that allows us to implement rendering traits on it.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct BoxedView<T>(Box<T>);
|
||||
|
||||
impl<T> BoxedView<T> {
|
||||
/// Stores view on the heap.
|
||||
pub fn new(value: T) -> Self {
|
||||
Self(Box::new(value))
|
||||
}
|
||||
|
||||
/// Deferences the view to its inner value.
|
||||
pub fn into_inner(self) -> T {
|
||||
*self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for BoxedView<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsMut<T> for BoxedView<T> {
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for BoxedView<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for BoxedView<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Rndr> Render<Rndr> for BoxedView<T>
|
||||
where
|
||||
T: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type State = T::State;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
self.into_inner().build()
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
self.into_inner().rebuild(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Rndr> RenderHtml<Rndr> for BoxedView<T>
|
||||
where
|
||||
T: RenderHtml<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type AsyncOutput = BoxedView<T::AsyncOutput>;
|
||||
|
||||
const MIN_LENGTH: usize = T::MIN_LENGTH;
|
||||
|
||||
fn dry_resolve(&mut self) {
|
||||
self.as_mut().dry_resolve();
|
||||
}
|
||||
|
||||
async fn resolve(self) -> Self::AsyncOutput {
|
||||
let inner = self.into_inner().resolve().await;
|
||||
BoxedView::new(inner)
|
||||
}
|
||||
|
||||
fn to_html_with_buf(
|
||||
self,
|
||||
buf: &mut String,
|
||||
position: &mut Position,
|
||||
escape: bool,
|
||||
mark_branches: bool,
|
||||
) {
|
||||
self.into_inner()
|
||||
.to_html_with_buf(buf, position, escape, mark_branches)
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
self.into_inner().hydrate::<FROM_SERVER>(cursor, position)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ToTemplate for BoxedView<T>
|
||||
where
|
||||
T: ToTemplate,
|
||||
{
|
||||
fn to_template(
|
||||
buf: &mut String,
|
||||
class: &mut String,
|
||||
style: &mut String,
|
||||
inner_html: &mut String,
|
||||
position: &mut Position,
|
||||
) {
|
||||
T::to_template(buf, class, style, inner_html, position);
|
||||
}
|
||||
}
|
||||
|
||||
/// A newtype around a view that allows us to get out of certain compile errors.
|
||||
///
|
||||
/// It is unlikely that you need this in your own work.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct WrappedView<T>(T);
|
||||
|
||||
impl<T> WrappedView<T> {
|
||||
/// Wraps the view.
|
||||
pub fn new(value: T) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
|
||||
/// Unwraps the view to its inner value.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for WrappedView<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for WrappedView<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for WrappedView<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsMut<T> for WrappedView<T> {
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Rndr> Render<Rndr> for WrappedView<T>
|
||||
where
|
||||
T: Render<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type State = T::State;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
self.into_inner().build()
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
self.into_inner().rebuild(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Rndr> RenderHtml<Rndr> for WrappedView<T>
|
||||
where
|
||||
T: RenderHtml<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type AsyncOutput = BoxedView<T::AsyncOutput>;
|
||||
|
||||
const MIN_LENGTH: usize = T::MIN_LENGTH;
|
||||
|
||||
fn dry_resolve(&mut self) {
|
||||
self.as_mut().dry_resolve();
|
||||
}
|
||||
|
||||
async fn resolve(self) -> Self::AsyncOutput {
|
||||
let inner = self.into_inner().resolve().await;
|
||||
BoxedView::new(inner)
|
||||
}
|
||||
|
||||
fn to_html_with_buf(
|
||||
self,
|
||||
buf: &mut String,
|
||||
position: &mut Position,
|
||||
escape: bool,
|
||||
mark_branches: bool,
|
||||
) {
|
||||
self.into_inner()
|
||||
.to_html_with_buf(buf, position, escape, mark_branches)
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor<Rndr>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
self.into_inner().hydrate::<FROM_SERVER>(cursor, position)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ToTemplate for WrappedView<T>
|
||||
where
|
||||
T: ToTemplate,
|
||||
{
|
||||
fn to_template(
|
||||
buf: &mut String,
|
||||
class: &mut String,
|
||||
style: &mut String,
|
||||
inner_html: &mut String,
|
||||
position: &mut Position,
|
||||
) {
|
||||
T::to_template(buf, class, style, inner_html, position);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::{Mountable, Position, PositionState, Render, RenderHtml};
|
||||
use crate::{
|
||||
hydration::Cursor,
|
||||
no_attrs,
|
||||
renderer::{CastFrom, Rndr},
|
||||
renderer::{CastFrom, Renderer},
|
||||
view::ToTemplate,
|
||||
};
|
||||
use std::{
|
||||
@@ -20,41 +20,41 @@ macro_rules! render_primitive {
|
||||
($($child_type:ty),* $(,)?) => {
|
||||
$(
|
||||
paste::paste! {
|
||||
pub struct [<$child_type:camel State>](crate::renderer::types::Text, $child_type);
|
||||
pub struct [<$child_type:camel State>]<R>(R::Text, $child_type) where R: Renderer;
|
||||
|
||||
impl Mountable for [<$child_type:camel State>] {
|
||||
impl<R: Renderer> Mountable<R> for [<$child_type:camel State>]<R> {
|
||||
fn unmount(&mut self) {
|
||||
self.0.unmount()
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
parent: &<R as Renderer>::Element,
|
||||
marker: Option<&<R as Renderer>::Node>,
|
||||
) {
|
||||
Rndr::insert_node(parent, self.0.as_ref(), marker);
|
||||
R::insert_node(parent, self.0.as_ref(), marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self,
|
||||
child: &mut dyn Mountable,
|
||||
child: &mut dyn Mountable<R>,
|
||||
) -> bool {
|
||||
self.0.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for $child_type {
|
||||
type State = [<$child_type:camel State>];
|
||||
impl<R: Renderer> Render<R> for $child_type {
|
||||
type State = [<$child_type:camel State>]<R>;
|
||||
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let node = Rndr::create_text_node(&self.to_string());
|
||||
let node = R::create_text_node(&self.to_string());
|
||||
[<$child_type:camel State>](node, self)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let [<$child_type:camel State>](node, this) = state;
|
||||
if &self != this {
|
||||
Rndr::set_text(node, &self.to_string());
|
||||
R::set_text(node, &self.to_string());
|
||||
*this = self;
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,9 @@ macro_rules! render_primitive {
|
||||
|
||||
no_attrs!($child_type);
|
||||
|
||||
impl RenderHtml for $child_type
|
||||
impl<R> RenderHtml<R> for $child_type
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
@@ -85,7 +87,7 @@ macro_rules! render_primitive {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
if position.get() == Position::FirstChild {
|
||||
@@ -100,11 +102,11 @@ macro_rules! render_primitive {
|
||||
}
|
||||
|
||||
let node = cursor.current();
|
||||
let node = crate::renderer::types::Text::cast_from(node)
|
||||
let node = R::Text::cast_from(node)
|
||||
.expect("couldn't cast text node from node");
|
||||
|
||||
if !FROM_SERVER {
|
||||
Rndr::set_text(&node, &self.to_string());
|
||||
R::set_text(&node, &self.to_string());
|
||||
}
|
||||
position.set(Position::NextChildAfterText);
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use super::{
|
||||
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
|
||||
RenderHtml, ToTemplate,
|
||||
RenderHtml, ToTemplate, WrappedView,
|
||||
};
|
||||
use crate::{
|
||||
html::attribute::{Attribute, AttributeKey, AttributeValue, NextAttribute},
|
||||
hydration::Cursor,
|
||||
renderer::Rndr,
|
||||
renderer::Renderer,
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
@@ -54,9 +54,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, const V: &'static str> Attribute for StaticAttr<K, V>
|
||||
impl<K, const V: &'static str, R> Attribute<R> for StaticAttr<K, V>
|
||||
where
|
||||
K: AttributeKey,
|
||||
R: Renderer,
|
||||
{
|
||||
const MIN_LENGTH: usize = K::KEY.len() + 3 + V.len(); // K::KEY + ="..." + V
|
||||
|
||||
@@ -77,17 +78,14 @@ where
|
||||
_style: &mut String,
|
||||
_inner_html: &mut String,
|
||||
) {
|
||||
AttributeValue::to_html(V, K::KEY, buf)
|
||||
AttributeValue::<R>::to_html(V, K::KEY, buf)
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
_el: &crate::renderer::types::Element,
|
||||
) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, _el: &R::Element) -> Self::State {
|
||||
}
|
||||
|
||||
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
|
||||
Rndr::set_attribute(el, K::KEY, V);
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
R::set_attribute(el, K::KEY, V);
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
@@ -107,13 +105,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, const V: &'static str> NextAttribute for StaticAttr<K, V>
|
||||
impl<K, const V: &'static str, R> NextAttribute<R> for StaticAttr<K, V>
|
||||
where
|
||||
K: AttributeKey,
|
||||
R: Renderer,
|
||||
{
|
||||
type Output<NewAttr: Attribute> = (Self, NewAttr);
|
||||
type Output<NewAttr: Attribute<R>> = (Self, NewAttr);
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
new_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr> {
|
||||
@@ -139,22 +138,27 @@ impl<const V: &'static str> AsRef<str> for Static<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<const V: &'static str> Render for Static<V>
|
||||
impl<const V: &'static str, R: Renderer> Render<R> for Static<V>
|
||||
where
|
||||
crate::renderer::types::Text: Mountable,
|
||||
R::Text: Mountable<R>,
|
||||
{
|
||||
type State = Option<crate::renderer::types::Text>;
|
||||
type State = Option<R::Text>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
// a view state has to be returned so it can be mounted
|
||||
Some(Rndr::create_text_node(V))
|
||||
Some(R::create_text_node(V))
|
||||
}
|
||||
|
||||
// This type is specified as static, so no rebuilding is done.
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
}
|
||||
|
||||
impl<const V: &'static str> RenderHtml for Static<V> {
|
||||
impl<const V: &'static str, R> RenderHtml<R> for Static<V>
|
||||
where
|
||||
R: Renderer,
|
||||
|
||||
R::Text: Mountable<R>,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
const MIN_LENGTH: usize = V.len();
|
||||
@@ -190,7 +194,7 @@ impl<const V: &'static str> RenderHtml for Static<V> {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
if position.get() == Position::FirstChild {
|
||||
@@ -208,17 +212,20 @@ impl<const V: &'static str> RenderHtml for Static<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<const V: &'static str> AddAnyAttr for Static<V> {
|
||||
type Output<SomeNewAttr: Attribute> = Static<V>;
|
||||
impl<R, const V: &'static str> AddAnyAttr<R> for Static<V>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute<R>> = WrappedView<Static<V>>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<R>,
|
||||
{
|
||||
todo!()
|
||||
WrappedView::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use super::{
|
||||
use crate::{
|
||||
hydration::Cursor,
|
||||
no_attrs,
|
||||
renderer::{CastFrom, Rndr},
|
||||
renderer::{CastFrom, Renderer},
|
||||
};
|
||||
use std::{borrow::Cow, rc::Rc, sync::Arc};
|
||||
|
||||
@@ -14,29 +14,32 @@ no_attrs!(Arc<str>);
|
||||
no_attrs!(Cow<'a, str>);
|
||||
|
||||
/// Retained view state for `&str`.
|
||||
pub struct StrState<'a> {
|
||||
pub(crate) node: crate::renderer::types::Text,
|
||||
pub struct StrState<'a, R: Renderer> {
|
||||
pub(crate) node: R::Text,
|
||||
str: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> Render for &'a str {
|
||||
type State = StrState<'a>;
|
||||
impl<'a, R: Renderer> Render<R> for &'a str {
|
||||
type State = StrState<'a, R>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let node = Rndr::create_text_node(self);
|
||||
let node = R::create_text_node(self);
|
||||
StrState { node, str: self }
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let StrState { node, str } = state;
|
||||
if &self != str {
|
||||
Rndr::set_text(node, self);
|
||||
R::set_text(node, self);
|
||||
*str = self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RenderHtml for &'a str {
|
||||
impl<'a, R> RenderHtml<R> for &'a str
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
const MIN_LENGTH: usize = 0;
|
||||
@@ -75,7 +78,7 @@ impl<'a> RenderHtml for &'a str {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
if position.get() == Position::FirstChild {
|
||||
@@ -90,11 +93,11 @@ impl<'a> RenderHtml for &'a str {
|
||||
}
|
||||
|
||||
let node = cursor.current();
|
||||
let node = crate::renderer::types::Text::cast_from(node)
|
||||
let node = R::Text::cast_from(node)
|
||||
.expect("couldn't cast text node from node");
|
||||
|
||||
if !FROM_SERVER {
|
||||
Rndr::set_text(&node, self);
|
||||
R::set_text(&node, self);
|
||||
}
|
||||
position.set(Position::NextChildAfterText);
|
||||
|
||||
@@ -120,48 +123,54 @@ impl<'a> ToTemplate for &'a str {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mountable for StrState<'a> {
|
||||
impl<'a, R> Mountable<R> for StrState<'a, R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
self.node.unmount()
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
parent: &<R as Renderer>::Element,
|
||||
marker: Option<&<R as Renderer>::Node>,
|
||||
) {
|
||||
Rndr::insert_node(parent, self.node.as_ref(), marker);
|
||||
R::insert_node(parent, self.node.as_ref(), marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
self.node.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
/// Retained view state for `String`.
|
||||
pub struct StringState {
|
||||
node: crate::renderer::types::Text,
|
||||
pub struct StringState<R: Renderer> {
|
||||
node: R::Text,
|
||||
str: String,
|
||||
}
|
||||
|
||||
impl Render for String {
|
||||
type State = StringState;
|
||||
impl<R: Renderer> Render<R> for String {
|
||||
type State = StringState<R>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let node = Rndr::create_text_node(&self);
|
||||
let node = R::create_text_node(&self);
|
||||
StringState { node, str: self }
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let StringState { node, str } = state;
|
||||
if &self != str {
|
||||
Rndr::set_text(node, &self);
|
||||
R::set_text(node, &self);
|
||||
*str = self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderHtml for String {
|
||||
impl<R> RenderHtml<R> for String
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
const MIN_LENGTH: usize = 0;
|
||||
type AsyncOutput = Self;
|
||||
|
||||
@@ -182,7 +191,7 @@ impl RenderHtml for String {
|
||||
escape: bool,
|
||||
mark_branches: bool,
|
||||
) {
|
||||
<&str as RenderHtml>::to_html_with_buf(
|
||||
<&str as RenderHtml<R>>::to_html_with_buf(
|
||||
self.as_str(),
|
||||
buf,
|
||||
position,
|
||||
@@ -193,7 +202,7 @@ impl RenderHtml for String {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let StrState { node, .. } =
|
||||
@@ -218,42 +227,42 @@ impl ToTemplate for String {
|
||||
}
|
||||
}
|
||||
|
||||
impl Mountable for StringState {
|
||||
impl<R: Renderer> Mountable<R> for StringState<R> {
|
||||
fn unmount(&mut self) {
|
||||
self.node.unmount()
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
parent: &<R as Renderer>::Element,
|
||||
marker: Option<&<R as Renderer>::Node>,
|
||||
) {
|
||||
Rndr::insert_node(parent, self.node.as_ref(), marker);
|
||||
R::insert_node(parent, self.node.as_ref(), marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
self.node.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
/// Retained view state for `Rc<str>`.
|
||||
pub struct RcStrState {
|
||||
node: crate::renderer::types::Text,
|
||||
pub struct RcStrState<R: Renderer> {
|
||||
node: R::Text,
|
||||
str: Rc<str>,
|
||||
}
|
||||
|
||||
impl Render for Rc<str> {
|
||||
type State = RcStrState;
|
||||
impl<R: Renderer> Render<R> for Rc<str> {
|
||||
type State = RcStrState<R>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let node = Rndr::create_text_node(&self);
|
||||
let node = R::create_text_node(&self);
|
||||
RcStrState { node, str: self }
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let RcStrState { node, str } = state;
|
||||
if !Rc::ptr_eq(&self, str) {
|
||||
Rndr::set_text(node, &self);
|
||||
R::set_text(node, &self);
|
||||
*str = self;
|
||||
}
|
||||
}
|
||||
@@ -262,9 +271,9 @@ impl Render for Rc<str> {
|
||||
// can't Send an Rc<str> between threads, so can't implement async HTML rendering that might need
|
||||
// to send it
|
||||
/*
|
||||
impl RenderHtml for Rc<str>
|
||||
impl<R> RenderHtml<R> for Rc<str>
|
||||
where
|
||||
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
@@ -279,12 +288,12 @@ where
|
||||
}
|
||||
|
||||
fn to_html_with_buf(self, buf: &mut String, position: &mut Position, escape: bool, mark_branches: bool) {
|
||||
<&str as RenderHtml>::to_html_with_buf(&self, buf, position)
|
||||
<&str as RenderHtml<R>>::to_html_with_buf(&self, buf, position)
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let this: &str = self.as_ref();
|
||||
@@ -310,48 +319,51 @@ impl ToTemplate for Rc<str> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Mountable for RcStrState {
|
||||
impl<R: Renderer> Mountable<R> for RcStrState<R> {
|
||||
fn unmount(&mut self) {
|
||||
self.node.unmount()
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
parent: &<R as Renderer>::Element,
|
||||
marker: Option<&<R as Renderer>::Node>,
|
||||
) {
|
||||
Rndr::insert_node(parent, self.node.as_ref(), marker);
|
||||
R::insert_node(parent, self.node.as_ref(), marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
self.node.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
/// Retained view state for `Arc<str>`.
|
||||
pub struct ArcStrState {
|
||||
node: crate::renderer::types::Text,
|
||||
pub struct ArcStrState<R: Renderer> {
|
||||
node: R::Text,
|
||||
str: Arc<str>,
|
||||
}
|
||||
|
||||
impl Render for Arc<str> {
|
||||
type State = ArcStrState;
|
||||
impl<R: Renderer> Render<R> for Arc<str> {
|
||||
type State = ArcStrState<R>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let node = Rndr::create_text_node(&self);
|
||||
let node = R::create_text_node(&self);
|
||||
ArcStrState { node, str: self }
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let ArcStrState { node, str } = state;
|
||||
if !Arc::ptr_eq(&self, str) {
|
||||
Rndr::set_text(node, &self);
|
||||
R::set_text(node, &self);
|
||||
*str = self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderHtml for Arc<str> {
|
||||
impl<R> RenderHtml<R> for Arc<str>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
const MIN_LENGTH: usize = 0;
|
||||
@@ -373,7 +385,7 @@ impl RenderHtml for Arc<str> {
|
||||
escape: bool,
|
||||
mark_branches: bool,
|
||||
) {
|
||||
<&str as RenderHtml>::to_html_with_buf(
|
||||
<&str as RenderHtml<R>>::to_html_with_buf(
|
||||
&self,
|
||||
buf,
|
||||
position,
|
||||
@@ -384,7 +396,7 @@ impl RenderHtml for Arc<str> {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let this: &str = self.as_ref();
|
||||
@@ -410,48 +422,51 @@ impl ToTemplate for Arc<str> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Mountable for ArcStrState {
|
||||
impl<R: Renderer> Mountable<R> for ArcStrState<R> {
|
||||
fn unmount(&mut self) {
|
||||
self.node.unmount()
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
parent: &<R as Renderer>::Element,
|
||||
marker: Option<&<R as Renderer>::Node>,
|
||||
) {
|
||||
Rndr::insert_node(parent, self.node.as_ref(), marker);
|
||||
R::insert_node(parent, self.node.as_ref(), marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
self.node.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
/// Retained view state for `Cow<'_, str>`.
|
||||
pub struct CowStrState<'a> {
|
||||
node: crate::renderer::types::Text,
|
||||
pub struct CowStrState<'a, R: Renderer> {
|
||||
node: R::Text,
|
||||
str: Cow<'a, str>,
|
||||
}
|
||||
|
||||
impl<'a> Render for Cow<'a, str> {
|
||||
type State = CowStrState<'a>;
|
||||
impl<'a, R: Renderer> Render<R> for Cow<'a, str> {
|
||||
type State = CowStrState<'a, R>;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let node = Rndr::create_text_node(&self);
|
||||
let node = R::create_text_node(&self);
|
||||
CowStrState { node, str: self }
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let CowStrState { node, str } = state;
|
||||
if self != *str {
|
||||
Rndr::set_text(node, &self);
|
||||
R::set_text(node, &self);
|
||||
*str = self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RenderHtml for Cow<'a, str> {
|
||||
impl<'a, R> RenderHtml<R> for Cow<'a, str>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = Self;
|
||||
|
||||
const MIN_LENGTH: usize = 0;
|
||||
@@ -473,7 +488,7 @@ impl<'a> RenderHtml for Cow<'a, str> {
|
||||
escape: bool,
|
||||
mark_branches: bool,
|
||||
) {
|
||||
<&str as RenderHtml>::to_html_with_buf(
|
||||
<&str as RenderHtml<R>>::to_html_with_buf(
|
||||
&self,
|
||||
buf,
|
||||
position,
|
||||
@@ -484,7 +499,7 @@ impl<'a> RenderHtml for Cow<'a, str> {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
let this: &str = self.as_ref();
|
||||
@@ -510,20 +525,20 @@ impl<'a> ToTemplate for Cow<'a, str> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mountable for CowStrState<'a> {
|
||||
impl<'a, R: Renderer> Mountable<R> for CowStrState<'a, R> {
|
||||
fn unmount(&mut self) {
|
||||
self.node.unmount()
|
||||
}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
parent: &<R as Renderer>::Element,
|
||||
marker: Option<&<R as Renderer>::Node>,
|
||||
) {
|
||||
Rndr::insert_node(parent, self.node.as_ref(), marker);
|
||||
R::insert_node(parent, self.node.as_ref(), marker);
|
||||
}
|
||||
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, child: &mut dyn Mountable<R>) -> bool {
|
||||
self.node.insert_before_this(child)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,35 +2,44 @@ use super::{
|
||||
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
|
||||
RenderHtml, ToTemplate,
|
||||
};
|
||||
use crate::{html::attribute::Attribute, hydration::Cursor, renderer::Rndr};
|
||||
use crate::{
|
||||
html::attribute::Attribute, hydration::Cursor, renderer::DomRenderer,
|
||||
};
|
||||
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> {
|
||||
pub struct ViewTemplate<V, R> {
|
||||
view: V,
|
||||
rndr: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<V> ViewTemplate<V>
|
||||
impl<V, R> ViewTemplate<V, R>
|
||||
where
|
||||
V: Render + ToTemplate + 'static,
|
||||
V: Render<R> + ToTemplate + 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
/// Creates a new view template.
|
||||
pub fn new(view: V) -> Self {
|
||||
Self { view }
|
||||
Self {
|
||||
view,
|
||||
rndr: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_template() -> crate::renderer::types::TemplateElement {
|
||||
Rndr::get_template::<V>()
|
||||
fn to_template() -> R::TemplateElement {
|
||||
R::get_template::<V>()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Render for ViewTemplate<V>
|
||||
impl<V, R> Render<R> for ViewTemplate<V, R>
|
||||
where
|
||||
V: Render + RenderHtml + ToTemplate + 'static,
|
||||
V::State: Mountable,
|
||||
V: Render<R> + RenderHtml<R> + ToTemplate + 'static,
|
||||
V::State: Mountable<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = V::State;
|
||||
|
||||
@@ -38,7 +47,7 @@ where
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
let tpl = Self::to_template();
|
||||
let contents = Rndr::clone_template(&tpl);
|
||||
let contents = R::clone_template(&tpl);
|
||||
self.view
|
||||
.hydrate::<false>(&Cursor::new(contents), &Default::default())
|
||||
}
|
||||
@@ -48,28 +57,30 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> AddAnyAttr for ViewTemplate<V>
|
||||
impl<V, R> AddAnyAttr<R> for ViewTemplate<V, R>
|
||||
where
|
||||
V: RenderHtml + ToTemplate + 'static,
|
||||
V::State: Mountable,
|
||||
V: RenderHtml<R> + ToTemplate + 'static,
|
||||
V::State: Mountable<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> = ViewTemplate<V>;
|
||||
type Output<SomeNewAttr: Attribute<R>> = ViewTemplate<V, R>;
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<R>>(
|
||||
self,
|
||||
_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<R>,
|
||||
{
|
||||
panic!("AddAnyAttr not supported on ViewTemplate");
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> RenderHtml for ViewTemplate<V>
|
||||
impl<V, R> RenderHtml<R> for ViewTemplate<V, R>
|
||||
where
|
||||
V: RenderHtml + ToTemplate + 'static,
|
||||
V::State: Mountable,
|
||||
V: RenderHtml<R> + ToTemplate + 'static,
|
||||
V::State: Mountable<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type AsyncOutput = V::AsyncOutput;
|
||||
|
||||
@@ -88,7 +99,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
self.view.hydrate::<FROM_SERVER>(cursor, position)
|
||||
@@ -103,10 +114,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> ToTemplate for ViewTemplate<V>
|
||||
impl<V, R> ToTemplate for ViewTemplate<V, R>
|
||||
where
|
||||
V: RenderHtml + ToTemplate + 'static,
|
||||
V::State: Mountable,
|
||||
V: RenderHtml<R> + ToTemplate + 'static,
|
||||
V::State: Mountable<R>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
const TEMPLATE: &'static str = V::TEMPLATE;
|
||||
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
use super::{
|
||||
Mountable, Position, PositionState, Render, RenderHtml, ToTemplate,
|
||||
Mountable, Position, PositionState, Render, RenderHtml, Renderer,
|
||||
ToTemplate,
|
||||
};
|
||||
use crate::{
|
||||
html::attribute::Attribute,
|
||||
hydration::Cursor,
|
||||
renderer::Rndr,
|
||||
view::{add_attr::AddAnyAttr, StreamBuilder},
|
||||
};
|
||||
use const_str_slice_concat::{
|
||||
const_concat, const_concat_with_separator, str_from_buffer,
|
||||
};
|
||||
|
||||
impl Render for () {
|
||||
type State = crate::renderer::types::Placeholder;
|
||||
impl<R: Renderer> Render<R> for () {
|
||||
type State = R::Placeholder;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
Rndr::create_placeholder()
|
||||
R::create_placeholder()
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
}
|
||||
|
||||
impl RenderHtml for () {
|
||||
impl<R> RenderHtml<R> for ()
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = ();
|
||||
|
||||
const MIN_LENGTH: usize = 3;
|
||||
@@ -40,7 +43,7 @@ impl RenderHtml for () {
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
cursor.next_placeholder(position)
|
||||
@@ -51,30 +54,29 @@ impl RenderHtml for () {
|
||||
fn dry_resolve(&mut self) {}
|
||||
}
|
||||
|
||||
impl AddAnyAttr for () {
|
||||
type Output<SomeNewAttr: Attribute> = ();
|
||||
impl<Rndr> AddAnyAttr<Rndr> for ()
|
||||
where
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> = ();
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
_attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
().add_any_attr(_attr)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mountable for () {
|
||||
impl<R: Renderer> Mountable<R> for () {
|
||||
fn unmount(&mut self) {}
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
_parent: &crate::renderer::types::Element,
|
||||
_marker: Option<&crate::renderer::types::Node>,
|
||||
) {
|
||||
}
|
||||
fn mount(&mut self, _parent: &R::Element, _marker: Option<&R::Node>) {}
|
||||
|
||||
fn insert_before_this(&self, _child: &mut dyn Mountable) -> bool {
|
||||
fn insert_before_this(&self, _child: &mut dyn Mountable<R>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -93,7 +95,7 @@ impl ToTemplate for () {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Render> Render for (A,) {
|
||||
impl<A: Render<R>, R: Renderer> Render<R> for (A,) {
|
||||
type State = A::State;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
@@ -105,9 +107,10 @@ impl<A: Render> Render for (A,) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> RenderHtml for (A,)
|
||||
impl<A, R> RenderHtml<R> for (A,)
|
||||
where
|
||||
A: RenderHtml,
|
||||
A: RenderHtml<R>,
|
||||
R: Renderer,
|
||||
{
|
||||
type AsyncOutput = (A::AsyncOutput,);
|
||||
|
||||
@@ -147,7 +150,7 @@ where
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
self.0.hydrate::<FROM_SERVER>(cursor, position)
|
||||
@@ -178,18 +181,19 @@ impl<A: ToTemplate> ToTemplate for (A,) {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> AddAnyAttr for (A,)
|
||||
impl<A, Rndr> AddAnyAttr<Rndr> for (A,)
|
||||
where
|
||||
A: AddAnyAttr,
|
||||
A: AddAnyAttr<Rndr>,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> = (A::Output<SomeNewAttr>,);
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> = (A::Output<SomeNewAttr>,);
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
(self.0.add_any_attr(attr),)
|
||||
}
|
||||
@@ -197,11 +201,11 @@ where
|
||||
|
||||
macro_rules! impl_view_for_tuples {
|
||||
($first:ident, $($ty:ident),* $(,)?) => {
|
||||
impl<$first, $($ty),*> Render for ($first, $($ty,)*)
|
||||
impl<$first, $($ty),*, Rndr> Render<Rndr> for ($first, $($ty,)*)
|
||||
where
|
||||
$first: Render,
|
||||
$($ty: Render),*,
|
||||
|
||||
$first: Render<Rndr>,
|
||||
$($ty: Render<Rndr>),*,
|
||||
Rndr: Renderer
|
||||
{
|
||||
type State = ($first::State, $($ty::State,)*);
|
||||
|
||||
@@ -225,11 +229,11 @@ macro_rules! impl_view_for_tuples {
|
||||
}
|
||||
}
|
||||
|
||||
impl<$first, $($ty),*> RenderHtml for ($first, $($ty,)*)
|
||||
impl<$first, $($ty),*, Rndr> RenderHtml<Rndr> for ($first, $($ty,)*)
|
||||
where
|
||||
$first: RenderHtml,
|
||||
$($ty: RenderHtml),*,
|
||||
|
||||
$first: RenderHtml<Rndr>,
|
||||
$($ty: RenderHtml<Rndr>),*,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type AsyncOutput = ($first::AsyncOutput, $($ty::AsyncOutput,)*);
|
||||
|
||||
@@ -260,7 +264,7 @@ macro_rules! impl_view_for_tuples {
|
||||
$($ty.to_html_async_with_buf::<OUT_OF_ORDER>(buf, position, escape, mark_branches));*
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(self, cursor: &Cursor, position: &PositionState) -> Self::State {
|
||||
fn hydrate<const FROM_SERVER: bool>(self, cursor: &Cursor<Rndr>, position: &PositionState) -> Self::State {
|
||||
#[allow(non_snake_case)]
|
||||
let ($first, $($ty,)* ) = self;
|
||||
(
|
||||
@@ -307,10 +311,10 @@ macro_rules! impl_view_for_tuples {
|
||||
}
|
||||
}
|
||||
|
||||
impl<$first, $($ty),*> Mountable for ($first, $($ty,)*) where
|
||||
$first: Mountable,
|
||||
$($ty: Mountable),*,
|
||||
|
||||
impl<$first, $($ty),*, Rndr> Mountable<Rndr> for ($first, $($ty,)*) where
|
||||
$first: Mountable<Rndr>,
|
||||
$($ty: Mountable<Rndr>),*,
|
||||
Rndr: Renderer
|
||||
{
|
||||
fn unmount(&mut self) {
|
||||
#[allow(non_snake_case)] // better macro performance
|
||||
@@ -321,8 +325,8 @@ macro_rules! impl_view_for_tuples {
|
||||
|
||||
fn mount(
|
||||
&mut self,
|
||||
parent: &crate::renderer::types::Element,
|
||||
marker: Option<&crate::renderer::types::Node>,
|
||||
parent: &Rndr::Element,
|
||||
marker: Option<&Rndr::Node>,
|
||||
) {
|
||||
#[allow(non_snake_case)] // better macro performance
|
||||
let ($first, $($ty,)*) = self;
|
||||
@@ -331,7 +335,7 @@ macro_rules! impl_view_for_tuples {
|
||||
}
|
||||
|
||||
fn insert_before_this(&self,
|
||||
child: &mut dyn Mountable,
|
||||
child: &mut dyn Mountable<Rndr>,
|
||||
) -> bool {
|
||||
#[allow(non_snake_case)] // better macro performance
|
||||
let ($first, $($ty,)*) = self;
|
||||
@@ -340,20 +344,20 @@ macro_rules! impl_view_for_tuples {
|
||||
}
|
||||
}
|
||||
|
||||
impl<$first, $($ty,)*> AddAnyAttr for ($first, $($ty,)*)
|
||||
impl<$first, $($ty,)* Rndr> AddAnyAttr<Rndr> for ($first, $($ty,)*)
|
||||
where
|
||||
$first: AddAnyAttr,
|
||||
$($ty: AddAnyAttr),*,
|
||||
|
||||
$first: AddAnyAttr<Rndr>,
|
||||
$($ty: AddAnyAttr<Rndr>),*,
|
||||
Rndr: Renderer,
|
||||
{
|
||||
type Output<SomeNewAttr: Attribute> = ($first::Output<SomeNewAttr::Cloneable>, $($ty::Output<SomeNewAttr::Cloneable>,)*);
|
||||
type Output<SomeNewAttr: Attribute<Rndr>> = ($first::Output<SomeNewAttr::Cloneable>, $($ty::Output<SomeNewAttr::Cloneable>,)*);
|
||||
|
||||
fn add_any_attr<NewAttr: Attribute>(
|
||||
fn add_any_attr<NewAttr: Attribute<Rndr>>(
|
||||
self,
|
||||
attr: NewAttr,
|
||||
) -> Self::Output<NewAttr>
|
||||
where
|
||||
Self::Output<NewAttr>: RenderHtml,
|
||||
Self::Output<NewAttr>: RenderHtml<Rndr>,
|
||||
{
|
||||
let shared = attr.into_cloneable();
|
||||
#[allow(non_snake_case)] // better macro performance
|
||||
|
||||
Reference in New Issue
Block a user