Open teenjuna opened 1 year ago
RE props. Maybe we can use different syntax for different components.
For(
each = move || counters.get(),
key = |counter| counter.id,
view = move |counter| view![counter.count.get()]
)
For(
move || counters.get(),
|counter| counter.id,
move |counter| view![counter.count.get()]
)
For(ForProps {
each: move || counters.get(),
key: |counter| counter.id,
view: move |counter| view![counter.count.get()]
})
So, you could still create components with the #[component]
macro, but alternative view!
(they should be usable from native view!
macro). Or you can create components with normal arguments or props structs (such components may not be usable with the native view!
macro).
RE snake_case
-named components. Native macro doesn't support them, as they can't be differentiated from the normal HTML elements. I wonder how Dioxus does this. I have a feeling that we can add use leptos::html::*
to the beginning of the expanded macro code. Then we would rely on the normal Rust scoping. If you create a component named header
and try to use it inside other component, you wouldn't able to, because leptos::html::header
would overwrite it. But you would be able to use self::header
. That's the behaviour of Dioxus, so they may also use this approach.
Also, if we go with that approach, then this example from original snippet won't work:
"custom-web-component-with-dashes"(enabled = true),
custom_web_component_with_underscore(enabled = true),
Because custom_web_component_with_underscore
won't be found in the scope. Instead, we would do this:
"custom-web-component-with-dashes"(enabled = true),
"custom_web_component_with_underscore"(enabled = true),
Which makes sense, since it clearly indicates that these elements are coming from somewhere else.
For macro implementation, we can take a look on how it's done for https://docs.rs/mogwai-dom/latest/mogwai_dom/macro.rsx.html
@Jinxit what do you think?
Just looked at the playground link, I love how it works with Rustfmt as is!
The biggest issue I have with it though is the separation between props/attrs and children are not so clear for me.
It seems like children are the last items, and do not have any =
, but it still might be nice to explore a way to clarify the distinction to better visualize the layout of the elements.
For example, this seems to work:
view![
div(
class = "class1",
class = (
foo = move |_| signal.get(),
"bar baz" = move |_| signal.get()
),
on = (click = move |_| {}, keydown = move |_| {}),
custom_prop = "custom_prop_value",
)([
div(
class = "class2",
div(class = "class3"),
div(class = "class3"),
),
div(
class = "class2",
div(class = "class3"),
div(class = "class3"),
),
]),
];
Not sure if I love it though :')
@tqwewe I thought about something like this:
fn main() {
view![
div(
id = "id1",
class = "class1",
on = (click = move |_| {}, keydown = move |_| {})
)(
div(id = "id2", class = "class2"),
div(id = "id3", class = "class3")
),
div(),
];
}
It looks nice when it's folded, but inline is kind of meh
This is a nice snippet for comparison:
fn main() {
view![
div(
id = "id1",
class = "class1",
on = (click = move |_| {}, keydown = move |_| {})
)(
div(id = "id2", class = "class2")(div()),
div(id = "id3", class = "class3")
),
div(),
];
view![
div(
id = "id1",
class = "class1",
on = (click = move |_| {}, keydown = move |_| {}),
div(id = "id2", class = "class2", div()),
div(id = "id3", class = "class3")
),
div(),
];
}
The problem with the explicit props/children is that if you have a div
that only has children, it would look like this: div()(...)
, which is meh. Second variant would be div(...)
. Just empty div could be div
in both cases.
And for the sake of exploring all options, another option might be:
view![
div(
id = "id1",
class = "class1",
on = (click = move |_| {}, keydown = move |_| {}),
[
div(id = "id2", class = "class2"),
div(id = "id3", class = "class3")
]
),
div(),
];
The problem with the explicit props/children is that if you have a div that only has children, it would look like this: div()(...), which is meh.
This snippet doesn't have that issue at least!
But this adds extra tab, which sucks :( I wish that worked:
fn main() {
view![
div(
id = "id1",
class = "class1",
on = (click = move |_| {}, keydown = move |_| {})
)[
div(id = "id2", class = "class2")[div()],
div(id = "id3", class = "class3")
],
div(),
];
}
But it breaks rustfmt
:(
So, we continued the Discussion in the discord thread and chose this form:
fn main() {
view![
div(
id = "id1",
class = "class1",
on[click] = move |_| {},
on[keydown] = move |_| {},
div(id = "id2", class = "class2", "Just a string"),
div(id = "id3", class = "class3"),
div("Another string"),
),
div(
id = "id1",
class = "class1",
on[click] = move |_| {},
on[keydown] = move |_| {},
//
div(id = "id2", class = "class2", "Just a string"),
div(id = "id3", class = "class3"),
div("Another string"),
),
div(
id = "id1",
class = "class1",
on[click] = move |_| {},
on[keydown] = move |_| {},
[
div(id = "id2", class = "class2", "Just a string"),
div(id = "id3", class = "class3"),
div("Another string"),
]
),
];
}
@tqwewe already started implementation and I'll try to add a spec later today.
So, in this Discord thread we came up with an idea of how an alternative,
rustfmt
-friendly macro for Leptos could look like.Here is a snippet: playground.
There still needs to be a Discussion on how to deal with props. Personally, I don't like the builder pattern achieved with the
#[component]
macro. Writingsnake_case
-named components with normal Rust arguments looks cleaner. But we need to support normal Leptos components.Right now it's worth making a
SPEC.md
file that describes the main parsing rules of the macro.