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

Conversation

ranile
Copy link
Member

@ranile ranile commented Jan 22, 2022

Description

Pulls useful code from #2369

The implementation generates components for each HTML element using a new macro, generate_element!. Every attribute and listener is passed as props to the component. The component's view method creates a VTag based on the passed data,

Special casing for textarea in VTag has also been removed. Previously, we used value attribute on textarea, which is not part of standard. See MDN docs:

Default content entered between the opening and closing tags. <textarea> does not support the value attribute.

Limitations

Right now, there are some limitations (to be resolved in the future):

  • Typed support for SVG elements is not implemented. The MDN documentation does not list every attribute for the elements so I can't generate code from the information on that page. If there's a resource for that, I would like to hear it.
  • data attributes not yet supported with static typing.
  • Custom elements can't be used directly with html! macro

Workaround

These limitations can be bypassed by opting out of static typing. This is done by using dynamic tags.

Checklist

  • I have run cargo make pr-flow CI passes
  • I have reviewed my own code
  • I have added tests

@ranile ranile added A-yew Area: The main yew crate A-yew-macro Area: The yew-macro crate labels Jan 22, 2022
@ranile ranile added this to the v0.20 milestone Jan 22, 2022
@ranile ranile mentioned this pull request Jan 22, 2022
github-actions[bot]
github-actions bot previously approved these changes Jan 22, 2022
@github-actions
Copy link

github-actions bot commented Jan 22, 2022

Visit the preview URL for this PR (updated for commit 3ee3b94):

https://yew-rs--pr2396-typed-vdom-foundatio-2dnyy8kt.web.app

(expires Tue, 15 Mar 2022 14:07:48 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

github-actions[bot]
github-actions bot previously approved these changes Jan 23, 2022
github-actions[bot]
github-actions bot previously approved these changes Jan 23, 2022
github-actions[bot]
github-actions bot previously approved these changes Jan 23, 2022
It clashes with export of html! macro and it's basically never used in code anyway
github-actions[bot]
github-actions bot previously approved these changes Jan 23, 2022
github-actions[bot]
github-actions bot previously approved these changes Jan 23, 2022
github-actions[bot]
github-actions bot previously approved these changes Jan 23, 2022
@ranile
Copy link
Member Author

ranile commented Feb 17, 2022

Marking this as draft because I'll need to solve this at the macro level somehow, not by creating hundreds of components. I ran twiggy the diff shows the size is increased by the components (which is fair):

$ twiggy diff pr.wasm master.wasm -n 50
$ twiggy diff dist/index-b9871236aa4f6b69_bg.wasm master-dist/index-fc3d6b16858b68ee_bg.wasm -n 50
 Delta Bytes │ Item
─────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
      -59281 ┊ "function names" subsection
      -52592 ┊ yew::virtual_dom::typings::ImgProps::into_data::h798a20522d09ae59
      -51122 ┊ yew::virtual_dom::typings::ButtonProps::into_data::h6587679c2d8fbd63
      -50534 ┊ yew::virtual_dom::typings::AProps::into_data::he567b5a109edbea8
      -48770 ┊ yew::virtual_dom::typings::ProgressProps::into_data::h7e52ff2fa1703ea7
      -48182 ┊ yew::virtual_dom::typings::LiProps::into_data::h3271d4721a9f6d7f
      -47888 ┊ yew::virtual_dom::typings::AsideProps::into_data::h0339672d07a9b47e
      -21554 ┊ core::ptr::drop_in_place<yew::virtual_dom::typings::ImgProps>::h5d224db68655c387
      -21536 ┊ <alloc::rc::Rc<T> as core::ops::drop::Drop>::drop::h9b8962da91ac57fc
      -21059 ┊ core::ptr::drop_in_place<yew::virtual_dom::typings::ButtonProps>::h160df357b25170d2
      -20864 ┊ <alloc::rc::Rc<T> as core::ops::drop::Drop>::drop::h8f18339a1a95e5b7
      -20861 ┊ core::ptr::drop_in_place<yew::virtual_dom::typings::AProps>::h503b1b99f6174d02
      -20502 ┊ <yew::virtual_dom::typings::img as yew::html::component::Component>::view::h401eeed7fbad9be9
      -20288 ┊ <alloc::rc::Rc<T> as core::ops::drop::Drop>::drop::h191bb4be03cbc4e6
      -20267 ┊ core::ptr::drop_in_place<yew::virtual_dom::typings::ProgressProps>::h135fba91a34e4934
      -20069 ┊ core::ptr::drop_in_place<yew::virtual_dom::typings::LiProps>::h2066807df7209364
      -19970 ┊ core::ptr::drop_in_place<yew::virtual_dom::typings::BProps>::h100f9486104434ce
      -19807 ┊ <yew::virtual_dom::typings::button as yew::html::component::Component>::view::h413818a45e71c222
      -19555 ┊ <yew::virtual_dom::typings::a as yew::html::component::Component>::view::h432e6172d107e2c4
      -18695 ┊ <yew::virtual_dom::typings::progress as yew::html::component::Component>::view::he925a64d6647d9c9
      -18443 ┊ <yew::virtual_dom::typings::li as yew::html::component::Component>::view::hb6c9c2f0d3ba96e8
      -18278 ┊ <yew::virtual_dom::typings::article as yew::html::component::Component>::view::h39f382968d97614f
      -18278 ┊ <yew::virtual_dom::typings::b as yew::html::component::Component>::view::h213e57b738ff5038
      -18278 ┊ <yew::virtual_dom::typings::br as yew::html::component::Component>::view::h95ad701e5a7832e6
      -18278 ┊ <yew::virtual_dom::typings::div as yew::html::component::Component>::view::h801c28be4036c64d
      -18278 ┊ <yew::virtual_dom::typings::figure as yew::html::component::Component>::view::h526121785f85a749
      -18278 ┊ <yew::virtual_dom::typings::footer as yew::html::component::Component>::view::hd3d2b929813848e0
      -18278 ┊ <yew::virtual_dom::typings::h1 as yew::html::component::Component>::view::hcbd8d3cc1ee219c0
      -18278 ┊ <yew::virtual_dom::typings::h2 as yew::html::component::Component>::view::h437b7bbcb9150452
      -18278 ┊ <yew::virtual_dom::typings::i as yew::html::component::Component>::view::h6ea9d08af8ca122e
      -18278 ┊ <yew::virtual_dom::typings::main as yew::html::component::Component>::view::h97305f08adcf824c
      -18278 ┊ <yew::virtual_dom::typings::nav as yew::html::component::Component>::view::hf8508a87f7e3d7a9
      -18278 ┊ <yew::virtual_dom::typings::p as yew::html::component::Component>::view::h429043d55845c2f2
      -18278 ┊ <yew::virtual_dom::typings::section as yew::html::component::Component>::view::h12f4c26e88697c5b
      -18278 ┊ <yew::virtual_dom::typings::span as yew::html::component::Component>::view::hc9c549a70fa0b9b4
      -18278 ┊ <yew::virtual_dom::typings::strong as yew::html::component::Component>::view::h385cdbe34dcc9e7d
      -18278 ┊ <yew::virtual_dom::typings::sup as yew::html::component::Component>::view::h84919961802c2aeb
      -18278 ┊ <yew::virtual_dom::typings::ul as yew::html::component::Component>::view::heb9923d3b7bdf10d
      -12588 ┊ <T as yew::html::component::BaseComponent>::view::h157501dcaafecafb
      -11929 ┊ <yew::html::component::lifecycle::CompStateInner<COMP> as yew::html::component::lifecycle::Stateful>::props_changed::hc21fbc756545a0dd
      -11706 ┊ <yew::html::component::lifecycle::CompStateInner<COMP> as yew::html::component::lifecycle::Stateful>::props_changed::h1c5de16122339e7a
      -11494 ┊ <yew::html::component::lifecycle::CompStateInner<COMP> as yew::html::component::lifecycle::Stateful>::props_changed::h3adef91bebc8b9e0
      -11122 ┊ <yew::html::component::lifecycle::CompStateInner<COMP> as yew::html::component::lifecycle::Stateful>::props_changed::hf77e11e4a693327f
      -11086 ┊ <yew::html::component::lifecycle::CompStateInner<COMP> as yew::html::component::lifecycle::Stateful>::props_changed::h67e37a0c99bdc933
      -11025 ┊ <yew::html::component::lifecycle::CompStateInner<COMP> as yew::html::component::lifecycle::Stateful>::props_changed::h08cd1c3efe8fb137
      -11025 ┊ <yew::html::component::lifecycle::CompStateInner<COMP> as yew::html::component::lifecycle::Stateful>::props_changed::h1bebdee15cc56576
      -11025 ┊ <yew::html::component::lifecycle::CompStateInner<COMP> as yew::html::component::lifecycle::Stateful>::props_changed::h2d055a7357f874c1
      -11025 ┊ <yew::html::component::lifecycle::CompStateInner<COMP> as yew::html::component::lifecycle::Stateful>::props_changed::h3f5a733a8cd2304c
      -11025 ┊ <yew::html::component::lifecycle::CompStateInner<COMP> as yew::html::component::lifecycle::Stateful>::props_changed::h43f6817be2c167c8
      -11025 ┊ <yew::html::component::lifecycle::CompStateInner<COMP> as yew::html::component::lifecycle::Stateful>::props_changed::h8c3b42c99b73aa3c
     -283829 ┊ ... and 2404 more.
    -1433163 ┊ Σ [2454 Total Rows]

When I tried to run cargo expand in typings,rs from my IDE, it almost crashed. Needless to say, the generated code is too much

github-actions[bot]
github-actions bot previously approved these changes Feb 22, 2022
github-actions[bot]
github-actions bot previously approved these changes Feb 22, 2022
github-actions[bot]
github-actions bot previously approved these changes Feb 22, 2022
@cecton
Copy link
Member

cecton commented Feb 23, 2022

I'm unsubscribing for now but please let me know when you have interesting updates or you need a review!

@WorldSEnder
Copy link
Member

What if this strictly typed dom would only be used to typecheck the declared html!{} but not actually when producing the runtime output, i.e a compile-time only expression that is eliminated by the compiler? That should keep the code-size in check. The downside I can see is that it's somewhere in between components and what we currently have, i.e. no specific VChild<Input> et al, but there'd still be errors when wrongly using html!{ <input non-existing-attribute="value" /> }.

@ranile
Copy link
Member Author

ranile commented Mar 7, 2022

This PR became a thing because of my other (properties) PR. I had the code ready to get components to be built so I did it. At the moment, the thing that is preventing this to move forward is the if let Some(value) = prop checks to avoid allocating huge arrays.

While writing this comment, I got the idea of using a function instead of putting the checks in the component code. I'll try that soon

# Conflicts:
#	packages/yew-router/src/components/link.rs
#	packages/yew/src/virtual_dom/vlist.rs
#	packages/yew/src/virtual_dom/vportal.rs
#	packages/yew/src/virtual_dom/vtag.rs
@ranile
Copy link
Member Author

ranile commented Mar 8, 2022

Does anyone know why these tests are failing: https://github.com/yewstack/yew/runs/5465453730?check_suite_focus=true

The failures seem to be coming out of bundle_dom tests. @WorldSEnder can you have a look?

@WorldSEnder
Copy link
Member

WorldSEnder commented Mar 8, 2022

My guess is that ref-handling is different when using components vs using VTag directly. Would have to have a closer look, but components may incorrectly forget to unbind refs.

@ranile
Copy link
Member Author

ranile commented Mar 8, 2022

This is basically what the generated code looks like:

impl ::yew::Component for #element_name {
    type Message = ();
    type Properties = #props_ident;

    fn create(_ctx: &::yew::html::Context<Self>) -> Self {
        Self
    }

    fn view(&self, ctx: &::yew::html::Context<Self>) -> ::yew::html::Html {
        #[allow(unused_mut)]
        let mut element = ctx.props().clone().into_data();

        ::std::convert::Into::<::yew::virtual_dom::VNode>::into({ 
            ::yew::virtual_dom::VTag::__new_other(
                ::std::stringify!(#element_name).into(),
                element.node_ref,
                element.key,
                ::yew::virtual_dom::Attributes::IndexMap(element.attributes.into_iter().collect()),
                ::yew::virtual_dom::Listeners::Pending(element.listeners.into_boxed_slice()),
                ::yew::virtual_dom::VList::with_children(element.children, ::std::option::Option::None),
            )
        })
    }
}

Props have this node_ref:

#[prop_or_default]
pub node_ref: ::std::option::Option::<::yew::NodeRef>,

Props get converted into data like so:

impl #props_ident {
    fn into_data(self) -> ::yew::virtual_dom::typings::ElementData {

        ::yew::virtual_dom::typings::ElementData {
            node_ref: ::std::option::Option::unwrap_or_default(self.node_ref.clone()),
            attributes: {
                let mut attrs = ::std::collections::HashMap::new();
                #(#attr_if_lets)*
                attrs
            },
            listeners: {
                let mut listeners = ::std::vec![];
                #(#listeners_if_lets)*
                listeners
            },
            key: self.key.clone(),
            children: self.children.into_iter().collect(),
        }
    }
}

I'm not sure how the adding a component could change the behavior of node ref.

This is what the generated code looks like:
#[allow(non_camel_case_types)]
pub struct button;
#[derive(::std::default::Default, ::std::clone::Clone, ::std::fmt::Debug, ::yew::html::Properties, ::std::cmp::PartialEq)]
pub struct ButtonProps {
    #[prop_or_default] pub __globals: ::yew::virtual_dom::typings::globals::Globals,
    #[prop_or_default] pub node_ref: ::std::option::Option::<::yew::NodeRef>,
    #[prop_or_default] pub key: ::std::option::Option::<::yew::virtual_dom::Key>,
    #[prop_or_default] pub children: ::yew::Children,
    #[prop_or_default] pub r#autofocus: ::std::option::Option::<AttrValue>,
    #[prop_or_default] pub r#disabled: ::std::option::Option::<AttrValue>,
    #[prop_or_default] pub r#form: ::std::option::Option::<AttrValue>,
    #[prop_or_default] pub r#formaction: ::std::option::Option::<AttrValue>,
    #[prop_or_default] pub r#formenctype: ::std::option::Option::<AttrValue>,
    #[prop_or_default] pub r#formmethod: ::std::option::Option::<AttrValue>,
    #[prop_or_default] pub r#formnovalidate: ::std::option::Option::<AttrValue>,
    #[prop_or_default] pub r#formtarget: ::std::option::Option::<AttrValue>,
    #[prop_or_default] pub r#name: ::std::option::Option::<AttrValue>,
    #[prop_or_default] pub r#type: ::std::option::Option::<AttrValue>,
    #[prop_or_default] pub r#value: ::std::option::Option::<AttrValue>,
}
impl std::ops::Deref for ButtonProps {
    type Target = ::yew::virtual_dom::typings::globals::Globals;
    fn deref(&self) -> &Self::Target { &self.__globals }
}
impl std::ops::DerefMut for ButtonProps { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.__globals } }
impl ButtonProps {
    fn into_data(self) -> ::yew::virtual_dom::typings::ElementData {
        ::yew::virtual_dom::typings::ElementData {
            node_ref: ::std::option::Option::unwrap_or_default(self.node_ref.clone()),
            attributes: {
                let mut attrs = ::std::collections::HashMap::new();
                if let ::std::option::Option::Some(val) = self.r#autocapitalize.as_ref() { attrs.insert("autocapitalize", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#contextmenu.as_ref() { attrs.insert("contextmenu", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#contenteditable.as_ref() { attrs.insert("contenteditable", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#slot.as_ref() { attrs.insert("slot", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#spellcheck.as_ref() { attrs.insert("spellcheck", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#class.as_ref() { attrs.insert("class", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#title.as_ref() { attrs.insert("title", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#itemprop.as_ref() { attrs.insert("itemprop", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#accesskey.as_ref() { attrs.insert("accesskey", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#lang.as_ref() { attrs.insert("lang", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#id.as_ref() { attrs.insert("id", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#translate.as_ref() { attrs.insert("translate", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#draggable.as_ref() { attrs.insert("draggable", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#style.as_ref() { attrs.insert("style", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#dir.as_ref() { attrs.insert("dir", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#tabindex.as_ref() { attrs.insert("tabindex", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#hidden.as_ref() { attrs.insert("hidden", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_atomic.as_ref() { attrs.insert("aria-atomic", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_invalid.as_ref() { attrs.insert("aria-invalid", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_rowcount.as_ref() { attrs.insert("aria-rowcount", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_colindex.as_ref() { attrs.insert("aria-colindex", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_grabbed.as_ref() { attrs.insert("aria-grabbed", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_hidden.as_ref() { attrs.insert("aria-hidden", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_details.as_ref() { attrs.insert("aria-details", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_owns.as_ref() { attrs.insert("aria-owns", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_placeholder.as_ref() { attrs.insert("aria-placeholder", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_required.as_ref() { attrs.insert("aria-required", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_selected.as_ref() { attrs.insert("aria-selected", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_sort.as_ref() { attrs.insert("aria-sort", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_activedescendant.as_ref() { attrs.insert("aria-activedescendant", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_autocomplete.as_ref() { attrs.insert("aria-autocomplete", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_keyshortcuts.as_ref() { attrs.insert("aria-keyshortcuts", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_live.as_ref() { attrs.insert("aria-live", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_valuemax.as_ref() { attrs.insert("aria-valuemax", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_current.as_ref() { attrs.insert("aria-current", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_labelledby.as_ref() { attrs.insert("aria-labelledby", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_colcount.as_ref() { attrs.insert("aria-colcount", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_setsize.as_ref() { attrs.insert("aria-setsize", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_readonly.as_ref() { attrs.insert("aria-readonly", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_valuetext.as_ref() { attrs.insert("aria-valuetext", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_disabled.as_ref() { attrs.insert("aria-disabled", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_pressed.as_ref() { attrs.insert("aria-pressed", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_haspopup.as_ref() { attrs.insert("aria-haspopup", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_valuenow.as_ref() { attrs.insert("aria-valuenow", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_relevant.as_ref() { attrs.insert("aria-relevant", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_description.as_ref() { attrs.insert("aria-description", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_busy.as_ref() { attrs.insert("aria-busy", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_multiselectable.as_ref() { attrs.insert("aria-multiselectable", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_expanded.as_ref() { attrs.insert("aria-expanded", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_level.as_ref() { attrs.insert("aria-level", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_describedby.as_ref() { attrs.insert("aria-describedby", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_rowindex.as_ref() { attrs.insert("aria-rowindex", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_errormessage.as_ref() { attrs.insert("aria-errormessage", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_flowto.as_ref() { attrs.insert("aria-flowto", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_dropeffect.as_ref() { attrs.insert("aria-dropeffect", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_multiline.as_ref() { attrs.insert("aria-multiline", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_modal.as_ref() { attrs.insert("aria-modal", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_label.as_ref() { attrs.insert("aria-label", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_controls.as_ref() { attrs.insert("aria-controls", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_checked.as_ref() { attrs.insert("aria-checked", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_orientation.as_ref() { attrs.insert("aria-orientation", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_roledescription.as_ref() { attrs.insert("aria-roledescription", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_posinset.as_ref() { attrs.insert("aria-posinset", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_valuemin.as_ref() { attrs.insert("aria-valuemin", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_rowspan.as_ref() { attrs.insert("aria-rowspan", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#aria_colspan.as_ref() { attrs.insert("aria-colspan", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#role.as_ref() { attrs.insert("role", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#autofocus.as_ref() { attrs.insert("autofocus", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#disabled.as_ref() { attrs.insert("disabled", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#form.as_ref() { attrs.insert("form", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#formaction.as_ref() { attrs.insert("formaction", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#formenctype.as_ref() { attrs.insert("formenctype", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#formmethod.as_ref() { attrs.insert("formmethod", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#formnovalidate.as_ref() { attrs.insert("formnovalidate", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#formtarget.as_ref() { attrs.insert("formtarget", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#name.as_ref() { attrs.insert("name", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#type.as_ref() { attrs.insert("type", val.clone()); }
                if let ::std::option::Option::Some(val) = self.r#value.as_ref() { attrs.insert("value", val.clone()); }
                attrs
            },
            listeners: {
                let mut listeners = (::alloc::vec::Vec::new());
                if let Some(value) = self.onabort.as_ref() { listeners.push(::yew::html::onabort::Wrapper::__macro_new(value)); }
                if let Some(value) = self.oncancel.as_ref() { listeners.push(::yew::html::oncancel::Wrapper::__macro_new(value)); }
                if let Some(value) = self.oncanplay.as_ref() { listeners.push(::yew::html::oncanplay::Wrapper::__macro_new(value)); }
                if let Some(value) = self.oncanplaythrough.as_ref() { listeners.push(::yew::html::oncanplaythrough::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onclose.as_ref() { listeners.push(::yew::html::onclose::Wrapper::__macro_new(value)); }
                if let Some(value) = self.oncuechange.as_ref() { listeners.push(::yew::html::oncuechange::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ondurationchange.as_ref() { listeners.push(::yew::html::ondurationchange::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onemptied.as_ref() { listeners.push(::yew::html::onemptied::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onended.as_ref() { listeners.push(::yew::html::onended::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onerror.as_ref() { listeners.push(::yew::html::onerror::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onformdata.as_ref() { listeners.push(::yew::html::onformdata::Wrapper::__macro_new(value)); }
                if let Some(value) = self.oninvalid.as_ref() { listeners.push(::yew::html::oninvalid::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onload.as_ref() { listeners.push(::yew::html::onload::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onloadeddata.as_ref() { listeners.push(::yew::html::onloadeddata::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onloadedmetadata.as_ref() { listeners.push(::yew::html::onloadedmetadata::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onpause.as_ref() { listeners.push(::yew::html::onpause::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onplay.as_ref() { listeners.push(::yew::html::onplay::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onplaying.as_ref() { listeners.push(::yew::html::onplaying::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onratechange.as_ref() { listeners.push(::yew::html::onratechange::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onreset.as_ref() { listeners.push(::yew::html::onreset::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onresize.as_ref() { listeners.push(::yew::html::onresize::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onsecuritypolicyviolation.as_ref() { listeners.push(::yew::html::onsecuritypolicyviolation::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onseeked.as_ref() { listeners.push(::yew::html::onseeked::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onseeking.as_ref() { listeners.push(::yew::html::onseeking::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onselect.as_ref() { listeners.push(::yew::html::onselect::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onslotchange.as_ref() { listeners.push(::yew::html::onslotchange::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onstalled.as_ref() { listeners.push(::yew::html::onstalled::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onsuspend.as_ref() { listeners.push(::yew::html::onsuspend::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ontimeupdate.as_ref() { listeners.push(::yew::html::ontimeupdate::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ontoggle.as_ref() { listeners.push(::yew::html::ontoggle::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onvolumechange.as_ref() { listeners.push(::yew::html::onvolumechange::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onwaiting.as_ref() { listeners.push(::yew::html::onwaiting::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onchange.as_ref() { listeners.push(::yew::html::onchange::Wrapper::__macro_new(value)); }
                if let Some(value) = self.oncopy.as_ref() { listeners.push(::yew::html::oncopy::Wrapper::__macro_new(value)); }
                if let Some(value) = self.oncut.as_ref() { listeners.push(::yew::html::oncut::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onpaste.as_ref() { listeners.push(::yew::html::onpaste::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onpointerlockchange.as_ref() { listeners.push(::yew::html::onpointerlockchange::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onpointerlockerror.as_ref() { listeners.push(::yew::html::onpointerlockerror::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onselectionchange.as_ref() { listeners.push(::yew::html::onselectionchange::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onselectstart.as_ref() { listeners.push(::yew::html::onselectstart::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onshow.as_ref() { listeners.push(::yew::html::onshow::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onauxclick.as_ref() { listeners.push(::yew::html::onauxclick::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onclick.as_ref() { listeners.push(::yew::html::onclick::Wrapper::__macro_new(value)); }
                if let Some(value) = self.oncontextmenu.as_ref() { listeners.push(::yew::html::oncontextmenu::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ondblclick.as_ref() { listeners.push(::yew::html::ondblclick::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ondrag.as_ref() { listeners.push(::yew::html::ondrag::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ondragend.as_ref() { listeners.push(::yew::html::ondragend::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ondragenter.as_ref() { listeners.push(::yew::html::ondragenter::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ondragexit.as_ref() { listeners.push(::yew::html::ondragexit::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ondragleave.as_ref() { listeners.push(::yew::html::ondragleave::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ondragover.as_ref() { listeners.push(::yew::html::ondragover::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ondragstart.as_ref() { listeners.push(::yew::html::ondragstart::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ondrop.as_ref() { listeners.push(::yew::html::ondrop::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onblur.as_ref() { listeners.push(::yew::html::onblur::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onfocus.as_ref() { listeners.push(::yew::html::onfocus::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onfocusin.as_ref() { listeners.push(::yew::html::onfocusin::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onfocusout.as_ref() { listeners.push(::yew::html::onfocusout::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onkeydown.as_ref() { listeners.push(::yew::html::onkeydown::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onkeypress.as_ref() { listeners.push(::yew::html::onkeypress::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onkeyup.as_ref() { listeners.push(::yew::html::onkeyup::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onloadstart.as_ref() { listeners.push(::yew::html::onloadstart::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onprogress.as_ref() { listeners.push(::yew::html::onprogress::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onloadend.as_ref() { listeners.push(::yew::html::onloadend::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onmousedown.as_ref() { listeners.push(::yew::html::onmousedown::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onmouseenter.as_ref() { listeners.push(::yew::html::onmouseenter::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onmouseleave.as_ref() { listeners.push(::yew::html::onmouseleave::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onmousemove.as_ref() { listeners.push(::yew::html::onmousemove::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onmouseout.as_ref() { listeners.push(::yew::html::onmouseout::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onmouseover.as_ref() { listeners.push(::yew::html::onmouseover::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onmouseup.as_ref() { listeners.push(::yew::html::onmouseup::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onwheel.as_ref() { listeners.push(::yew::html::onwheel::Wrapper::__macro_new(value)); }
                if let Some(value) = self.oninput.as_ref() { listeners.push(::yew::html::oninput::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onsubmit.as_ref() { listeners.push(::yew::html::onsubmit::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onanimationcancel.as_ref() { listeners.push(::yew::html::onanimationcancel::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onanimationend.as_ref() { listeners.push(::yew::html::onanimationend::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onanimationiteration.as_ref() { listeners.push(::yew::html::onanimationiteration::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onanimationstart.as_ref() { listeners.push(::yew::html::onanimationstart::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ongotpointercapture.as_ref() { listeners.push(::yew::html::ongotpointercapture::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onlostpointercapture.as_ref() { listeners.push(::yew::html::onlostpointercapture::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onpointercancel.as_ref() { listeners.push(::yew::html::onpointercancel::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onpointerdown.as_ref() { listeners.push(::yew::html::onpointerdown::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onpointerenter.as_ref() { listeners.push(::yew::html::onpointerenter::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onpointerleave.as_ref() { listeners.push(::yew::html::onpointerleave::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onpointermove.as_ref() { listeners.push(::yew::html::onpointermove::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onpointerout.as_ref() { listeners.push(::yew::html::onpointerout::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onpointerover.as_ref() { listeners.push(::yew::html::onpointerover::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onpointerup.as_ref() { listeners.push(::yew::html::onpointerup::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ontouchcancel.as_ref() { listeners.push(::yew::html::ontouchcancel::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ontouchend.as_ref() { listeners.push(::yew::html::ontouchend::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ontransitioncancel.as_ref() { listeners.push(::yew::html::ontransitioncancel::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ontransitionend.as_ref() { listeners.push(::yew::html::ontransitionend::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ontransitionrun.as_ref() { listeners.push(::yew::html::ontransitionrun::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ontransitionstart.as_ref() { listeners.push(::yew::html::ontransitionstart::Wrapper::__macro_new(value)); }
                if let Some(value) = self.onscroll.as_ref() { listeners.push(::yew::html::onscroll::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ontouchmove.as_ref() { listeners.push(::yew::html::ontouchmove::Wrapper::__macro_new(value)); }
                if let Some(value) = self.ontouchstart.as_ref() { listeners.push(::yew::html::ontouchstart::Wrapper::__macro_new(value)); }
                listeners
            },
            key: self.key.clone(),
            children: self.children.into_iter().collect(),
        }
    }
}
impl ::yew::Component for button {
    type Message = ();
    type Properties = ButtonProps;
    fn create(_ctx: &::yew::html::Context<Self>) -> Self { Self }
    fn view(&self, ctx: &::yew::html::Context<Self>) -> ::yew::html::Html {
        #[allow(unused_mut)] let mut element = ctx.props().clone().into_data();
        ::std::convert::Into::<::yew::virtual_dom::VNode>::into({ ::yew::virtual_dom::VTag::__new_other(::std::stringify!(button ).into(), element.node_ref, element.key, ::yew::virtual_dom::Attributes::IndexMap(element.attributes.into_iter().collect()), ::yew::virtual_dom::Listeners::Pending(element.listeners.into_boxed_slice()), ::yew::virtual_dom::VList::with_children(element.children, ::std::option::Option::None)) })
    }
}

Unrelated side note: any suggestions on how to reduce the if lets would be helpful

@WorldSEnder
Copy link
Member

WorldSEnder commented Mar 8, 2022

I'm a bit confused if the ref is being translated into a ref on the generated component, or if it's actually being set on the properties. I.e. in the end we have

let __yew_props = #build_props;
::yew::virtual_dom::VChild::<#ty>::new(__yew_props, #node_ref, #key)

but the Props also include a field called node_ref. Which of those is supposed to carry the ref? It probably should be the Property's one (and only the inner one), but I can't pinpoint where the node_ref of the passed properties would be set.

So if I have

<a ref={foo} />
// Equivalent to
<::yew::virtual_dom::typings::a node_ref={foo} />

right?

@ranile
Copy link
Member Author

ranile commented Mar 8, 2022

Oh that's confusing. I didn't know component's could have their refs too. I wonder which one is set

@WorldSEnder
Copy link
Member

Worst case, both are set. That would probably confuse the internals the most.

@ranile
Copy link
Member Author

ranile commented Mar 8, 2022

I doubt both are set since props can't be duplicated and i think special_props take priority internally

@ranile
Copy link
Member Author

ranile commented Apr 6, 2022

I'm closing this as this implementation should not be used. We have to look at a different way to implementing this in the future

@ranile ranile closed this Apr 6, 2022
@ranile ranile removed this from the v0.20 milestone Apr 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-yew Area: The main yew crate A-yew-macro Area: The yew-macro crate breaking change
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants