Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lay foundation for a statically typed vdom #2396

Closed
wants to merge 56 commits into from
Closed
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
e4eaac6
foundations of statically typed vdom
ranile Jan 22, 2022
d95ca3e
add tiny test
ranile Jan 22, 2022
6926723
listeners
ranile Jan 23, 2022
c25c612
a proper test
ranile Jan 23, 2022
60af6ab
generate all elements
ranile Jan 23, 2022
03250f2
-, not _
ranile Jan 23, 2022
507c2ee
it works
ranile Jan 23, 2022
4a885d4
we don't build typings for <html>
ranile Jan 23, 2022
6c431ad
make the html! macro work
ranile Jan 23, 2022
362aee9
please clippy and fmt
ranile Jan 23, 2022
3a0b4d5
reduce the number of compile errors
ranile Jan 24, 2022
b38cce5
fix the macro?
ranile Jan 25, 2022
36fdea5
fix tests
ranile Jan 25, 2022
09ae6c1
some clippy
ranile Jan 25, 2022
9f81614
disabled is not valid for <a>
ranile Jan 25, 2022
7399aa2
bool conversion
ranile Jan 25, 2022
1a5293d
try to make things compile
ranile Jan 25, 2022
9c7c867
more things compile
ranile Jan 26, 2022
a8539cf
text area uses children not value attribute
ranile Jan 26, 2022
c7870aa
clippy, fmt
ranile Jan 26, 2022
59297b9
suspense_not_suspended_at_start passes
ranile Jan 26, 2022
3dd8f6a
svg is untyped
ranile Jan 26, 2022
c24bb2d
yeet special treatment for textarea
ranile Jan 26, 2022
e35f193
examples should compile
ranile Jan 26, 2022
f19a28d
most of CI should be green
ranile Jan 26, 2022
9f05bc5
Merge branch 'master' into typed-vdom-foundation
ranile Jan 28, 2022
f43d0f5
fix doc tests
ranile Jan 28, 2022
dbcc1de
fix lints
ranile Jan 28, 2022
f117b84
clippy, fmt
ranile Jan 28, 2022
075004d
should fix doc tests?
ranile Jan 28, 2022
7430230
html-dashed-name is not an ident + revert unneeded change
ranile Jan 31, 2022
bda5ac8
raw idents or HtmlDashedName for prop labels
ranile Jan 31, 2022
edb1ee5
uncomment aria-labels attrs
ranile Jan 31, 2022
36152a4
fmt
ranile Jan 31, 2022
c8d98a8
fix error
ranile Jan 31, 2022
3960035
fmt
ranile Jan 31, 2022
89499bd
fix doc test
ranile Feb 1, 2022
23fb6a5
please fmt
ranile Feb 1, 2022
5d42e49
please
ranile Feb 3, 2022
c0e6ffe
please v2
ranile Feb 3, 2022
1035b7e
will this fucking work?
ranile Feb 5, 2022
50dcb36
another try
ranile Feb 5, 2022
fbfebfa
commit file from CI
ranile Feb 5, 2022
cec52f8
Revert "another try"
ranile Feb 5, 2022
e355d96
Merge branch 'master' into typed-vdom-foundation
ranile Feb 11, 2022
f5b252b
fix typo in docs, ignore failing test
ranile Feb 11, 2022
9173042
Fix IntoPropValue impl for bool
ranile Feb 16, 2022
3c473f9
hygiene + fragment key
ranile Feb 16, 2022
78cdf16
support for ARIA attributes
ranile Feb 16, 2022
5bec200
opps, we ain't on 0.58
ranile Feb 16, 2022
95cb65e
fix macro test
ranile Feb 16, 2022
f51003c
Merge branch 'master' into typed-vdom-foundation
ranile Feb 22, 2022
741c29b
don't add global fields in literally every element props struct
ranile Feb 22, 2022
8be6eec
clippy & fmt
ranile Feb 22, 2022
06d4bd8
fix fuck up
ranile Feb 22, 2022
3ee3b94
Merge branch 'master' into typed-vdom-foundation
ranile Mar 8, 2022
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} fill={color} /> }
html! { <@{"polygon"} {points} fill={color} /> }
}
}

