ccbrown / iocraft

A Rust crate for beautiful, artisanally crafted CLIs, TUIs, and text-based IO.
https://crates.io/crates/iocraft
Apache License 2.0
315 stars 4 forks source link

Few questions on usage #29

Open milesj opened 5 days ago

milesj commented 5 days ago

@ccbrown Just have a few questions so far that I haven't been able to figure out.

1 - How to pass children through for custom components? Maybe we need a Fragment component here?

#[derive(Default, Props)]
pub struct EntryProps<'a> {
    pub title: String,
    pub children: Vec<AnyElement<'a>>,
}

#[component]
pub fn Entry<'a>(props: &EntryProps<'a>) -> impl Into<AnyElement<'static>> {
    element! {
        Box {
            Box(margin_right: 1) {
                Text(content: format!("{}:", props.title))
            }
            Box {
                // What to do here???
                #(props.children.iter())
            }
        }
    }
}

2 - Is it possible to render components as props to other components? Or is it only possible with children?

// This
Box {
    Entry(title: "Root", value: StyledText(content: env.store.dir.to_string_lossy(), style: Style::Path))
}

// Instead of 
Box {
    Entry(title: "Root") {
        StyledText(content: env.store.dir.to_string_lossy(), style: Style::Path)
    }
}
ccbrown commented 5 days ago
  1. What you have almost works, except props needs to become a mutable reference, and the lifetime of the returned element becomes 'a:
#[component]
pub fn Entry<'a>(props: &mut EntryProps<'a>) -> impl Into<AnyElement<'a>> {
    element! {
        Box {
            Box(margin_right: 1) {
                Text(content: format!("{}:", props.title))
            }
            Box {
                #(&mut props.children)
            }
        }
    }
}
  1. There's nothing particularly special about the props values. Anything you can express in Rust can be passed. So to pass another element, you would need to use the element! macro like so:
#[derive(Default, Props)]
pub struct ExampleProps<'a> {
    pub my_child: Option<AnyElement<'a>>,
}

#[component]
fn Example<'a>(props: &mut ExampleProps<'a>) -> impl Into<AnyElement<'a>> {
    element! {
        Box {
            #(&mut props.my_child)
        }
    }
}

fn main() {
    element! {
        Example(my_child: element! {
            Text(content: "foo")
        }.into_any())
    }
    .print();
}

Or alternatively, if you want to avoid nesting, you can define the child beforehand:

fn main() {
    let my_child = element! {
        Text(content: "foo")
    }
    .into_any();

    element! {
        Example(my_child)
    }
    .print();
}
milesj commented 5 days ago

Awesome, got these working, thank you! It would be great of their was an example file with all the different ways things can be done in regards to the syntax.