Disambiguate expressions in rsx by requiring curlies

This commit is contained in:
Jonathan Kelley committed 2024-01-10 19:33:34 -08:00
1 parent 663296ae45
commit d9b84f9f8f
53 files changed
+399 -373

No files matched your search

+5 -1
View File
@@ -76,7 +76,11 @@ fn app(cx: Scope) -> Element {
"Hover, click, type or scroll to see the info down below"
}
div { events.read().iter().map(|event| rsx!( div { "{event:?}" } )) }
div {
for event in events.read().iter() {
div { "{event:?}" }
}
}
}
))
}
+4 -4
View File
@@ -58,13 +58,13 @@ fn app(cx: Scope) -> Element {
};
cx.render(rsx!(
style { include_str!("./assets/calculator.css") }
style { {include_str!("./assets/calculator.css")} }
div { id: "wrapper",
div { class: "app",
div { class: "calculator",
tabindex: "0",
onkeydown: handle_key_down_event,
div { class: "calculator-display", val.to_string() }
div { class: "calculator-display", "{val}" }
div { class: "calculator-keypad",
div { class: "input-keys",
div { class: "function-keys",
@@ -103,14 +103,14 @@ fn app(cx: Scope) -> Element {
div { class: "digit-keys",
button { class: "calculator-key key-0", onclick: move |_| input_digit(0), "0" }
button { class: "calculator-key key-dot", onclick: move |_| val.make_mut().push('.'), "●" }
(1..10).map(|k| rsx!{
for k in 1..10 {
button {
class: "calculator-key {k}",
name: "key-{k}",
onclick: move |_| input_digit(k),
"{k}"
}
}),
}
}
}
div { class: "operator-keys",
+2 -2
View File
@@ -32,12 +32,12 @@ fn app(cx: Scope) -> Element {
}
ul {
emails_sent.read().iter().map(|message| cx.render(rsx! {
for message in emails_sent.read().iter() {
li {
h3 { "email" }
span {"{message}"}
}
}))
}
}
}
})
+1 -1
View File
@@ -16,7 +16,7 @@ fn app(cx: Scope) -> Element {
loop {
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
if let Some(element) = elements.read().get(focused) {
element.set_focus(true);
_ = element.set_focus(true).await;
} else {
focused = 0;
}
+2 -2
View File
@@ -62,7 +62,7 @@ fn ClientList(cx: Scope) -> Element {
Link { to: Route::ClientAdd {}, class: "pure-button pure-button-primary", "Add Client" }
Link { to: Route::Settings {}, class: "pure-button", "Settings" }
clients.read().iter().map(|client| rsx! {
for client in clients.read().iter() {
div {
class: "client",
style: "margin-bottom: 50px",
@@ -70,7 +70,7 @@ fn ClientList(cx: Scope) -> Element {
p { "Name: {client.first_name} {client.last_name}" }
p { "Description: {client.description}" }
}
})
}
})
}
+2 -2
View File
@@ -14,9 +14,9 @@ fn app(cx: Scope) -> Element {
}
render! {
(0..count).map(|_| rsx!{
{(0..count).map(|_| rsx!{
drop_child {}
})
})}
}
}
+3 -3
View File
@@ -39,9 +39,9 @@ fn ChildWithRef(cx: Scope) -> Element {
cx.render(rsx! {
div {
ul {
names.read().iter().map(|f| rsx!{
li { "hello: {f}" }
})
for name in names.read().iter() {
li { "hello: {name}" }
}
}
button {
onclick: move |_| {
+9 -11
View File
@@ -28,12 +28,12 @@ fn app(cx: Scope) -> Element {
link { href:"https://fonts.googleapis.com/icon?family=Material+Icons", rel:"stylesheet", }
header {
i { class: "material-icons icon-menu", "menu" }
h1 { "Files: ", files.read().current() }
h1 { "Files: ", {files.read().current()} }
span { }
i { class: "material-icons", onclick: move |_| files.write().go_up(), "logout" }
}
main {
files.read().path_names.iter().enumerate().map(|(dir_id, path)| {
{files.read().path_names.iter().enumerate().map(|(dir_id, path)| {
let path_end = path.split('/').last().unwrap_or(path.as_str());
let icon_type = if path_end.contains('.') {
"description"
@@ -52,15 +52,13 @@ fn app(cx: Scope) -> Element {
h1 { "{path_end}" }
}
)
}),
files.read().err.as_ref().map(|err| {
rsx! (
div {
code { "{err}" }
button { onclick: move |_| files.write().clear_err(), "x" }
}
)
})
})},
if let Some(err) = files.read().err.as_ref() {
div {
code { "{err}" }
button { onclick: move |_| files.write().clear_err(), "x" }
}
}
}
}
})
+6 -5
View File
@@ -66,9 +66,9 @@ fn app(cx: Scope) -> Element {
}
table {
tbody {
items.read().iter().enumerate().map(|(id, item)| {
let is_in_danger = if (*selected).map(|s| s == id).unwrap_or(false) {"danger"} else {""};
rsx!(tr { class: "{is_in_danger}",
for (id, item) in items.read().iter().enumerate() {
tr {
class: if (*selected).map(|s| s == id).unwrap_or(false) { "danger" },
td { class:"col-md-1" }
td { class:"col-md-1", "{item.key}" }
td { class:"col-md-1", onclick: move |_| selected.set(Some(id)),
@@ -80,8 +80,9 @@ fn app(cx: Scope) -> Element {
}
}
td { class: "col-md-6" }
})
})
}
}
}
}
span { class: "preloadicon glyphicon glyphicon-remove", aria_hidden: "true" }
+3 -3
View File
@@ -37,7 +37,7 @@ const FIELDS: &[(&str, &str)] = &[
fn app(cx: Scope) -> Element {
cx.render(rsx! {
div { margin_left: "30px",
select_example(cx),
{select_example(cx)},
div {
// handling inputs on divs will catch all input events below
// so the value of our input event will be either huey, dewey, louie, or true/false (because of the checkboxe)
@@ -114,7 +114,7 @@ fn app(cx: Scope) -> Element {
}
}
FIELDS.iter().map(|(field, value)| rsx! {
for (field, value) in FIELDS.iter() {
div {
input {
id: "{field}",
@@ -131,7 +131,7 @@ fn app(cx: Scope) -> Element {
}
br {}
}
})
}
}
})
}
@@ -41,7 +41,7 @@ pub fn LogOut(cx: Scope<ClientProps>) -> Element {
}
Err(error) => {
rsx! {
div { format!{"Failed to load disconnection url: {:?}", error} }
div { "Failed to load disconnection url: {error:?}" }
}
}
},
@@ -143,9 +143,9 @@ pub fn LoadClient(cx: Scope) -> Element {
}
}
Err(error) => {
log::info! {"Failed to load client: {:?}", error};
rsx! {
div { format!{"Failed to load client: {:?}", error} }
log::info!{"Failed to load client: {:?}", error},
div { "Failed to load client: {error:?}" }
Outlet::<Route> {}
}
}
@@ -184,7 +184,7 @@ pub fn AuthHeader(cx: Scope) -> Element {
Ok(email) => {
rsx! {
div {
div { email }
div { {email} }
LogOut { client_id: client_props.client_id, client: client_props.client }
Outlet::<Route> {}
}
@@ -207,7 +207,7 @@ pub fn AuthHeader(cx: Scope) -> Element {
log::info!("Other issue with token");
rsx! {
div {
div { error.to_string() }
div { "{error}" }
Outlet::<Route> {}
}
}
@@ -2,6 +2,9 @@ use dioxus::prelude::*;
#[component]
pub fn NotFound(cx: Scope, route: Vec<String>) -> Element {
let routes = route.join("");
render! {rsx! {div{routes}}}
render! {
div{
{route.join("")}
}
}
}
+5 -5
View File
@@ -39,11 +39,11 @@ fn app(cx: Scope) -> Element {
let state = use_ref(cx, Calculator::new);
cx.render(rsx! {
style { include_str!("./assets/calculator.css") }
style { {include_str!("./assets/calculator.css")} }
div { id: "wrapper",
div { class: "app",
div { class: "calculator", onkeypress: move |evt| state.write().handle_keydown(evt),
div { class: "calculator-display", state.read().formatted_display() }
div { class: "calculator-display", {state.read().formatted_display()} }
div { class: "calculator-keypad",
div { class: "input-keys",
div { class: "function-keys",
@@ -74,14 +74,14 @@ fn app(cx: Scope) -> Element {
onclick: move |_| state.write().input_dot(),
"●"
}
(1..10).map(move |k| rsx!{
for k in 1..10 {
CalculatorKey {
key: "{k}",
name: "key-{k}",
onclick: move |_| state.write().input_digit(k),
"{k}"
}
})
}
}
}
div { class: "operator-keys",
@@ -130,7 +130,7 @@ fn CalculatorKey<'a>(cx: Scope<'a, CalculatorKeyProps<'a>>) -> Element {
button {
class: "calculator-key {cx.props.name}",
onclick: move |e| cx.props.onclick.call(e),
&cx.props.children
{&cx.props.children}
}
})
}
+1 -1
View File
@@ -20,7 +20,7 @@ fn app(cx: Scope) -> Element {
cx.render(rsx!(
div {
h1 {"Select an option"}
h3 { "The radio is... ", state.is_playing(), "!" }
h3 { "The radio is... ", {state.is_playing()}, "!" }
button { onclick: move |_| state.make_mut().reduce(PlayerAction::Pause),
"Pause"
}
+2 -2
View File
@@ -66,7 +66,7 @@ impl FromQuery for ManualBlogQuerySegments {
fn BlogPost(cx: Scope, query_params: ManualBlogQuerySegments) -> Element {
render! {
div{"This is your blogpost with a query segment:"}
div{format!("{:?}", query_params)}
div{ "{query_params:?}" }
}
}
@@ -74,7 +74,7 @@ fn BlogPost(cx: Scope, query_params: ManualBlogQuerySegments) -> Element {
fn AutomaticBlogPost(cx: Scope, name: String, surname: String) -> Element {
render! {
div{"This is your blogpost with a query segment:"}
div{format!("name={}&surname={}", name, surname)}
div{ "name={name}&surname={surname}" }
}
}
+8 -8
View File
@@ -36,17 +36,17 @@ fn example(cx: Scope) -> Element {
div { id: "asd",
"your neighborhood spiderman"
items.iter().cycle().take(5).map(|f| rsx!{
div { "{f.a}" }
})
for item in items.iter().cycle().take(5) {
div { "{item.a}" }
}
things_list.iter().map(|f| rsx!{
div { "{f.a}" "{f.b}" }
})
for thing in things_list.iter() {
div { "{thing.a}" "{thing.b}" }
}
mything_read.as_ref().map(|f| rsx! {
if let Some(f) = mything_read.as_ref() {
div { "{f}" }
})
}
}
}
))
+17 -16
View File
@@ -61,7 +61,7 @@ fn App(cx: Scope) -> Element {
h1 {"Some text"}
h1 {"Some text with {formatting}"}
h1 {"Formatting basic expressions {formatting_tuple.0} and {formatting_tuple.1}"}
h1 {"Formatting without interpolation " formatting_tuple.0 "and" formatting_tuple.1 }
h1 {"Formatting without interpolation " {formatting_tuple.0} "and" {formatting_tuple.1} }
h2 {
"Multiple"
"Text"
@@ -94,10 +94,10 @@ fn App(cx: Scope) -> Element {
}
// Expressions can be used in element position too:
rsx!(p { "More templating!" }),
{rsx!(p { "More templating!" })},
// Iterators
(0..10).map(|i| rsx!(li { "{i}" })),
{(0..10).map(|i| rsx!(li { "{i}" }))},
// Iterators within expressions
{
@@ -117,24 +117,25 @@ fn App(cx: Scope) -> Element {
// Conditional rendering
// Dioxus conditional rendering is based around None/Some. We have no special syntax for conditionals.
// You can convert a bool condition to rsx! with .then and .or
true.then(|| rsx!(div {})),
{true.then(|| rsx!(div {}))},
// Alternatively, you can use the "if" syntax - but both branches must be resolve to Element
if false {
rsx!(h1 {"Top text"})
h1 {"Top text"}
} else {
rsx!(h1 {"Bottom text"})
h1 {"Bottom text"}
}
// Using optionals for diverging branches
if true {
// Note that since this is wrapped in curlies, it's interpreted as an expression
{if true {
Some(rsx!(h1 {"Top text"}))
} else {
None
}
}}
// returning "None" without a diverging branch is a bit noisy... but rare in practice
None as Option<()>,
{None as Option<()>},
// can also just use empty fragments
Fragment {}
@@ -169,13 +170,13 @@ fn App(cx: Scope) -> Element {
// Can pass in props directly as an expression
{
let props = TallerProps {a: "hello", children: cx.render(rsx!(()))};
let props = TallerProps {a: "hello", children: None };
rsx!(Taller { ..props })
}
// Spreading can also be overridden manually
Taller {
..TallerProps { a: "ballin!", children: cx.render(rsx!(()) )},
..TallerProps { a: "ballin!", children: None },
a: "not ballin!"
}
@@ -204,16 +205,16 @@ fn App(cx: Scope) -> Element {
// helper functions
// Anything that implements IntoVnode can be dropped directly into Rsx
helper(cx, "hello world!")
{helper(cx, "hello world!")}
// Strings can be supplied directly
String::from("Hello world!")
{String::from("Hello world!")}
// So can format_args
format_args!("Hello {}!", "world")
{format_args!("Hello {}!", "world")}
// Or we can shell out to a helper function
format_dollars(10, 50)
{format_dollars(10, 50)}
}
})
}
@@ -269,7 +270,7 @@ pub struct TallerProps<'a> {
#[component]
pub fn Taller<'a>(cx: Scope<'a, TallerProps<'a>>) -> Element {
cx.render(rsx! {
&cx.props.children
{&cx.props.children}
})
}
+1 -1
View File
@@ -23,7 +23,7 @@ fn app(cx: Scope) -> Element {
button {
onclick: move |_| {
if let Some(header) = header_element.read().as_ref() {
header.scroll_to(ScrollBehavior::Smooth);
_ = header.scroll_to(ScrollBehavior::Smooth);
}
},
"Scroll to top"
+3 -3
View File
@@ -31,7 +31,7 @@ fn app(cx: Scope) -> Element {
// We can do boolean operations on the current signal value
if count.value() > 5 {
rsx!{ h2 { "High five!" } }
h2 { "High five!" }
}
// We can cleanly map signals with iterators
@@ -41,9 +41,9 @@ fn app(cx: Scope) -> Element {
// We can also use the signal value as a slice
if let [ref first, .., ref last] = saved_values.read().as_slice() {
rsx! { li { "First and last: {first}, {last}" } }
li { "First and last: {first}, {last}" }
} else {
rsx! { "No saved values" }
"No saved values"
}
})
}
+5 -5
View File
@@ -8,17 +8,17 @@ fn app(cx: Scope) -> Element {
cx.render(rsx!(
div {
// Use Map directly to lazily pull elements
(0..10).map(|f| rsx! { "{f}" }),
{(0..10).map(|f| rsx! { "{f}" })},
// Collect into an intermediate collection if necessary, and call into_iter
["a", "b", "c", "d", "e", "f"]
{["a", "b", "c", "d", "e", "f"]
.into_iter()
.map(|f| rsx! { "{f}" })
.collect::<Vec<_>>()
.into_iter(),
.into_iter()},
// Use optionals
Some(rsx! { "Some" }),
{Some(rsx! { "Some" })},
// use a for loop where the body itself is RSX
for name in 0..10 {
@@ -27,7 +27,7 @@ fn app(cx: Scope) -> Element {
// Or even use an unterminated conditional
if true {
rsx!{ "hello world!" }
"hello world!"
}
}
))
+1 -1
View File
@@ -97,7 +97,7 @@ pub fn Die<'a>(cx: Scope<'a, DieProps<'a>>) -> Element {
fill: "{fill}",
}
dots
{dots}
}
})
}
+33 -35
View File
@@ -52,30 +52,30 @@ pub fn app(cx: Scope<()>) -> Element {
TodoHeader { todos: todos }
section { class: "main",
if !todos.is_empty() {
rsx! {
input {
id: "toggle-all",
class: "toggle-all",
r#type: "checkbox",
onchange: move |_| {
let check = active_todo_count != 0;
for (_, item) in todos.make_mut().iter_mut() {
item.checked = check;
}
},
checked: if active_todo_count == 0 { "true" } else { "false" },
}
label { r#for: "toggle-all" }
input {
id: "toggle-all",
class: "toggle-all",
r#type: "checkbox",
onchange: move |_| {
let check = active_todo_count != 0;
for (_, item) in todos.make_mut().iter_mut() {
item.checked = check;
}
},
checked: if active_todo_count == 0 { "true" } else { "false" },
}
label { r#for: "toggle-all" }
}
ul { class: "todo-list",
filtered_todos.iter().map(|id| rsx!(TodoEntry {
key: "{id}",
id: *id,
todos: todos,
}))
for id in filtered_todos.iter() {
TodoEntry {
key: "{id}",
id: *id,
todos: todos,
}
}
}
(!todos.is_empty()).then(|| rsx!(
if !todos.is_empty() {
ListFooter {
active_todo_count: active_todo_count,
active_todo_text: active_todo_text,
@@ -83,7 +83,7 @@ pub fn app(cx: Scope<()>) -> Element {
todos: todos,
filter: filter,
}
))
}
}
}
PageFooter {}
@@ -172,7 +172,7 @@ pub fn TodoEntry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element {
prevent_default: "onclick"
}
}
is_editing.then(|| rsx!{
if **is_editing {
input {
class: "edit",
value: "{todo.contents}",
@@ -186,7 +186,7 @@ pub fn TodoEntry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element {
}
},
}
})
}
}
})
}
@@ -220,29 +220,27 @@ pub fn ListFooter<'a>(cx: Scope<'a, ListFooterProps<'a>>) -> Element {
}
ul { class: "filters",
for (state , state_text , url) in [
(FilterState::All, "All", "#/"),
(FilterState::Active, "Active", "#/active"),
(FilterState::Completed, "Completed", "#/completed"),
] {
(FilterState::All, "All", "#/"),
(FilterState::Active, "Active", "#/active"),
(FilterState::Completed, "Completed", "#/completed"),
] {
li {
a {
href: url,
class: selected(state),
onclick: move |_| cx.props.filter.set(state),
prevent_default: "onclick",
state_text
{state_text}
}
}
}
}
if cx.props.show_clear_completed {
cx.render(rsx! {
button {
class: "clear-completed",
onclick: move |_| cx.props.todos.make_mut().retain(|_, todo| !todo.checked),
"Clear completed"
}
})
button {
class: "clear-completed",
onclick: move |_| cx.props.todos.make_mut().retain(|_, todo| !todo.checked),
"Clear completed"
}
}
}
})