Compare commits
1 Commits
main
...
revert-538
Author | SHA1 | Date |
---|---|---|
Greg Johnston | f0f7d900a1 |
|
@ -4,55 +4,55 @@ use leptos::*;
|
|||
|
||||
#[component]
|
||||
pub fn App(cx: Scope) -> impl IntoView {
|
||||
let pending_thing = create_resource(
|
||||
cx,
|
||||
|| false,
|
||||
|_| async {
|
||||
if cfg!(feature = "ssr") {
|
||||
let (tx, rx) = futures::channel::oneshot::channel();
|
||||
spawn_local(async {
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
tx.send(());
|
||||
});
|
||||
rx.await;
|
||||
} else {
|
||||
}
|
||||
true
|
||||
},
|
||||
);
|
||||
let pending_thing = create_resource(
|
||||
cx,
|
||||
|| false,
|
||||
|_| async {
|
||||
if cfg!(feature = "ssr") {
|
||||
let (tx, rx) = futures::channel::oneshot::channel();
|
||||
spawn_local(async {
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
tx.send(());
|
||||
});
|
||||
rx.await;
|
||||
} else {
|
||||
}
|
||||
true
|
||||
},
|
||||
);
|
||||
|
||||
view! { cx,
|
||||
view! { cx,
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
"This is some text"
|
||||
</div>
|
||||
// <Suspense fallback=move || view! { cx, <p>"Loading..."</p> }>
|
||||
{move || pending_thing.read().map(|n| view! { cx, <ComponentA/> })}
|
||||
// </Suspense>
|
||||
"This is some text"
|
||||
</div>
|
||||
}
|
||||
// <Suspense fallback=move || view! { cx, <p>"Loading..."</p> }>
|
||||
{move || pending_thing.read().map(|n| view! { cx, <ComponentA/> })}
|
||||
// </Suspense>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn ComponentA(cx: Scope) -> impl IntoView {
|
||||
let (value, set_value) = create_signal(cx, "Hello?".to_string());
|
||||
let (counter, set_counter) = create_signal(cx, 0);
|
||||
let (value, set_value) = create_signal(cx, "Hello?".to_string());
|
||||
let (counter, set_counter) = create_signal(cx, 0);
|
||||
|
||||
// Test to make sure hydration isn't broken by
|
||||
// something like this
|
||||
//let _ = [div(cx)].into_view(cx);
|
||||
// Test to make sure hydration isn't broken by
|
||||
// something like this
|
||||
//let _ = [div(cx)].into_view(cx);
|
||||
|
||||
div(cx)
|
||||
.id("the-div")
|
||||
.child(
|
||||
input(cx)
|
||||
.attr("type", "text")
|
||||
.prop("value", (cx, value))
|
||||
.on(ev::input, move |e| set_value(event_target_value(&e))),
|
||||
)
|
||||
.child(input(cx).attr("type", "text").prop("value", value))
|
||||
.child(p(cx).child("Value: ").child(value))
|
||||
.into_view(cx)
|
||||
div(cx)
|
||||
.id("the-div")
|
||||
.child(
|
||||
input(cx)
|
||||
.attr("type", "text")
|
||||
.prop("value", (cx, value))
|
||||
.on(ev::input, move |e| set_value(event_target_value(&e))),
|
||||
)
|
||||
.child(input(cx).attr("type", "text").prop("value", value))
|
||||
.child(p(cx).child("Value: ").child(value))
|
||||
.into_view(cx)
|
||||
}
|
||||
|
||||
#[cfg(feature = "hydrate")]
|
||||
|
@ -61,11 +61,11 @@ use wasm_bindgen::prelude::wasm_bindgen;
|
|||
#[cfg(feature = "hydrate")]
|
||||
#[wasm_bindgen]
|
||||
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| {
|
||||
view! { cx, <App/> }
|
||||
});
|
||||
leptos::mount_to_body(move |cx| {
|
||||
view! { cx, <App/> }
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ gloo = { version = "0.8", features = ["futures"] }
|
|||
leptos = { path = "../../../leptos", features = ["tracing"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
tracing-subscriber-wasm = "0.1"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
web-sys = "0.3"
|
||||
|
||||
|
|
|
@ -1,42 +1,102 @@
|
|||
#![allow(warnings)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
||||
mod utils;
|
||||
|
||||
use leptos::*;
|
||||
use tracing_subscriber::prelude::*;
|
||||
use tracing::field::debug;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
|
||||
fn main() {
|
||||
tracing_subscriber::fmt()
|
||||
.with_writer(tracing_subscriber_wasm::MakeConsoleWriter::default())
|
||||
.without_time()
|
||||
.with_max_level(tracing::Level::TRACE)
|
||||
.pretty()
|
||||
.with_target(false)
|
||||
.init();
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
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 app(cx: Scope) -> impl IntoView {
|
||||
let (data, set_data) = create_signal(cx, vec![1, 3, 5]);
|
||||
fn view_fn(cx: Scope) -> impl IntoView {
|
||||
let view = view! { cx,
|
||||
<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 |_| {
|
||||
set_data.update(|data| {
|
||||
if [1, 3, 5] == data[..] {
|
||||
*data = vec![0, 1, 2, 3, 4, 5, 6];
|
||||
} else {
|
||||
*data = vec![1, 3, 5];
|
||||
}
|
||||
})
|
||||
};
|
||||
let (a, set_a) = create_signal(cx, view.clone());
|
||||
let (b, set_b) = create_signal(cx, view);
|
||||
|
||||
view! { cx,
|
||||
<button on:click=handle_change>"Reverse"</button>
|
||||
let (is_a, set_is_a) = create_signal(cx, true);
|
||||
|
||||
<For
|
||||
each=data
|
||||
key=|item| *item
|
||||
view=|cx, i| view! { cx, <h3>{i}</h3> }
|
||||
/>
|
||||
let handle_toggle = move |_| {
|
||||
trace!("toggling");
|
||||
if is_a() {
|
||||
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>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -347,72 +347,60 @@ where
|
|||
let (children, closing) =
|
||||
(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| {
|
||||
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)
|
||||
{
|
||||
child.get_opening_node()
|
||||
} else {
|
||||
closing.clone()
|
||||
};
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
let opening = if let Some(Some(child)) = children_borrow.get(0) {
|
||||
child.get_opening_node()
|
||||
} else {
|
||||
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 =
|
||||
items.iter().map(&key_fn).collect::<FxIndexSet<_>>();
|
||||
let hashed_items =
|
||||
items.iter().map(&key_fn).collect::<FxIndexSet<_>>();
|
||||
|
||||
if let Some(HashRun(prev_hash_run)) = prev_hash_run {
|
||||
let cmds = diff(&prev_hash_run, &hashed_items);
|
||||
if let Some(HashRun(prev_hash_run)) = prev_hash_run {
|
||||
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(
|
||||
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 {
|
||||
let (each_item, _) = cx.run_child_scope(|cx| EachItem::new(cx, each_fn(cx, item).into_view(cx)));
|
||||
|
||||
for item in items {
|
||||
let (each_item, disposer) = cx.run_child_scope(|cx| {
|
||||
EachItem::new(cx, each_fn(cx, item).into_view(cx))
|
||||
});
|
||||
#[cfg(all(target_arch = "wasm32", feature = "web"))]
|
||||
mount_child(MountKind::Before(&closing), &each_item);
|
||||
|
||||
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)
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
|
||||
{
|
||||
} else {
|
||||
*component.children.borrow_mut() = (items_fn)()
|
||||
.into_iter()
|
||||
.map(|child| {
|
||||
cx.run_child_scope(|cx| {
|
||||
Some(EachItem::new(
|
||||
cx,
|
||||
(each_fn)(cx, child).into_view(cx),
|
||||
))
|
||||
})
|
||||
.0
|
||||
})
|
||||
.collect();
|
||||
.into_iter()
|
||||
.map(|child| cx.run_child_scope(|cx| Some(EachItem::new(cx, (each_fn)(cx, child).into_view(cx)))).0)
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
|
||||
View::CoreComponent(CoreComponent::Each(component))
|
||||
|
@ -423,7 +411,7 @@ where
|
|||
#[educe(Debug)]
|
||||
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"))]
|
||||
fn diff<K: Eq + Hash>(from: &FxIndexSet<K>, to: &FxIndexSet<K>) -> Diff {
|
||||
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
|
||||
let mut removed =
|
||||
from.difference(to).map(|k| from.get_index_of(k).unwrap());
|
||||
let mut removed = from.difference(to);
|
||||
|
||||
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
|
||||
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 {
|
||||
at: idx,
|
||||
mode: Default::default(),
|
||||
});
|
||||
let added_cmds =
|
||||
added
|
||||
.clone()
|
||||
.map(|k| to.get_full(k).unwrap().0)
|
||||
.map(|idx| DiffOpAdd {
|
||||
at: idx,
|
||||
mode: Default::default(),
|
||||
});
|
||||
|
||||
let mut normalized_idx = 0i64;
|
||||
let mut next_added_idx = added.next();
|
||||
let mut next_removed_idx = removed.next();
|
||||
// Get moved items
|
||||
let mut normalized_idx = 0;
|
||||
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
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, k)| {
|
||||
let is_added = if let Some(idx) = next_added_idx {
|
||||
if i == idx {
|
||||
next_added_idx = added.next();
|
||||
normalized_idx -= 1;
|
||||
for (idx, k) in to.iter().enumerate() {
|
||||
if let Some(added_idx) = added_idx.as_mut().filter(|r_i| **r_i == idx) {
|
||||
if let Some(next_added) =
|
||||
added.next().map(|k| to.get_full(k).unwrap().0)
|
||||
{
|
||||
*added_idx = next_added;
|
||||
|
||||
true
|
||||
} 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
|
||||
normalized_idx = usize::wrapping_sub(normalized_idx, 1);
|
||||
}
|
||||
})
|
||||
.map(|(from, to, normalized_idx)| DiffOpMove {
|
||||
from,
|
||||
to,
|
||||
move_in_dom: to != normalized_idx as usize,
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
if let Some(removed_idx) =
|
||||
removed_idx.as_mut().filter(|r_i| **r_i == idx)
|
||||
{
|
||||
normalized_idx = normalized_idx.wrapping_add(1);
|
||||
|
||||
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 {
|
||||
removed: removed_cmds.collect(),
|
||||
|
@ -634,16 +621,17 @@ fn apply_cmds<T, EF, N>(
|
|||
if opening.previous_sibling().is_none()
|
||||
&& closing.next_sibling().is_none()
|
||||
{
|
||||
if let Some(parent) = closing
|
||||
let parent = closing
|
||||
.parent_node()
|
||||
.map(JsCast::unchecked_into::<web_sys::Element>)
|
||||
{
|
||||
#[cfg(debug_assertions)]
|
||||
parent.append_with_node_2(opening, closing).unwrap();
|
||||
.expect("could not get closing node")
|
||||
.unchecked_into::<web_sys::Element>();
|
||||
parent.set_text_content(Some(""));
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
parent.append_with_node_1(closing).unwrap();
|
||||
}
|
||||
#[cfg(debug_assertions)]
|
||||
parent.append_with_node_2(opening, closing).unwrap();
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
parent.append_with_node_1(closing).unwrap();
|
||||
} else {
|
||||
range.set_start_before(opening).unwrap();
|
||||
range.set_end_before(closing).unwrap();
|
||||
|
@ -676,7 +664,7 @@ fn apply_cmds<T, EF, N>(
|
|||
for DiffOpAdd { at, mode } in cmds.added {
|
||||
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);
|
||||
EachItem::new(cx, child)
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue