Merge branch 'leptos_dom_v2' of https://github.com/jquesada2016/leptos into leptos_dom_v2

This commit is contained in:
Greg Johnston 2022-12-10 14:09:43 -05:00
commit 4a9f906571
12 changed files with 122 additions and 56 deletions

View File

@ -12,6 +12,7 @@ pub use fragment::*;
use leptos_reactive::Scope;
use std::borrow::Cow;
pub use unit::*;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use wasm_bindgen::JsCast;
/// The core foundational leptos components.

View File

@ -1,8 +1,11 @@
use crate::{hydration::HydrationCtx, Comment, IntoView, View};
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use crate::{mount_child, MountKind, Mountable};
use leptos_reactive::{create_effect, Scope, ScopeDisposer};
use leptos_reactive::Scope;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use leptos_reactive::{create_effect, ScopeDisposer};
use std::{borrow::Cow, cell::RefCell, rc::Rc};
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use wasm_bindgen::JsCast;
/// The internal representation of the [`DynChild`] core-component.

View File

@ -1,18 +1,20 @@
use crate::{hydration::HydrationCtx, Comment, CoreComponent, IntoView, View};
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use crate::{mount_child, MountKind, Mountable, RANGE};
use leptos_reactive::{create_effect, Scope};
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use leptos_reactive::create_effect;
use leptos_reactive::Scope;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use rustc_hash::FxHasher;
use smallvec::SmallVec;
use std::{
borrow::Cow,
cell::RefCell,
hash::{BuildHasherDefault, Hash},
rc::Rc,
};
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use std::hash::BuildHasherDefault;
use std::{borrow::Cow, cell::RefCell, hash::Hash, rc::Rc};
use typed_builder::TypedBuilder;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use wasm_bindgen::JsCast;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
type FxIndexSet<T> = indexmap::IndexSet<T, BuildHasherDefault<FxHasher>>;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
@ -269,12 +271,14 @@ where
key_fn,
} = self;
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
let _ = key_fn;
let component = EachRepr::default();
let children = component.children.clone();
#[cfg(all(target_arch = "wasm32", feature = "web"))]
let closing = component.closing.node.clone();
let (children, closing) =
(component.children.clone(), component.closing.node.clone());
cfg_if::cfg_if! {
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
@ -341,6 +345,7 @@ where
struct HashRun<T>(#[educe(Debug(ignore))] T);
/// Calculates the operations need to get from `a` to `b`.
#[cfg(all(target_arch = "wasm32", feature = "web"))]
fn diff<K: Eq + Hash>(from: &FxIndexSet<K>, to: &FxIndexSet<K>) -> Diff {
if from.is_empty() && to.is_empty() {
return Diff::default();
@ -428,6 +433,7 @@ fn diff<K: Eq + Hash>(from: &FxIndexSet<K>, to: &FxIndexSet<K>) -> Diff {
diffs
}
#[cfg(all(target_arch = "wasm32", feature = "web"))]
fn apply_opts<K: Eq + Hash>(
from: &FxIndexSet<K>,
to: &FxIndexSet<K>,
@ -463,6 +469,7 @@ fn apply_opts<K: Eq + Hash>(
}
#[derive(Debug, Default)]
#[allow(unused)]
struct Diff {
removed: SmallVec<[DiffOpRemove; 8]>,
moved: SmallVec<[DiffOpMove; 8]>,
@ -471,6 +478,7 @@ struct Diff {
}
#[derive(Debug)]
#[allow(unused)]
struct DiffOpMove {
from: usize,
to: usize,
@ -478,17 +486,20 @@ struct DiffOpMove {
}
#[derive(Debug)]
#[allow(unused)]
struct DiffOpAdd {
at: usize,
mode: DiffOpAddMode,
}
#[derive(Debug)]
#[allow(unused)]
struct DiffOpRemove {
at: usize,
}
#[derive(Default, Debug)]
#[allow(unused)]
enum DiffOpAddMode {
#[default]
Normal,

View File

@ -1,6 +1,7 @@
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use crate::Mountable;
use crate::{hydration::HydrationCtx, Comment, CoreComponent, IntoView, View};
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use wasm_bindgen::JsCast;
/// The internal representation of the [`Unit`] core-component.

View File

@ -1,9 +1,11 @@
pub mod typed;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use crate::window;
use std::{borrow::Cow, cell::RefCell, collections::HashSet};
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use wasm_bindgen::{
closure::Closure, convert::FromWasmAbi, intern, JsCast, JsValue,
convert::FromWasmAbi, intern, prelude::Closure, JsCast, JsValue,
UnwrapThrowExt,
};
@ -12,6 +14,7 @@ thread_local! {
}
/// Adds an event listener to the `Window`.
#[cfg(all(target_arch = "wasm32", feature = "web"))]
pub fn window_event_listener(
event_name: &str,
cb: impl Fn(web_sys::Event) + 'static,
@ -23,6 +26,7 @@ pub fn window_event_listener(
}
/// Adds an event listener to the target DOM element using implicit event delegation.
#[cfg(all(target_arch = "wasm32", feature = "web"))]
pub fn add_event_listener<E>(
target: &web_sys::Element,
event_name: Cow<'static, str>,
@ -37,6 +41,7 @@ pub fn add_event_listener<E>(
}
#[doc(hidden)]
#[cfg(all(target_arch = "wasm32", feature = "web"))]
pub fn add_event_listener_undelegated<E>(
target: &web_sys::Element,
event_name: &str,
@ -50,6 +55,7 @@ pub fn add_event_listener_undelegated<E>(
}
// cf eventHandler in ryansolid/dom-expressions
#[cfg(all(target_arch = "wasm32", feature = "web"))]
pub(crate) fn add_delegated_event_listener(event_name: Cow<'static, str>) {
GLOBAL_EVENTS.with(|global_events| {
let mut events = global_events.borrow_mut();
@ -113,6 +119,7 @@ pub(crate) fn add_delegated_event_listener(event_name: Cow<'static, str>) {
})
}
#[cfg(all(target_arch = "wasm32", feature = "web"))]
pub(crate) fn event_delegation_key(event_name: &str) -> String {
let event_name = intern(event_name);
let mut n = String::from("$$$");

View File

@ -1,11 +1,16 @@
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use crate::events::*;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use crate::macro_helpers::Property;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use crate::macro_helpers::{
attribute_expression, class_expression, property_expression,
};
use crate::{
ev::EventDescriptor,
hydration::HydrationCtx,
macro_helpers::{
attribute_expression, class_expression, property_expression, Attribute,
Class, IntoAttribute, IntoChild, IntoClass, IntoProperty, Property,
Attribute, Class, IntoAttribute, IntoChild, IntoClass, IntoProperty,
},
Element, Fragment, IntoView, NodeRef, Text, View,
};
@ -15,10 +20,15 @@ use crate::{mount_child, MountKind};
use std::cell::OnceCell;
use cfg_if::cfg_if;
use leptos_reactive::{create_render_effect, Scope};
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use leptos_reactive::create_render_effect;
use leptos_reactive::Scope;
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
use smallvec::{smallvec, SmallVec};
use std::{borrow::Cow, cell::LazyCell, fmt, ops::Deref};
use std::{borrow::Cow, fmt};
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use std::{cell::LazyCell, ops::Deref};
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use wasm_bindgen::JsCast;
/// Trait alias for the trait bounts on [`IntoElement`].
@ -61,14 +71,45 @@ pub trait IntoElement: IntoElementBounds {
fn hydration_id(&self) -> usize;
}
/// Trait for converting [`web_sys::Element`] to [`HtmlElement`].
pub trait ToHtmlElement {
/// Converts the type to [`HtmlElement`].
fn to_leptos_element(self, cx: Scope) -> HtmlElement<AnyElement>;
}
impl<T> ToHtmlElement for T
where
T: AsRef<web_sys::Element>,
{
fn to_leptos_element(self, cx: Scope) -> HtmlElement<AnyElement> {
#[cfg(all(target_arch = "wasm32", feature = "web"))]
{
let el = self.as_ref().clone().unchecked_into();
let element = AnyElement {
name: "".into(),
is_void: false,
element: el,
};
HtmlElement { cx, element }
}
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
{
let _ = cx;
unreachable!();
}
}
}
/// Represents potentially any element.
#[derive(Clone, Debug)]
#[cfg_attr(all(target_arch = "wasm32", feature = "web"), derive(educe::Educe))]
#[cfg_attr(all(target_arch = "wasm32", feature = "web"), educe(Deref))]
pub struct AnyElement {
name: Cow<'static, str>,
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
dynamic: bool,
#[cfg(all(target_arch = "wasm32", feature = "web"))]
#[educe(Deref)]
element: web_sys::HtmlElement,
@ -151,7 +192,6 @@ cfg_if! {
pub(crate) cx: Scope,
pub(crate) element: El,
pub(crate) id: OnceCell<Cow<'static, str>>,
pub(crate) dynamic: bool,
#[educe(Debug(ignore))]
pub(crate) attrs: SmallVec<[(Cow<'static, str>, Cow<'static, str>); 4]>,
#[educe(Debug(ignore))]
@ -173,7 +213,6 @@ impl<El: IntoElement> HtmlElement<El> {
Self {
cx,
id: Default::default(),
dynamic: false,
attrs: smallvec![],
children: smallvec![],
element,
@ -203,7 +242,6 @@ impl<El: IntoElement> HtmlElement<El> {
let Self {
cx,
id,
dynamic,
attrs,
children,
element,
@ -212,12 +250,10 @@ impl<El: IntoElement> HtmlElement<El> {
HtmlElement {
cx,
id,
dynamic,
attrs,
children,
element: AnyElement {
name: element.name(),
dynamic,
is_void: element.is_void(),
id: element.hydration_id(),
},
@ -257,6 +293,9 @@ impl<El: IntoElement> HtmlElement<El> {
#[cfg(all(target_arch = "wasm32", feature = "web"))]
node_ref.load(&self);
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
let _ = node_ref;
self
}
@ -295,7 +334,6 @@ impl<El: IntoElement> HtmlElement<El> {
let mut attr = attr.into_attribute(this.cx);
while let Attribute::Fn(_, f) = attr {
this.dynamic = true;
attr = f();
}
match attr {
@ -357,10 +395,7 @@ impl<El: IntoElement> HtmlElement<El> {
let include = match class {
Class::Value(include) => include,
Class::Fn(_, f) => {
this.dynamic = true;
f()
}
Class::Fn(_, f) => f(),
};
if include {
@ -408,17 +443,15 @@ impl<El: IntoElement> HtmlElement<El> {
property_expression(el, prop_name, value)
}
};
self
}
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
{
let mut this = self;
this.dynamic = true;
this
let _ = name;
let _ = value;
}
self
}
/// Adds an event listener to this element.
@ -451,13 +484,10 @@ impl<El: IntoElement> HtmlElement<El> {
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
{
let mut this = self;
this.dynamic = true;
_ = event;
_ = event_handler;
this
self
}
}
@ -499,7 +529,6 @@ impl<El: IntoElement> IntoView for HtmlElement<El> {
element,
mut attrs,
children,
dynamic,
..
} = self;
@ -514,7 +543,6 @@ impl<El: IntoElement> IntoView for HtmlElement<El> {
attrs.push(("id".into(), format!("_{}", id).into()));
}
element.dynamic = dynamic;
element.attrs = attrs;
element.children.extend(children);
@ -572,6 +600,7 @@ macro_rules! generate_html_tags {
),* $(,)?) => {
paste::paste! {
$(
#[cfg(all(target_arch = "wasm32", feature = "web"))]
#[thread_local]
static [<$tag:upper>]: LazyCell<web_sys::HtmlElement> = LazyCell::new(|| {
crate::document()

View File

@ -1,8 +1,10 @@
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use std::cell::LazyCell;
/// We can tell if we start in hydration mode by checking to see if the
/// id "_0" is present in the DOM. If it is, we know we are hydrating from
/// the server, if not, we are starting off in CSR
#[cfg(all(target_arch = "wasm32", feature = "web"))]
#[thread_local]
static mut IS_HYDRATING: LazyCell<bool> = LazyCell::new(|| {
#[cfg(debug_assertions)]
@ -35,12 +37,14 @@ impl HydrationCtx {
unsafe { ID = 0 };
}
#[cfg(all(target_arch = "wasm32", feature = "web"))]
pub(crate) fn stop_hydrating() {
unsafe {
std::mem::take(&mut IS_HYDRATING);
}
}
#[cfg(all(target_arch = "wasm32", feature = "web"))]
pub(crate) fn is_hydrating() -> bool {
unsafe { *IS_HYDRATING }
}

View File

@ -29,11 +29,16 @@ pub use logging::*;
pub use node_ref::*;
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
use smallvec::SmallVec;
use std::{borrow::Cow, cell::LazyCell, fmt};
use std::borrow::Cow;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use std::{cell::LazyCell, fmt};
pub use wasm_bindgen;
use wasm_bindgen::{JsCast, UnwrapThrowExt};
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use wasm_bindgen::JsCast;
use wasm_bindgen::UnwrapThrowExt;
pub use web_sys;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
#[thread_local]
static COMMENT: LazyCell<web_sys::Node> =
LazyCell::new(|| document().create_comment("").unchecked_into());
@ -128,7 +133,6 @@ cfg_if! {
pub struct Element {
name: Cow<'static, str>,
is_void: bool,
dynamic: bool,
attrs: SmallVec<[(Cow<'static, str>, Cow<'static, str>); 4]>,
children: Vec<View>,
id: usize,
@ -158,7 +162,6 @@ impl Element {
Self {
name: el.name(),
is_void: el.is_void(),
dynamic: false,
attrs: Default::default(),
children: Default::default(),
id: el.hydration_id(),
@ -183,6 +186,12 @@ impl Comment {
) -> Self {
let content = content.into();
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
{
let _ = id;
let _ = closing;
}
#[cfg(all(target_arch = "wasm32", feature = "web"))]
let node = COMMENT.clone_node().unwrap();
@ -339,6 +348,7 @@ impl View {
}
}
#[cfg(all(target_arch = "wasm32", feature = "web"))]
fn get_text(&self) -> Option<&Text> {
if let Self::Text(t) = self {
Some(t)

View File

@ -1,6 +1,7 @@
use std::rc::Rc;
use leptos_reactive::Scope;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use wasm_bindgen::UnwrapThrowExt;
/// Represents the different possible values an attribute node could have.
@ -146,6 +147,7 @@ attr_type!(f32);
attr_type!(f64);
attr_type!(char);
#[cfg(all(target_arch = "wasm32", feature = "web"))]
pub fn attribute_expression(
el: &web_sys::Element,
attr_name: &str,

View File

@ -1,4 +1,5 @@
use leptos_reactive::Scope;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use wasm_bindgen::UnwrapThrowExt;
/// Represents the different possible values a single class on an element could have,
@ -65,6 +66,7 @@ impl<T: IntoClass> IntoClass for (Scope, T) {
}
}
#[cfg(all(target_arch = "wasm32", feature = "web"))]
pub fn class_expression(
class_list: &web_sys::DomTokenList,
class_name: &str,

View File

@ -1,5 +1,7 @@
use leptos_reactive::Scope;
use wasm_bindgen::{JsValue, UnwrapThrowExt};
use wasm_bindgen::JsValue;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use wasm_bindgen::UnwrapThrowExt;
/// Represents the different possible values an element property could have,
/// allowing you to do fine-grained updates to single fields.
@ -74,6 +76,7 @@ prop_type!(f32);
prop_type!(f64);
prop_type!(bool);
#[cfg(all(target_arch = "wasm32", feature = "web"))]
pub fn property_expression(
el: &web_sys::Element,
prop_name: &str,

View File

@ -3,7 +3,7 @@
use crate::{CoreComponent, HydrationCtx, View};
use cfg_if::cfg_if;
use itertools::Itertools;
use std::{borrow::Cow, fmt::Display};
use std::borrow::Cow;
impl View {
/// Consumes the node and renders it into an HTML string.
@ -123,17 +123,14 @@ impl View {
}
View::Element(el) => {
let tag_name = el.name;
let mut has_id = false;
let mut attrs = el
let attrs = el
.attrs
.into_iter()
.map(|(name, value)| -> Cow<'static, str> {
if value.is_empty() {
format!(" {name}").into()
} else {
if name == "id" {
has_id = true;
}
format!(
" {name}=\"{}\"",
html_escape::encode_double_quoted_attribute(&value)
@ -143,10 +140,6 @@ impl View {
})
.join("");
if !has_id && el.dynamic {
attrs.push_str(&format!(" id=\"_{}\"", el.id));
}
if el.is_void {
format!("<{tag_name}{attrs}/>").into()
} else {