Skip to content

Instantly share code, notes, and snippets.

@PoignardAzur
Created October 20, 2020 21:28
Show Gist options
  • Select an option

  • Save PoignardAzur/7336b1fa1e2a31daf83581c799ce60b7 to your computer and use it in GitHub Desktop.

Select an option

Save PoignardAzur/7336b1fa1e2a31daf83581c799ce60b7 to your computer and use it in GitHub Desktop.
pub trait VirtualDom {
fn update_value(&mut self, other: Self);
#[track_caller]
fn init_tree(&self, cx: &mut Cx);
fn apply_diff(&self, other: &Self, cx: &mut Cx) -> bool;
}
pub enum VDomLeaf {
Button(String),
Label(String),
};
impl VirtualDom for VDomLeaf {
fn update_value(&mut self, other: Self) {
self = other;
}
#[track_caller]
fn init_tree(&self, cx: &mut Cx) {
match &self {
Button(text) => cx.leaf_view(crate::Button(text, Location::caller())),
Label(text) => cx.leaf_view(crate::Label(text, Location::caller())),
};
}
fn apply_diff(&self, other: &Self, cx: &mut Cx) {
if (&self == &other) {
cx.skip(1);
}
else {
match &self {
Button(text) => cx.leaf_view(crate::Button(text, Location::caller())),
Label(text) => cx.leaf_view(crate::Label(text, Location::caller())),
};
}
}
}
pub struct ComponentTuple<C0, C1, C2, C3, C4, C5, C6, C7>(C0, C1, C2, C3, C4, C5, C6, C7);
impl<C0, C1, C2, C3, C4, C5, C6, C7> VirtualDom for ComponentTuple<C0, C1, C2, C3, C4, C5, C6, C7> {
fn update_value(&mut self, other: Self) {
self = other;
}
#[track_caller]
fn init_tree(&self, cx: &mut Cx) {
self.0.init_tree(cx);
self.1.init_tree(cx);
self.2.init_tree(cx);
self.3.init_tree(cx);
self.4.init_tree(cx);
self.5.init_tree(cx);
self.6.init_tree(cx);
self.7.init_tree(cx);
}
fn apply_diff(&self, other: &Self, cx: &mut Cx) {
self.0.apply_diff(other.0, cx);
self.1.apply_diff(other.1, cx);
self.2.apply_diff(other.2, cx);
self.3.apply_diff(other.3, cx);
self.4.apply_diff(other.4, cx);
self.5.apply_diff(other.5, cx);
self.6.apply_diff(other.6, cx);
self.7.apply_diff(other.7, cx);
}
}
// Instead of doing multiple implementations of TupleComponent for different tuple sizes,
// I'm being lazy and doing one implem for a huge tuple, and stuffing it with EmptyComponent
// when using it. It's *a lot* easier.
pub struct EmptyComponent();
impl VirtualDom for EmptyComponent {
fn update_value(&mut self, other: Self) {}
#[track_caller]
fn init_tree(&self, cx: &mut Cx) {}
fn apply_diff(&self, other: &Self, cx: &mut Cx) {}
}
pub struct ComponentList<Comp> {
components: Vec<(String, Comp)>
}
impl VirtualDom for ComponentList<Comp> {
fn update_value(&mut self, other: Self) {
self = other;
}
#[track_caller]
fn init_tree(&self, cx: &mut Cx) {
cx.begin_insert();
for (_, comp) in self.components {
comp.init_tree(cx);
}
cx.end();
}
// This only works if we assume that items are ever only added at the end of the list.
// Sounds perfectly reasonable to me.
// (seriously though, a serious implementation would try to do whatever crochet::List::run does)
fn apply_diff(&self, other: &Self, cx: &mut Cx) {
for (other_id, other_comp) in other.components {
if let (_, comp) = self.components.find(|(id, _)| id == other_id) {
cx.begin_update();
comp.apply_diff(other_comp, cx);
cx.end();
}
else {
cx.delete(1);
}
}
for (id, comp) in self.components {
if other.components.find(|(other_id, _)| id == other_id).is_none() {
cx.begin_insert();
comp.init_tree(cx);
cx.end();
}
}
}
}
pub struct ReactComponent<Props, VDom : VirtualDom> {
component: Fn(Props) -> VDom,
prev_vdom: VDom
}
impl ReactComponent<Props> {
fn run(&mut self, cx: &mut Cx, props: &Props) {
let vdom = self.component(props);
}
}
struct Props {
data: ListData<String>,
selected_row: Option<i32>,
counter: usize,
}
fn some_component(props: Props) -> impl VirtualDom {
let buttonCreate = Button::new("Create");
// TODO - on pressed Create
let buttonDelete = Button::new("Delete");
// TODO - on pressed Delete
let buttonUpdate = Button::new("Update");
// TODO - on pressed Update
let listView = props.data.map_i(|txt: &String, id: i32| {
list_row(txt, id == props.selected_row.or(-1));
});
ComponentTuple(
buttonCreate,
buttonDelete,
buttonUpdate,
listView,
)
}
fn some_component(row_str: &String, is_selected: bool) -> impl VirtualDom {
ComponentTuple(
Button::new("Select"), // TODO - change selected
Label::new(if is_selected { "[*]" } else { "[ ]" }),
Label::new(row_str)
)
}
impl MyAppLogic {
fn run(&mut self, cx: &mut Cx) {
Row::new().build(cx, |cx| {
if Button::new("Create").build(cx) {
self.data.push(format!("item {}", self.counter));
self.counter += 1;
}
if Button::new("Delete").build(cx) {
if let Some(id) = self.list_view.selected() {
if let Some(ix) = self.data.find_id(id) {
self.data.remove_at_ix(ix);
}
}
}
if Button::new("Update").build(cx) {
if let Some(id) = self.list_view.selected() {
if let Some(ix) = self.data.find_id(id) {
self.data.set_at_ix(ix, format!("update {}", self.counter));
self.counter += 1;
}
}
}
});
let mut new_sel = None;
self.list_view
.run(cx, &self.data, |cx, is_selected, id: Id, item| {
Row::new().build(cx, |cx| {
if Button::new("Select").build(cx) {
new_sel = Some(id);
}
let sel_str = if is_selected { "[*]" } else { "[ ]" };
Label::new(format!("{} {}", sel_str, item)).build(cx);
});
});
if let Some(id) = new_sel {
self.list_view.select(id);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment