-
Notifications
You must be signed in to change notification settings - Fork 120
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
xilem_html: Significant refactor and introduce DOM interface traits #141
xilem_html: Significant refactor and introduce DOM interface traits #141
Conversation
} | ||
} | ||
|
||
impl<A: Action> sealed::Sealed for A {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs a separate Sealed
trait because of e.g. this
…ping possible and flexible
…o avoid inference issues with composed types
* Add ancestor *and* descendent composable macros * Added a macro to correctly and conveniently implement dom interfaces for interface restricted Views, this is used currently for the Attr view and the event views
…attribute logic into separate functions
50c5214
to
4e1fbce
Compare
There's no reason edit: The |
I have just looked into that. At least memoize(memo_me, |memo_me| {
p(memo_me).attr("something", "memo")
})
.attr("something2", "changes here won't be propagated to the p element, unless memo_me has changed") This is because the context that is passed in Regarding the other Views, I think it should be possible to implement the impl<T, A, V1: Element, V2: Element> Element<T, A> for OneOf2<V1, V2> {}
impl<T, A, V1: HtmlElement, V2: HtmlElement> HtmlElement<T, A> for OneOf2<V1, V2> {}
// probably needs all the bounds that are defined on the View impl as well... I think Adapt and AdaptState should work as well like this: // Adapt
impl<ParentT, ParentA, ChildT, ChildA, V, F> Element<ParentT, ParentA>
for Adapt<ParentT, ParentA, ChildT, ChildA, V, F>
where
V: Element<ChildT, ChildA>,
F: Fn(&mut ParentT, AdaptThunk<ChildT, ChildA, V>) -> $crate::MessageResult<ParentA> $( $ss )*,
{
}
// AdaptState
impl<ParentT, ChildT, A, V, F> Element<ParentT, A> for AdaptState<ParentT, ChildT, V, F>
where
V: Element<ChildT, A>,
F: Fn(&mut ParentT) -> &mut ChildT,
{
} I think I'll look into/implement this, when I have more time. But as said, it's of course still possible to use these views, just currently not the extra methods defined on the DOM traits (and as |
Yeah, the thing is I need to use |
* Eliminate associated trait bound for the View, for easier use * Add additional OneSeqOf view sequences, to avoid removing the ViewMarker super trait bound on Element * implement all dom interfaces for the OneOf views
I have implemented all the DOM traits for Adapt and AdaptState and with a few changes as well for the I have also (mostly) fixed your caniuse xilem branch here, you may want to check if everything is correct there (specifically |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've not had a chance to run this yet, but I'm happy to approve anyway, as the Xilem html is experimental
It's not that clear to me why the attribute view doesn't apply the attribute changes itself, instead maintaining the set of current attributes. But I've not dug too deeply into the code
let element: &web_sys::HtmlInputElement = element.dyn_ref().unwrap_throw(); | ||
element.set_value(value) | ||
} else if name == "checked" { | ||
let element: &web_sys::HtmlInputElement = element.dyn_ref().unwrap_throw(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit surprising
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually this is existing code, that was just copied (from element/mod.rs
).
But it's a good point, I have just tried out only using element.set_attribute
and it also works for me?
@derekdreery do you have input/context here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, at least for value
there indeed seems to be an issue with clearing the value, it just isn't cleared in the input.
I think this is a good motivation to properly support DOM attributes instead of just HTML attributes.
crates/xilem_html/src/context.rs
Outdated
} | ||
|
||
fn remove_attribute(element: &web_sys::Element, name: &str) { | ||
// we have to special-case `value` because setting the value using `set_attribute` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Value isn't the key being special cased here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above but with element.remove_attribute
Thanks. The reason why I have decided to collect these attributes instead of setting them directly, is more control over the whole diffing/rebuilding and Collecting also avoids setting the attributes multiple times in the DOM (see examples given above), which should be more efficient (less DOM traffic). Could also be generally interesting for some kind of Modifier views. I originally wanted to do it with the associated I think I will include the changes in Philipp-M@724a5b4, I'm also generally not sure, whether types should have a I think I'll also reorder the generic attributes of composing views as proposed here. |
Ok, I'm merging this as it had multiple iterations, and I'm relatively happy with the outcome (little bit more complex than I would've wished, but I guess that's the price to pay for more sophisticated/correct typing with a lot of hierarchical DOM interfaces, avoiding a lot of boilerplate). |
This PR refactors xilem_html significantly such that it:
Refactors attributes to be managed by a separate view (
Attr
) that composes e.g. the underlying element view instead of managing all attributes via aVec
directly on the element, this makes the API more "functional/declarative" such that something likeset_attr
isn't possible anymore, but gives more control over how e.g.el.attr("n", "v").attr("n", "2")
should be handled exactly (currently the outermost attribute overwrites inner values (i.e.<el n="2"></el>
in this example)), it's also slightly more efficient as benchmarks have shown.Introduces DOM interface traits that roughly map to their equivalently named DOM interface counterparts that inherit from the base interface
Element
(currently only every interface that inherits fromHTMLElement
, but it could be easily extended to also include interfaces that inherit fromSVGElement
).There's e.g. a
HtmlCanvasElement
trait which has the super traitHtmlElement
which in turn hasElement
(the base interface) as super trait.Element
requiresView + ViewMarker + Sealed
as super trait bounds, so it should be a little bit more convenient to use (e.g.fn my_el<T>() -> impl Element<T>
instead offn my_el<T>() -> impl View<T> + ViewMarker
.Sealed
disallows further implementations downstream as safety precaution, since there are a few dynamic type-system "hacks" necessary to avoid long compilation times because of issues like this. In the future it may be feasible to allow implementations downstream.Each DOM interface trait has the potential to support only fields that are allowed on each of the element (e.g.
width
andheight
on aHtmlCanvasElement
). Currently, this is done by using the mentionedAttr
view, but could be extended by directly accessing the underlying DOM attributes/methods (and I have already experimented with this here). This needs careful evaluation though (wasm binary size, performance etc.).The DOM interface traits have another important function, as they're implemented for all kinds of views (e.g.
Attr
or event views) recursively until it reaches a concrete element view and all the composing views "take the dom interface type", such thatcanvas_el.on_click().width().attr()
is possible (which was not the case previously).So each view (like
Attr
) can be used as long as the "first/deepest" view in this composition chain is an element view.Currently as example
width
andheight
is implemented for aHtmlVideoElement
andHtmlCanvasElement
.A follow-up PR will likely add the rest of the attributes that are possible via HTML attributes (or goes the more ambitious and novel way of using DOM attributes (no other major framework I know of goes this route yet AFAIK)).
Refactors the element and event views, such that they're more compact and efficient when compiled (e.g. there's no extra
name
attribute, the view is directly macro-expanded with the relevant string. Same applies to the event views which were also majorly refactored (and with a few bug fixes too). This also replaces theElement
View with aCustomElement
view, which should support autonomous custom elements ("Web components"). Customized built-in elements are not supported for now, but it shouldn't be too difficult to add support for them.Removes the extra feature flag for typed views, as I don't see a big benefit currently (after benches and usage/experiments). In the future, it maybe makes sense for the extra DOM interfaces, but that needs to be evaluated carefully (to avoid friction for either the user as well as the maintainers of xilem_html).
Removes the
after_update
method for now, but an improved and extendedAfterUpdate
view is already implemented here (which will result in a PR shortly)For more (text-wally) information checkout the relevant zulip thread, which may contain interesting information collected along the (longer than planned) course of this PR.