-
Notifications
You must be signed in to change notification settings - Fork 22
Description
The Problem
Currently, when using components, it is necessary to explicitly specify .. to fill in omitted properties with their default values:
rsx! {
<Element ../>
<Element id="element" ../>
}This is because the generated code instantiates the component using struct literal syntax, where .. expands to ..Default::default():
Element {
id: "element",
..Default::default()
}This approach is simple and clear. However, for large components with many properties, it results in the almost constant use of .., which clutters the code:
rsx! {
<CodeExample ..>
<CodeExamplePreview resize=true ..>
<Element ..>"Hello"</Element>
</CodeExamplePreview>
<CodeExampleSource ..>
<code class="language-html">
r#"<Element ..>"Hello"</Element>"#
</code>
</CodeExampleSource>
<CodeExampleButton ..>"Code"</CodeExampleButton>
</CodeExample>
}Additionally, this makes it harder to use wrapper or aggregating structs, since all component attributes must match struct field names exactly. This leads to less clean syntax like the following:
#[derive(Default)]
pub struct CodeExample {
pub open: bool,
pub attrs: CommonAttrs,
pub children: Lazy<fn(&mut Buffer)>,
}
rsx! {
<CodeExample open=true attrs=(CommonAttrs::new().id("example").class("toggle").style("color: red"))>
...
</CodeExample>
}Proposed Solution
I propose extending the component instantiation code generator with an additional mode that relies on:
- The component struct implementing
Default - Builder-style setter methods for properties
For example:
Element::default()
.id("element")
.tabindex(2)With this approach, separate macro variants such as maud_cb! and rsx_cb! (CB = Component Builder) could significantly simplify component property syntax:
rsx_cb! {
<Element />
<Element id="element" />
<Element id="element" tabindex=2 />
}rsx_cb! {
<CodeExample />
<CodeExample open=true id="example" class="toggle" style="color: red" />
}The .attr(...) methods could be implemented either manually or automatically using a #[derive(Builder)] macro based on the struct’s fields:
#[derive(Default, Builder)]
struct Element<'a> {
id: &'a str,
tabindex: u32,
children: Lazy<fn(&mut Buffer)>,
#[builder(skip)]
_skipped: (),
}Implementation
I have implemented the functionality described above so it can be experimented with and potentially used as a foundation if there is interest in adding it to hypertext.
PR: #179
Working example in tests: https://github.com/vidhanio/hypertext/pull/179/changes#diff-dc0fadfd8cc5b92a1297488453c2a317f203183fd3b3a27f0c4870b7aa9cb47cR808