Compare commits

...

1 Commits

Author SHA1 Message Date
Greg Johnston f0f7d900a1 revert PR #538 2023-02-18 17:27:00 -05:00
5 changed files with 289 additions and 195 deletions

View File

@ -4,55 +4,55 @@ use leptos::*;
#[component] #[component]
pub fn App(cx: Scope) -> impl IntoView { pub fn App(cx: Scope) -> impl IntoView {
let pending_thing = create_resource( let pending_thing = create_resource(
cx, cx,
|| false, || false,
|_| async { |_| async {
if cfg!(feature = "ssr") { if cfg!(feature = "ssr") {
let (tx, rx) = futures::channel::oneshot::channel(); let (tx, rx) = futures::channel::oneshot::channel();
spawn_local(async { spawn_local(async {
std::thread::sleep(std::time::Duration::from_millis(10)); std::thread::sleep(std::time::Duration::from_millis(10));
tx.send(()); tx.send(());
}); });
rx.await; rx.await;
} else { } else {
} }
true true
}, },
); );
view! { cx, view! { cx,
<div>
<div> <div>
<div> "This is some text"
"This is some text"
</div>
// <Suspense fallback=move || view! { cx, <p>"Loading..."</p> }>
{move || pending_thing.read().map(|n| view! { cx, <ComponentA/> })}
// </Suspense>
</div> </div>
} // <Suspense fallback=move || view! { cx, <p>"Loading..."</p> }>
{move || pending_thing.read().map(|n| view! { cx, <ComponentA/> })}
// </Suspense>
</div>
}
} }
#[component] #[component]
pub fn ComponentA(cx: Scope) -> impl IntoView { pub fn ComponentA(cx: Scope) -> impl IntoView {
let (value, set_value) = create_signal(cx, "Hello?".to_string()); let (value, set_value) = create_signal(cx, "Hello?".to_string());
let (counter, set_counter) = create_signal(cx, 0); let (counter, set_counter) = create_signal(cx, 0);
// Test to make sure hydration isn't broken by // Test to make sure hydration isn't broken by
// something like this // something like this
//let _ = [div(cx)].into_view(cx); //let _ = [div(cx)].into_view(cx);
div(cx) div(cx)
.id("the-div") .id("the-div")
.child( .child(
input(cx) input(cx)
.attr("type", "text") .attr("type", "text")
.prop("value", (cx, value)) .prop("value", (cx, value))
.on(ev::input, move |e| set_value(event_target_value(&e))), .on(ev::input, move |e| set_value(event_target_value(&e))),
) )
.child(input(cx).attr("type", "text").prop("value", value)) .child(input(cx).attr("type", "text").prop("value", value))
.child(p(cx).child("Value: ").child(value)) .child(p(cx).child("Value: ").child(value))
.into_view(cx) .into_view(cx)
} }
#[cfg(feature = "hydrate")] #[cfg(feature = "hydrate")]
@ -61,11 +61,11 @@ use wasm_bindgen::prelude::wasm_bindgen;
#[cfg(feature = "hydrate")] #[cfg(feature = "hydrate")]
#[wasm_bindgen] #[wasm_bindgen]
pub fn hydrate() { pub fn hydrate() {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
gloo::console::debug!("starting WASM"); gloo::console::debug!("starting WASM");
leptos::mount_to_body(move |cx| { leptos::mount_to_body(move |cx| {
view! { cx, <App/> } view! { cx, <App/> }
}); });
} }

View File

@ -9,7 +9,6 @@ gloo = { version = "0.8", features = ["futures"] }
leptos = { path = "../../../leptos", features = ["tracing"] } leptos = { path = "../../../leptos", features = ["tracing"] }
tracing = "0.1" tracing = "0.1"
tracing-subscriber = "0.3" tracing-subscriber = "0.3"
tracing-subscriber-wasm = "0.1"
wasm-bindgen-futures = "0.4" wasm-bindgen-futures = "0.4"
web-sys = "0.3" web-sys = "0.3"

View File

@ -1,42 +1,102 @@
#![allow(warnings)]
#[macro_use] #[macro_use]
extern crate tracing; extern crate tracing;
mod utils;
use leptos::*; use leptos::*;
use tracing_subscriber::prelude::*; use tracing::field::debug;
use tracing_subscriber::util::SubscriberInitExt;
fn main() { fn main() {
tracing_subscriber::fmt() console_error_panic_hook::set_once();
.with_writer(tracing_subscriber_wasm::MakeConsoleWriter::default())
.without_time()
.with_max_level(tracing::Level::TRACE)
.pretty()
.with_target(false)
.init();
mount_to_body(app); tracing_subscriber::fmt()
.with_max_level(tracing::Level::TRACE)
.without_time()
.with_file(true)
.with_line_number(true)
.with_target(false)
.with_writer(utils::MakeConsoleWriter)
.with_ansi(false)
.pretty()
.finish()
.init();
mount_to_body(view_fn);
} }
#[instrument] fn view_fn(cx: Scope) -> impl IntoView {
fn app(cx: Scope) -> impl IntoView { let view = view! { cx,
let (data, set_data) = create_signal(cx, vec![1, 3, 5]); <For
each=|| vec![0, 1, 2, 3, 4, 5, 6, 7]
key=|i| *i
view=|cx, i| view! { cx, {i} }
/>
}
.into_view(cx);
let handle_change = move |_| { let (a, set_a) = create_signal(cx, view.clone());
set_data.update(|data| { let (b, set_b) = create_signal(cx, view);
if [1, 3, 5] == data[..] {
*data = vec![0, 1, 2, 3, 4, 5, 6];
} else {
*data = vec![1, 3, 5];
}
})
};
view! { cx, let (is_a, set_is_a) = create_signal(cx, true);
<button on:click=handle_change>"Reverse"</button>
<For let handle_toggle = move |_| {
each=data trace!("toggling");
key=|item| *item if is_a() {
view=|cx, i| view! { cx, <h3>{i}</h3> } set_b(a());
/>
set_is_a(false);
} else {
set_a(a());
set_is_a(true);
} }
};
let a_tag = view! { cx, <svg::a/> };
view! { cx,
<>
<div>
<button on:click=handle_toggle>"Toggle"</button>
</div>
<svg>{a_tag}</svg>
<Example/>
<A child=Signal::from(a) />
<A child=Signal::from(b) />
</>
}
}
#[component]
fn A(cx: Scope, child: Signal<View>) -> impl IntoView {
move || child()
}
#[component]
fn Example(cx: Scope) -> impl IntoView {
trace!("rendering <Example/>");
let (value, set_value) = create_signal(cx, 10);
let memo = create_memo(cx, move |_| value() * 2);
let derived = Signal::derive(cx, move || value() * 3);
create_effect(cx, move |_| {
trace!("logging value of derived..., {}", derived.get());
});
set_timeout(
move || set_value.update(|v| *v += 1),
std::time::Duration::from_millis(50),
);
view! { cx,
<h1>"Example"</h1>
<button on:click=move |_| set_value.update(|value| *value += 1)>
"Click me"
</button>
}
} }

View File

@ -0,0 +1,47 @@
pub struct MakeConsoleWriter;
use std::io::{self, Write};
use tracing_subscriber::fmt::MakeWriter;
impl<'a> MakeWriter<'a> for MakeConsoleWriter {
type Writer = ConsoleWriter;
fn make_writer(&'a self) -> Self::Writer {
unimplemented!("use make_writer_for instead");
}
fn make_writer_for(&'a self, meta: &tracing::Metadata<'_>) -> Self::Writer {
ConsoleWriter(*meta.level(), Vec::with_capacity(256))
}
}
pub struct ConsoleWriter(tracing::Level, Vec<u8>);
impl io::Write for ConsoleWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.1.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
use gloo::console;
use tracing::Level;
let data = String::from_utf8(self.1.to_owned())
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "data not UTF-8"))?;
match self.0 {
Level::TRACE => console::debug!(&data),
Level::DEBUG => console::debug!(&data),
Level::INFO => console::log!(&data),
Level::WARN => console::warn!(&data),
Level::ERROR => console::error!(&data),
}
Ok(())
}
}
impl Drop for ConsoleWriter {
fn drop(&mut self) {
let _ = self.flush();
}
}

View File

@ -347,72 +347,60 @@ where
let (children, closing) = let (children, closing) =
(component.children.clone(), component.closing.node.clone()); (component.children.clone(), component.closing.node.clone());
#[cfg(all(target_arch = "wasm32", feature = "web"))] cfg_if::cfg_if! {
{ if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
create_effect(cx, move |prev_hash_run| { create_effect(cx, move |prev_hash_run| {
let mut children_borrow = children.borrow_mut(); let mut children_borrow = children.borrow_mut();
let opening = if let Some(Some(child)) = children_borrow.get(0) #[cfg(all(target_arch = "wasm32", feature = "web"))]
{ let opening = if let Some(Some(child)) = children_borrow.get(0) {
child.get_opening_node() child.get_opening_node()
} else { } else {
closing.clone() closing.clone()
}; };
let items = items_fn(); let items = items_fn();
let items = items.into_iter().collect::<SmallVec<[_; 128]>>(); let items = items.into_iter().collect::<SmallVec<[_; 128]>>();
let hashed_items = let hashed_items =
items.iter().map(&key_fn).collect::<FxIndexSet<_>>(); items.iter().map(&key_fn).collect::<FxIndexSet<_>>();
if let Some(HashRun(prev_hash_run)) = prev_hash_run { if let Some(HashRun(prev_hash_run)) = prev_hash_run {
let cmds = diff(&prev_hash_run, &hashed_items); let cmds = diff(&prev_hash_run, &hashed_items);
tracing::debug!("cmds:\n{cmds:#?}"); apply_cmds(
cx,
#[cfg(all(target_arch = "wasm32", feature = "web"))]
&opening,
#[cfg(all(target_arch = "wasm32", feature = "web"))]
&closing,
cmds,
&mut children_borrow,
items.into_iter().map(|t| Some(t)).collect(),
&each_fn
);
} else {
*children_borrow = Vec::with_capacity(items.len());
apply_cmds( for item in items {
cx, let (each_item, _) = cx.run_child_scope(|cx| EachItem::new(cx, each_fn(cx, item).into_view(cx)));
&opening,
&closing,
cmds,
&mut children_borrow,
items.into_iter().map(|t| Some(t)).collect(),
&each_fn,
);
} else {
children_borrow.clear();
children_borrow.reserve(items.len());
for item in items { #[cfg(all(target_arch = "wasm32", feature = "web"))]
let (each_item, disposer) = cx.run_child_scope(|cx| { mount_child(MountKind::Before(&closing), &each_item);
EachItem::new(cx, each_fn(cx, item).into_view(cx))
});
mount_child(MountKind::Before(&closing), &each_item); children_borrow.push(Some(each_item));
children_borrow.push(Some(each_item));
}
} }
}
HashRun(hashed_items) HashRun(hashed_items)
}); });
} } else {
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
{
*component.children.borrow_mut() = (items_fn)() *component.children.borrow_mut() = (items_fn)()
.into_iter() .into_iter()
.map(|child| { .map(|child| cx.run_child_scope(|cx| Some(EachItem::new(cx, (each_fn)(cx, child).into_view(cx)))).0)
cx.run_child_scope(|cx| { .collect();
Some(EachItem::new( }
cx,
(each_fn)(cx, child).into_view(cx),
))
})
.0
})
.collect();
} }
View::CoreComponent(CoreComponent::Each(component)) View::CoreComponent(CoreComponent::Each(component))
@ -423,7 +411,7 @@ where
#[educe(Debug)] #[educe(Debug)]
struct HashRun<T>(#[educe(Debug(ignore))] T); struct HashRun<T>(#[educe(Debug(ignore))] T);
/// Calculates the operations needed to get from `a` to `b`. /// Calculates the operations need to get from `a` to `b`.
#[cfg(all(target_arch = "wasm32", feature = "web"))] #[cfg(all(target_arch = "wasm32", feature = "web"))]
fn diff<K: Eq + Hash>(from: &FxIndexSet<K>, to: &FxIndexSet<K>) -> Diff { fn diff<K: Eq + Hash>(from: &FxIndexSet<K>, to: &FxIndexSet<K>) -> Diff {
if from.is_empty() && to.is_empty() { if from.is_empty() && to.is_empty() {
@ -436,73 +424,72 @@ fn diff<K: Eq + Hash>(from: &FxIndexSet<K>, to: &FxIndexSet<K>) -> Diff {
} }
// Get removed items // Get removed items
let mut removed = let mut removed = from.difference(to);
from.difference(to).map(|k| from.get_index_of(k).unwrap());
let removed_cmds = removed.clone().map(|idx| DiffOpRemove { at: idx }); let removed_cmds = removed
.clone()
.map(|k| from.get_full(k).unwrap().0)
.map(|idx| DiffOpRemove { at: idx });
// Get added items // Get added items
let mut added = to.difference(from).map(|k| to.get_index_of(k).unwrap()); let mut added = to.difference(from);
let added_cmds = added.clone().map(|idx| DiffOpAdd { let added_cmds =
at: idx, added
mode: Default::default(), .clone()
}); .map(|k| to.get_full(k).unwrap().0)
.map(|idx| DiffOpAdd {
at: idx,
mode: Default::default(),
});
let mut normalized_idx = 0i64; // Get moved items
let mut next_added_idx = added.next(); let mut normalized_idx = 0;
let mut next_removed_idx = removed.next(); let mut move_cmds = SmallVec::<[_; 8]>::with_capacity(to.len());
let mut added_idx = added.next().map(|k| to.get_full(k).unwrap().0);
let mut removed_idx = removed.next().map(|k| from.get_full(k).unwrap().0);
let move_cmds = to for (idx, k) in to.iter().enumerate() {
.iter() if let Some(added_idx) = added_idx.as_mut().filter(|r_i| **r_i == idx) {
.enumerate() if let Some(next_added) =
.filter_map(|(i, k)| { added.next().map(|k| to.get_full(k).unwrap().0)
let is_added = if let Some(idx) = next_added_idx { {
if i == idx { *added_idx = next_added;
next_added_idx = added.next();
normalized_idx -= 1;
true normalized_idx = usize::wrapping_sub(normalized_idx, 1);
} else {
false
}
} else {
false
};
let is_removed = if let Some(idx) = next_removed_idx {
if i == idx {
next_removed_idx = removed.next();
normalized_idx += 1;
true
} else {
false
}
} else {
false
};
normalized_idx += 1;
if !is_added && !is_removed {
Some((
from.get_index_of(k).unwrap(),
i,
// We need to `-1` because otherwise, we'd be accounting for
// the NEXT iteration, not this current one
normalized_idx - 1,
))
} else {
None
} }
}) }
.map(|(from, to, normalized_idx)| DiffOpMove {
from, if let Some(removed_idx) =
to, removed_idx.as_mut().filter(|r_i| **r_i == idx)
move_in_dom: to != normalized_idx as usize, {
}) normalized_idx = normalized_idx.wrapping_add(1);
.collect();
if let Some(next_removed) =
removed.next().map(|k| from.get_full(k).unwrap().0)
{
*removed_idx = next_removed;
}
}
if let Some((from_idx, _)) = from.get_full(k) {
if from_idx != normalized_idx {
move_cmds.push(DiffOpMove {
from: from_idx,
to: idx,
move_in_dom: true,
});
} else if from_idx != idx {
move_cmds.push(DiffOpMove {
from: from_idx,
to: idx,
move_in_dom: false,
});
}
}
normalized_idx = normalized_idx.wrapping_add(1);
}
let mut diffs = Diff { let mut diffs = Diff {
removed: removed_cmds.collect(), removed: removed_cmds.collect(),
@ -634,16 +621,17 @@ fn apply_cmds<T, EF, N>(
if opening.previous_sibling().is_none() if opening.previous_sibling().is_none()
&& closing.next_sibling().is_none() && closing.next_sibling().is_none()
{ {
if let Some(parent) = closing let parent = closing
.parent_node() .parent_node()
.map(JsCast::unchecked_into::<web_sys::Element>) .expect("could not get closing node")
{ .unchecked_into::<web_sys::Element>();
#[cfg(debug_assertions)] parent.set_text_content(Some(""));
parent.append_with_node_2(opening, closing).unwrap();
#[cfg(not(debug_assertions))] #[cfg(debug_assertions)]
parent.append_with_node_1(closing).unwrap(); parent.append_with_node_2(opening, closing).unwrap();
}
#[cfg(not(debug_assertions))]
parent.append_with_node_1(closing).unwrap();
} else { } else {
range.set_start_before(opening).unwrap(); range.set_start_before(opening).unwrap();
range.set_end_before(closing).unwrap(); range.set_end_before(closing).unwrap();
@ -676,7 +664,7 @@ fn apply_cmds<T, EF, N>(
for DiffOpAdd { at, mode } in cmds.added { for DiffOpAdd { at, mode } in cmds.added {
let item = items[at].take().unwrap(); let item = items[at].take().unwrap();
let (each_item, disposer) = cx.run_child_scope(|cx| { let (each_item, _) = cx.run_child_scope(|cx| {
let child = each_fn(cx, item).into_view(cx); let child = each_fn(cx, item).into_view(cx);
EachItem::new(cx, child) EachItem::new(cx, child)
}); });