Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/boids/src/boid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ impl Boid {
points.push_str(&format!("{:.2},{:.2} ", x, y));
}

html! { <polygon points={points} fill={color} /> }
html! { <polygon {points} fill={color} /> }
}
}

Expand Down
2 changes: 1 addition & 1 deletion examples/boids/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl Component for Model {
html! {
<>
<h1 class="title">{ "Boids" }</h1>
<Simulation settings={settings.clone()} generation={generation} paused={paused} />
<Simulation settings={settings.clone()} {generation} {paused} />
{ self.view_panel() }
</>
}
Expand Down
2 changes: 1 addition & 1 deletion examples/boids/src/slider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl Component for Slider {
<div class="slider">
<label for={id.clone()} class="slider__label">{ label }</label>
<input type="range"
id={id}
{id}
class="slider__input"
min={min.to_string()} max={max.to_string()} step={step.to_string()}
oninput={onchange.reform(|data: InputData| data.value.parse().unwrap())}
Expand Down
23 changes: 11 additions & 12 deletions examples/nested_list/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,21 @@ impl Component for App {
let sub_list_link = &self.sub_list_link;

// note the use of `html_nested!` instead of `html!`.
let letters = ('A'..='C').map(
|letter| html_nested! { <ListItem name={letter.to_string()} on_hover={on_hover} /> },
);
let letters = ('A'..='C')
.map(|letter| html_nested! { <ListItem name={letter.to_string()} {on_hover} /> });

html! {
<div class="main" onmouseenter={onmouseenter}>
<div class="main" {onmouseenter}>
<h1>{ "Nested List Demo" }</h1>
<List on_hover={on_hover} weak_link={list_link}>
<ListHeader text="Calling all Rusties!" on_hover={on_hover} list_link={list_link} />
<ListItem name="Rustin" on_hover={on_hover} />
<ListItem hide=true name="Rustaroo" on_hover={on_hover} />
<ListItem name="Rustifer" on_hover={on_hover}>
<List {on_hover} weak_link={list_link}>
<ListHeader text="Calling all Rusties!" {on_hover} {list_link} />
<ListItem name="Rustin" {on_hover} />
<ListItem hide=true name="Rustaroo" {on_hover} />
<ListItem name="Rustifer" {on_hover}>
<div class="sublist">{ "Sublist!" }</div>
<List on_hover={on_hover} weak_link={sub_list_link}>
<ListHeader text="Sub Rusties!" on_hover={on_hover} list_link={sub_list_link}/>
<ListItem hide=true name="Hidden Sub" on_hover={on_hover} />
<List {on_hover} weak_link={sub_list_link}>
<ListHeader text="Sub Rusties!" {on_hover} list_link={sub_list_link}/>
<ListItem hide=true name="Hidden Sub" {on_hover} />
{ for letters }
</List>
</ListItem>
Expand Down
2 changes: 1 addition & 1 deletion examples/nested_list/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ impl Component for ListHeader {
html! {
<div
class="list-header"
onmouseover={onmouseover}
{onmouseover}
onclick={list_link.callback(|_| ListMsg::HeaderClick)}
>
{ &self.props.text }
Expand Down
2 changes: 1 addition & 1 deletion examples/nested_list/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl Component for ListItem {
})
};
html! {
<div class="list-item" onmouseover={onmouseover}>
<div class="list-item" {onmouseover}>
{ &self.props.name }
{ self.view_details() }
</div>
Expand Down
2 changes: 1 addition & 1 deletion examples/nested_list/src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl Component for List {
let onmouseover = self.props.on_hover.reform(|_| Hovered::List);
let onmouseout = self.props.on_hover.reform(|_| Hovered::None);
html! {
<div class="list-container" onmouseout={onmouseout} onmouseover={onmouseover}>
<div class="list-container" {onmouseout} {onmouseover}>
<div class={classes!("list", inactive)}>
{ self.view_header() }
<div class="items">
Expand Down
2 changes: 1 addition & 1 deletion examples/router/src/components/pagination.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl Pagination {

html! {
<li>
<a class={classes!("pagination-link", is_current_class)} aria-label={format!("Goto page {}", to_page)} onclick={onclick}>
<a class={classes!("pagination-link", is_current_class)} aria-label={format!("Goto page {}", to_page)} {onclick}>
{ to_page }
</a>
</li>
Expand Down
2 changes: 1 addition & 1 deletion examples/router/src/pages/author_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl Component for AuthorList {
html! {
<div class="tile is-parent">
<div class="tile is-child">
<AuthorCard seed={seed} />
<AuthorCard {seed} />
</div>
</div>
}
Expand Down
2 changes: 1 addition & 1 deletion examples/router/src/pages/post_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl Component for PostList {
<h2 class="subtitle">{ "All of our quality writing in one place" }</h2>
{ self.view_posts() }
<Pagination
page={page}
{page}
total_pages={TOTAL_PAGES}
on_switch_page={self.link.callback(Msg::ShowPage)}
/>
Expand Down
2 changes: 1 addition & 1 deletion examples/store/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl Component for Model {
<TextInput value="New post" onsubmit={self.link.callback(Msg::CreatePost)} />

<div>
{ for self.post_ids.iter().map(|&id| html!{ <Post key={id} id={id} /> }) }
{ for self.post_ids.iter().map(|&id| html!{ <Post key={id} {id} /> }) }
</div>
</>
}
Expand Down
2 changes: 1 addition & 1 deletion examples/todomvc/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ impl Model {
class.push(" completed");
}
html! {
<li class={class}>
<li {class}>
<div class="view">
<input
type="checkbox"
Expand Down
9 changes: 9 additions & 0 deletions packages/yew-macro/src/html_tree/html_dashed_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,12 @@ impl Stringify for HtmlDashedName {
self.to_lit_str().stringify()
}
}

impl From<Ident> for HtmlDashedName {
fn from(name: Ident) -> Self {
HtmlDashedName {
name,
extended: vec![],
}
}
}
47 changes: 46 additions & 1 deletion packages/yew-macro/src/props/prop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ use std::{
ops::{Deref, DerefMut},
};
use syn::{
braced,
parse::{Parse, ParseStream},
Block, Expr, ExprBlock, Stmt, Token,
token::Brace,
Block, Expr, ExprBlock, ExprPath, Stmt, Token,
};

pub struct Prop {
Expand All @@ -16,6 +18,49 @@ pub struct Prop {
}
impl Parse for Prop {
fn parse(input: ParseStream) -> syn::Result<Self> {
if input.peek(Brace) {
Self::parse_shorthand_prop_assignment(input)
} else {
Self::parse_prop_assignment(input)
}
}
}

/// Helpers for parsing props
impl Prop {
/// Parse a prop using the shorthand syntax `{value}`, short for `value={value}`
/// This only allows for labels with no hyphens, as it would otherwise create
/// an ambiguity in the syntax
fn parse_shorthand_prop_assignment(input: ParseStream) -> syn::Result<Self> {
let value;
let _brace = braced!(value in input);
let expr = value.parse::<Expr>()?;
let label = if let Expr::Path(ExprPath {
ref attrs,
qself: None,
ref path,
}) = expr
{
if let (Some(ident), true) = (path.get_ident(), attrs.is_empty()) {
syn::Result::Ok(HtmlDashedName::from(ident.clone()))
} else {
Err(syn::Error::new_spanned(
path,
"only simple identifiers are allowed in the shorthand property syntax",
))
}
} else {
return Err(syn::Error::new_spanned(
expr,
"missing label for property value. If trying to use the shorthand property syntax, only identifiers may be used",
));
}?;

Ok(Self { label, value: expr })
}

/// Parse a prop of the form `label={value}`
fn parse_prop_assignment(input: ParseStream) -> syn::Result<Self> {
let label = input.parse::<HtmlDashedName>()?;
let equals = input.parse::<Token![=]>().map_err(|_| {
syn::Error::new_spanned(
Expand Down
7 changes: 6 additions & 1 deletion packages/yew-macro/tests/html_macro/component-fail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ fn compile_fail() {
// using `children` as a prop while simultaneously passing children using the syntactic sugar
let children = ChildrenRenderer::new(vec![html_nested! { <Child int=0 /> }]);
html! {
<ChildContainer children={children}>
<ChildContainer {children}>
<Child int=1 />
</ChildContainer>
};
Expand All @@ -112,6 +112,11 @@ fn compile_fail() {
<span>{ 1 }</span>
<span>{ 2 }</span>
};

html! { <Child {std::f64::consts::PI} /> };
html! { <Child {7 + 6} /> };
html! { <Child {children.len()} /> };

}

fn main() {}
24 changes: 21 additions & 3 deletions packages/yew-macro/tests/html_macro/component-fail.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -154,17 +154,35 @@ error: only one root html element is allowed (hint: you can wrap multiple html e
| ^^^^^^^^^^^^^^^

error: cannot specify the `children` prop when the component already has children
--> $DIR/component-fail.rs:106:25
--> $DIR/component-fail.rs:106:26
|
106 | <ChildContainer children={children}>
| ^^^^^^^^
106 | <ChildContainer {children}>
| ^^^^^^^^

error: only one root html element is allowed (hint: you can wrap multiple html elements in a fragment `<></>`)
--> $DIR/component-fail.rs:113:9
|
113 | <span>{ 2 }</span>
| ^^^^^^^^^^^^^^^^^^

error: only simple identifiers are allowed in the shorthand property syntax
--> $DIR/component-fail.rs:116:21
|
116 | html! { <Child {std::f64::consts::PI} /> };
| ^^^^^^^^^^^^^^^^^^^^

error: missing label for property value. If trying to use the shorthand property syntax, only identifiers may be used
--> $DIR/component-fail.rs:117:21
|
117 | html! { <Child {7 + 6} /> };
| ^^^^^

error: missing label for property value. If trying to use the shorthand property syntax, only identifiers may be used
--> $DIR/component-fail.rs:118:21
|
118 | html! { <Child {children.len()} /> };
| ^^^^^^^^^^^^^^

error[E0425]: cannot find value `blah` in this scope
--> $DIR/component-fail.rs:69:25
|
Expand Down
15 changes: 15 additions & 0 deletions packages/yew-macro/tests/html_macro/component-pass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@ fn compile_pass() {
<Child int=1 string={name_expr} />
};

let string = "child";
let int = 1;
html! {
<Child {int} {string} />
};

html! {
<>
<Child int=1 />
Expand All @@ -193,6 +199,15 @@ fn compile_pass() {
</>
};

let int = 1;
let node_ref = NodeRef::default();
html! {
<>
<Child {int} ref={node_ref} />
</>
};


let props = <Container as Component>::Properties::default();
html! {
<>
Expand Down
2 changes: 1 addition & 1 deletion packages/yew-macro/tests/html_macro/html-element-pass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ fn compile_pass() {
</svg>
<img class={classes!("avatar", "hidden")} src="http://pic.com" />
<img class="avatar hidden" />
<button onclick={&onclick} onclick={onclick} />
<button onclick={&onclick} {onclick} />
<a href="http://google.com" />
<custom-tag-a>
<custom-tag-b />
Expand Down
2 changes: 1 addition & 1 deletion packages/yew-router/tests/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ fn component() -> Html {
Routes::Home => html! {
<>
<div id="result">{"Home"}</div>
<a onclick={onclick}>{"click me"}</a>
<a {onclick}>{"click me"}</a>
</>
},
Routes::No { id } => html! { <No id={*id} /> },
Expand Down
2 changes: 1 addition & 1 deletion packages/yew/src/functional/hooks/use_effect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ struct UseEffect<Destructor> {
/// };
///
/// html! {
/// <button onclick={onclick}>{ format!("Increment to {}", *counter) }</button>
/// <button {onclick}>{ format!("Increment to {}", *counter) }</button>
/// }
/// }
/// ```
Expand Down
4 changes: 2 additions & 2 deletions packages/yew/src/functional/hooks/use_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ use std::{cell::RefCell, rc::Rc};
///
/// html! {
/// <div>
/// <input onchange={onchange} value={(*message).clone()} />
/// <button onclick={onclick}>{ "Send" }</button>
/// <input {onchange} value={(*message).clone()} />
/// <button {onclick}>{ "Send" }</button>
/// </div>
/// }
/// }
Expand Down
2 changes: 1 addition & 1 deletion packages/yew/src/functional/hooks/use_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct UseState<T2> {
///
/// html! {
/// <div>
/// <button onclick={onclick}>{ "Increment value" }</button>
/// <button {onclick}>{ "Increment value" }</button>
/// <p>
/// <b>{ "Current value: " }</b>
/// { *counter }
Expand Down
2 changes: 1 addition & 1 deletion packages/yew/tests/use_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ fn use_context_update_works() {
<MyContextProvider context={Rc::new((*ctx).clone())}>
<RenderCounter id="test-0">
<ContextOutlet id="test-1"/>
<ContextOutlet id="test-2" magic={magic}/>
<ContextOutlet id="test-2" {magic}/>
</RenderCounter>
</MyContextProvider>
};
Expand Down
2 changes: 1 addition & 1 deletion packages/yew/tests/use_effect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ fn use_effect_destroys_on_component_drop() {
if *show {
let effect_called: Rc<dyn Fn()> = { Rc::new(move || show.set(false)) };
html! {
<UseEffectComponent destroy_called={props.destroy_called.clone()} effect_called={effect_called} />
<UseEffectComponent destroy_called={props.destroy_called.clone()} {effect_called} />
}
} else {
html! {
Expand Down
3 changes: 2 additions & 1 deletion website/docs/concepts/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ HTML-like code using Rust functions can become quite messy, so Yew provides a ma
for declaring HTML and SVG nodes (as well as attaching attributes and event listeners to them) and a
convenient way to render child components. The macro is somewhat similar to React's JSX (the
differences in programming language aside).
One difference is that Yew provides a shorthand syntax for properties, similar to Svelte, where instead of writing `onclick={onclick}`, you can just write `{onclick}`.

```rust
impl Component for MyComponent {
Expand All @@ -57,7 +58,7 @@ impl Component for MyComponent {
fn view(&self) -> Html {
let onclick = self.link.callback(|_| Msg::Click);
html! {
<button onclick={onclick}>{ self.props.button_text }</button>
<button {onclick}>{ self.props.button_text }</button>
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions website/docs/concepts/components/callbacks.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ A simple use of a callback might look something like this:
```rust
let onclick = self.link.callback(|_| Msg::Clicked);
html! {
<button onclick={onclick}>{ "Click" }</button>
<button {onclick}>{ "Click" }</button>
}
```

Expand All @@ -89,7 +89,7 @@ let onkeypress = self.link.batch_callback(|event| {
});

html! {
<input type="text" onkeypress={onkeypress} />
<input type="text" {onkeypress} />
}
```

Expand Down
Loading