Expand Down
4 changes: 2 additions & 2 deletions examples/boids/src/simulation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ impl Component for Simulation {
let view_box = format!("0 0 {} {}", SIZE.x, SIZE.y);

html! {
<svg class="simulation-window" viewBox={view_box}>
<@{"svg"} class="simulation-window" viewBox={view_box}>
{ for self.boids.iter().map(Boid::render) }
</svg>
</@>
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub fn ChessboardCard(props: &Props) -> Html {

html! {
<div class="chess-board-card-container">
<div class={classes!("card", flipped.then(|| "flipped"))} {onclick}>
<div class={classes!("card", flipped.then(|| "flipped"))} onclick={onclick}>
<img class="front" src={get_link_by_cardname} alt="card" />
<img class="back" src="public/back.png" alt="card" />
</div>
Expand Down
5 changes: 3 additions & 2 deletions examples/js_callback/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ impl Component for App {
let input: HtmlTextAreaElement = e.target_unchecked_into();
Msg::Payload(input.value())
})}
value={self.payload.clone()}
/>
>
{self.payload.clone()}
</textarea>
<button onclick={ctx.link().callback(|_| Msg::Payload(bindings::get_payload()))}>
{ "Get the payload!" }
</button>
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 @@ -31,7 +31,7 @@ impl Component for Pagination {

fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<nav class="pagination is-right" role="navigation" aria-label="pagination">
<nav class="pagination is-right" role="navigation" aria_label="pagination">
{ self.view_relnav_buttons(ctx.props()) }
<ul class="pagination-list">
{ self.view_links(ctx.props()) }
Expand Down
2 changes: 1 addition & 1 deletion examples/router/src/components/progress_delay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl Component for ProgressDelay {
fn view(&self, _ctx: &Context<Self>) -> Html {
let value = self.value;
html! {
<progress class="progress is-primary" value={value.to_string()} max=1.0>
<progress class="progress is-primary" value={value.to_string()} max="1.0">
{ format!("{:.0}%", 100.0 * value) }
</progress>
}
Expand Down
10 changes: 5 additions & 5 deletions examples/router/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,17 @@ impl App {
let active_class = if !navbar_active { "is-active" } else { "" };

html! {
<nav class="navbar is-primary" role="navigation" aria-label="main navigation">
<nav class="navbar is-primary" aria_roledescription="navigation" aria_label="main navigation">
<div class="navbar-brand">
<h1 class="navbar-item is-size-3">{ "Yew Blog" }</h1>

<button class={classes!("navbar-burger", "burger", active_class)}
aria-label="menu" aria-expanded="false"
aria_label="menu" aria_expanded="false"
onclick={link.callback(|_| Msg::ToggleNavbar)}
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria_hidden="true"></span>
<span aria_hidden="true"></span>
<span aria_hidden="true"></span>
</button>
</div>
<div class={classes!("navbar-menu", active_class)}>
Expand Down
2 changes: 1 addition & 1 deletion examples/suspense/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fn app_content() -> HtmlResult {

Ok(html! {
<div class="content-area">
<textarea value={value.to_string()} oninput={on_text_input}></textarea>
<textarea oninput={on_text_input}>{value.to_string()}</textarea>
<div class="action-area">
<button onclick={on_take_a_break}>{"Take a break!"}</button>
<div class="hint">{"You can take a break at anytime"}<br />{"and your work will be preserved."}</div>
Expand Down
1 change: 1 addition & 0 deletions packages/yew-macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ proc-macro = true

[dependencies]
boolinator = "2"
convert_case = "0.5"
lazy_static = "1"
proc-macro-error = "1"
proc-macro2 = "1"
Expand Down
2 changes: 1 addition & 1 deletion packages/yew-macro/Makefile.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tasks.test]
clear = true
toolchain = ""
toolchain = "1.56"
command = "cargo"
# test target can be optionally specified like `cargo make test html_macro`,
args = ["test", "${@}"]
Expand Down
33 changes: 25 additions & 8 deletions packages/yew-macro/src/html_tree/html_component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{
AngleBracketedGenericArguments, GenericArgument, Path, PathArguments, PathSegment, Token, Type,
TypePath,
parse_quote_spanned, AngleBracketedGenericArguments, GenericArgument, Path, PathArguments,
PathSegment, Token, Type, TypePath,
};

pub struct HtmlComponent {
ty: Type,
props: ComponentProps,
children: HtmlChildrenTree,
pub ty: Type,
pub props: ComponentProps,
pub children: HtmlChildrenTree,
}

impl PeekValue<()> for HtmlComponent {
Expand All @@ -26,6 +26,14 @@ impl PeekValue<()> for HtmlComponent {
}
}

fn element_or_ty(ty: &Type) -> Type {
if crate::typed_vdom::element_names().contains(&&*ty.to_token_stream().to_string()) {
parse_quote_spanned! {ty.span()=> ::yew::virtual_dom::typings::#ty}
} else {
ty.clone()
}
}

impl Parse for HtmlComponent {
fn parse(input: ParseStream) -> syn::Result<Self> {
if HtmlComponentClose::peek(input.cursor()).is_some() {
Expand All @@ -42,7 +50,7 @@ impl Parse for HtmlComponent {
// Return early if it's a self-closing tag
if open.is_self_closing() {
return Ok(HtmlComponent {
ty: open.ty,
ty: element_or_ty(&open.ty),
props: open.props,
children: HtmlChildrenTree::new(),
});
Expand Down Expand Up @@ -77,7 +85,7 @@ impl Parse for HtmlComponent {
}

Ok(HtmlComponent {
ty: open.ty,
ty: element_or_ty(&open.ty),
props: open.props,
children,
})
Expand All @@ -91,14 +99,23 @@ impl ToTokens for HtmlComponent {
props,
children,
} = self;
// incredibly jank but works ¯\_(ツ)_/¯
let is_element = {
let ty_str = ty.to_token_stream().to_string();
let ty_str = ty_str.split("::").filter_map(|it| {
let trimmed = it.trim();
if trimmed.is_empty() { None } else { Some(trimmed) }
}).take(3).collect::<Vec<_>>();
ty_str == vec!["yew", "virtual_dom", "typings"]
};

let props_ty = quote_spanned!(ty.span()=> <#ty as ::yew::html::BaseComponent>::Properties);
let children_renderer = if children.is_empty() {
None
} else {
Some(quote! { ::yew::html::ChildrenRenderer::new(#children) })
};
let build_props = props.build_properties_tokens(&props_ty, children_renderer);
let build_props = props.build_properties_tokens(&props_ty, children_renderer, is_element);

let special_props = props.special();
let node_ref = if let Some(node_ref) = &special_props.node_ref {
Expand Down
6 changes: 4 additions & 2 deletions packages/yew-macro/src/html_tree/html_dashed_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub struct HtmlDashedName {
impl HtmlDashedName {
/// Checks if this name is equal to the provided item (which can be anything implementing
/// `Into<String>`).
#[allow(dead_code)]
pub fn eq_ignore_ascii_case<S>(&self, other: S) -> bool
where
S: Into<String>,
Expand Down Expand Up @@ -76,7 +77,7 @@ impl Parse for HtmlDashedName {
let name = input.call(Ident::parse_any)?;
let mut extended = Vec::new();
while input.peek(Token![-]) {
extended.push((input.parse::<Token![-]>()?, input.parse::<Ident>()?));
extended.push((input.parse::<Token![-]>()?, Ident::parse_any(input)?));
}

Ok(HtmlDashedName { name, extended })
Expand All @@ -89,7 +90,8 @@ impl ToTokens for HtmlDashedName {
let dashes = extended.iter().map(|(dash, _)| quote! {#dash});
let idents = extended.iter().map(|(_, ident)| quote! {#ident});
let extended = quote! { #(#dashes #idents)* };
tokens.extend(quote! { #name #extended });
let label = quote! { #name #extended };
tokens.extend(label.to_token_stream())
}
}

Expand Down
29 changes: 4 additions & 25 deletions packages/yew-macro/src/html_tree/html_element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ impl ToTokens for HtmlElement {
quote! { ::yew::virtual_dom::listeners::Listeners::None }
} else {
let listeners_it = listeners.iter().map(|Prop { label, value, .. }| {
let name = &label.name;
let name = &label.name();
quote! {
::yew::html::#name::Wrapper::__macro_new(#value)
}
Expand Down Expand Up @@ -313,19 +313,6 @@ impl ToTokens for HtmlElement {
)
}
}
"textarea" => {
quote! {
::std::convert::Into::<::yew::virtual_dom::VNode>::into(
::yew::virtual_dom::VTag::__new_textarea(
#value,
#node_ref,
#key,
#attributes,
#listeners,
),
)
}
}
_ => {
quote! {
::std::convert::Into::<::yew::virtual_dom::VNode>::into(
Expand Down Expand Up @@ -387,17 +374,9 @@ impl ToTokens for HtmlElement {
#[allow(clippy::redundant_clone, unused_braces, clippy::let_and_return)]
let mut #vtag = match ::std::convert::AsRef::<::std::primitive::str>::as_ref(&#vtag_name) {
"input" => {
::yew::virtual_dom::VTag::__new_textarea(
#value,
#node_ref,
#key,
#attributes,
#listeners,
)
}
"textarea" => {
::yew::virtual_dom::VTag::__new_textarea(
::yew::virtual_dom::VTag::__new_input(
#value,
#checked,
#node_ref,
#key,
#attributes,
Expand Down Expand Up @@ -449,7 +428,7 @@ impl ToTokens for HtmlElement {
}
}

fn wrap_attr_prop(prop: &Prop) -> TokenStream {
fn wrap_attr_prop(prop: &Prop<false>) -> TokenStream {
let value = prop.value.optimize_literals();
quote_spanned! {value.span()=>
::yew::html::IntoPropValue::<
Expand Down
4 changes: 2 additions & 2 deletions packages/yew-macro/src/html_tree/html_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,12 @@ impl Parse for HtmlListProps {
let key = if input.is_empty() {
None
} else {
let prop: Prop = input.parse()?;
let prop: Prop<true> = input.parse()?;
if !input.is_empty() {
return Err(input.error("only a single `key` prop is allowed on a fragment"));
}

if prop.label.to_ascii_lowercase_string() != "key" {
if prop.label.to_string().to_ascii_lowercase() != "key" {
return Err(syn::Error::new_spanned(
prop.label,
"fragments only accept the `key` prop",
Expand Down
Loading