Skip to content

Commit

Permalink
Generalize slice view to any iterator
Browse files Browse the repository at this point in the history
  • Loading branch information
kmicklas committed Jun 25, 2024
1 parent 57e3299 commit d68fa76
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 46 deletions.
8 changes: 4 additions & 4 deletions examples/tutorial/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::BTreeMap;
use ravel::{adapt_ref, with, with_local};
use ravel_web::{
any, attr,
collections::{btree_map, slice},
collections::{btree_map, iter},
el,
event::{self, on, on_},
format_text,
Expand Down Expand Up @@ -193,7 +193,7 @@ fn dynamic_view(model: &Model) -> View!(Model, '_) {
/// at any position.
///
/// If the data is just an array which grows or shrinks at the end, we can use
/// [`slice()`] to generate a [`trait@View`] over a [`Vec`]/slice.
/// [`iter()`] to generate a [`trait@View`] over any iterator.
fn lists(model: &Model) -> View!(Model, '_) {
(
el::h2("Map view"),
Expand All @@ -215,10 +215,10 @@ fn lists(model: &Model) -> View!(Model, '_) {
)))
})),
))),
el::h2("Slice view"),
el::h2("Iterator view"),
el::p(el::table((
el::thead(el::tr((el::td(()), el::td("Id"), el::td("Message")))),
el::tbody(slice(&model.item_vec, |cx, i, (key, value)| {
el::tbody(iter(&model.item_vec, |cx, i, (key, value)| {
let key = *key;
cx.build(el::tr((
el::td(el::button((
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{cmp::Ordering, marker::PhantomData};
use std::{iter::once, marker::PhantomData};

use ravel::{with, State, Token};
use web_sys::wasm_bindgen::UnwrapThrowExt;
Expand All @@ -8,23 +8,22 @@ use crate::{
BuildCx, Builder, Cx, RebuildCx, Web,
};

pub struct SliceBuilder<'data, T, RenderItem, S> {
data: &'data [T],
pub struct IterBuilder<I, RenderItem, S> {
iter: I,
render_item: RenderItem,
phantom: PhantomData<S>,
}

impl<'data, T, RenderItem, S: 'static> Builder<Web>
for SliceBuilder<'data, T, RenderItem, S>
impl<I: Iterator, RenderItem, S: 'static> Builder<Web>
for IterBuilder<I, RenderItem, S>
where
RenderItem: Fn(Cx<S, Web>, usize, &T) -> Token<S>,
RenderItem: Fn(Cx<S, Web>, usize, I::Item) -> Token<S>,
{
type State = SliceState<S>;
type State = IterState<S>;

fn build(self, cx: BuildCx) -> Self::State {
let data = self
.data
.iter()
.iter
.enumerate()
.map(|(i, v)| {
let header =
Expand All @@ -41,22 +40,22 @@ where
let footer = web_sys::Comment::new_with_data("|").unwrap_throw();
cx.position.insert(&footer);

SliceState { data, footer }
IterState { data, footer }
}

fn rebuild(self, cx: RebuildCx, state: &mut Self::State) {
for (i, (v, entry)) in
self.data.iter().zip(state.data.iter_mut()).enumerate()
{
with(|cx| (self.render_item)(cx, i, v))
.rebuild(cx, &mut entry.state)
}
fn rebuild(mut self, cx: RebuildCx, state: &mut Self::State) {
let mut data = state.data.iter_mut();

match self.data.len().cmp(&state.data.len()) {
Ordering::Equal => {}
Ordering::Greater => state.data.extend(
self.data.iter().enumerate().skip(state.data.len()).map(
|(i, v)| {
for i in 0.. {
match (self.iter.next(), data.next()) {
(None, None) => break,
(None, Some(entry)) => {
clear(cx.parent, &entry.header, &state.footer);
state.data.truncate(i);
break;
}
(Some(v), None) => {
state.data.extend(once(v).chain(self.iter).map(|v| {
let position = Position {
parent: cx.parent,
insert_before: &state.footer,
Expand All @@ -72,27 +71,24 @@ where
state: with(|cx| (self.render_item)(cx, i, v))
.build(BuildCx { position }),
}
},
),
),
Ordering::Less => {
clear(
cx.parent,
&state.data[self.data.len()].header,
&state.footer,
);
state.data.truncate(self.data.len());
}));
break;
}
(Some(v), Some(entry)) => {
with(|cx| (self.render_item)(cx, i, v))
.rebuild(cx, &mut entry.state)
}
}
}
}
}

pub struct SliceState<S> {
pub struct IterState<S> {
data: Vec<Entry<S>>,
footer: web_sys::Comment,
}

impl<S, Output> State<Output> for SliceState<S>
impl<S, Output> State<Output> for IterState<S>
where
S: State<Output>,
{
Expand All @@ -108,16 +104,16 @@ struct Entry<S> {
state: S,
}

pub fn slice<T, RenderItem, S>(
data: &[T],
pub fn iter<I: IntoIterator, RenderItem, S>(
iter: I,
render_item: RenderItem,
) -> SliceBuilder<T, RenderItem, S>
) -> IterBuilder<I::IntoIter, RenderItem, S>
where
RenderItem: Fn(Cx<S, Web>, usize, &T) -> Token<S>,
RenderItem: Fn(Cx<S, Web>, usize, I::Item) -> Token<S>,
{
SliceBuilder {
IterBuilder {
render_item,
data,
iter: iter.into_iter(),
phantom: PhantomData,
}
}
4 changes: 2 additions & 2 deletions ravel-web/src/collections/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Views over dynamically sized collections.

pub mod btree_map;
pub mod slice;
pub mod iter;

pub use btree_map::btree_map;
pub use slice::slice;
pub use iter::iter;

0 comments on commit d68fa76

Please sign in to comment.