-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Implement imperative refs, capturing scopes to child components #2551
Closed
Closed
Changes from 41 commits
Commits
Show all changes
43 commits
Select commit
Hold shift + click to select a range
562e879
implement imperative refs, capturing a scope
WorldSEnder d1dd4bc
documentation for ComponentRef
WorldSEnder 32dfe0d
fix doc test
WorldSEnder ff99444
Fix a bunch of issues with docs
WorldSEnder f8b917e
address review:
WorldSEnder 1f3de5d
restrict new and get to struct components for now
WorldSEnder 560e56e
shift ComponentRef into inner state
WorldSEnder 7ecb58f
rename ComponentAnyRef
WorldSEnder fc563ad
fixup linting
WorldSEnder 7070025
implement HtmlRef
WorldSEnder 08fb45e
commit to changed macro errors
WorldSEnder f1908f4
commit the component ref immdiately after view
WorldSEnder 39492bf
make the reference bindable
WorldSEnder 3e3c674
some code size optimizations
WorldSEnder 00a2e0b
Merge remote-tracking branch 'upstream/master' into imperative-ref
WorldSEnder 0a4a2ee
fix doc test
WorldSEnder d5684bc
fix website tests
WorldSEnder 0a05ffc
the ref is now bound in BaseComponent::create
WorldSEnder 58087d5
remove dyn Debug of user binds, saves space
WorldSEnder c2785c7
fixup website tests
WorldSEnder fc753ba
feature soundness, apparently
WorldSEnder 9eb2edf
re-expose event log for debugging
WorldSEnder b857074
add default fn to BaseComponent
WorldSEnder 56fa37d
Merge remote-tracking branch 'upstream/master' into imperative-ref
WorldSEnder cbf0dd7
introduce intermediate trait ComponentWithRef
WorldSEnder da0c131
pass BindableRef to FunctionComponent
WorldSEnder 4bf5e23
introduce explicit ErasedHtmlRef::unbound
WorldSEnder 7698970
convert light_switch to example
WorldSEnder cbad47b
assert that ref is set in debug mode
WorldSEnder 6c3d655
Merge remote-tracking branch 'upstream/master' into imperative-ref
WorldSEnder f229c62
inch towards imperative ref on function comps
WorldSEnder b104b58
remove ErasedStorage
WorldSEnder 895ce20
NoReference is now an empty enum
WorldSEnder 8e77558
fix website link
WorldSEnder 22e6db5
Rc<Setter> -> Setter
WorldSEnder 6f9590d
readability, use CompRef
WorldSEnder afcbeb2
move erased_ref to ComponentState
WorldSEnder 5dd862a
fix feature linting
WorldSEnder 9deac03
omit a drop impl
WorldSEnder cbabe59
adjust documentation
WorldSEnder 258cc3a
move node erasure to construction
WorldSEnder 42e1a0c
fixup of previous 2 commits
WorldSEnder 3eeae19
fix doc test in website-docs
WorldSEnder File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[package] | ||
name = "light_switch" | ||
version = "0.1.0" | ||
authors = ["WorldSEnder <WorldSEnder@users.noreply.github.com>"] | ||
edition = "2021" | ||
license = "MIT OR Apache-2.0" | ||
|
||
[dependencies] | ||
yew = { path = "../../packages/yew", features = ["csr"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Counter Example | ||
|
||
[![Demo](https://img.shields.io/website?label=demo&url=https%3A%2F%2Fexamples.yew.rs%2Flight_switch)](https://examples.yew.rs/light_switch) | ||
|
||
A simple example of a light switch controlling the light setting in neighboring component | ||
|
||
## Running | ||
|
||
Run a debug version of this application: | ||
|
||
```bash | ||
trunk serve | ||
``` | ||
|
||
Run a release version of this application: | ||
|
||
```bash | ||
trunk serve --release | ||
``` | ||
|
||
## Concepts | ||
|
||
Demonstrates the use of ComponentRef to send messages to children and sibling components. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<title>Yew • Light Switch</title> | ||
|
||
<link data-trunk rel="rust" /> | ||
<link data-trunk rel="sass" href="index.scss" /> | ||
</head> | ||
|
||
<body></body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
.switchlabel { | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
} | ||
|
||
.switch { | ||
position: relative; | ||
display: inline-block; | ||
width: 60px; | ||
height: 34px; | ||
margin: 2px; | ||
} | ||
|
||
.switch input { | ||
display: none; | ||
} | ||
|
||
.switch .drive { | ||
position: absolute; | ||
cursor: pointer; | ||
top: 0; | ||
left: 0; | ||
right: 0; | ||
bottom: 0; | ||
background-color: #ccc; | ||
-webkit-transition: .4s; | ||
transition: .4s; | ||
border-radius: 34px; | ||
display: flex; | ||
} | ||
|
||
.switch .pin { | ||
height: 26px; | ||
width: 26px; | ||
background-color: white; | ||
border-radius: 50%; | ||
margin: 4px; | ||
} | ||
|
||
.drive .buffer { | ||
transition: flex .1s; | ||
} | ||
|
||
.switch input:checked + .drive { | ||
background-color: #2196F3; | ||
} | ||
|
||
.switch input:checked + .drive .buffer { | ||
flex: 1; | ||
} | ||
|
||
.switch input:focus + .drive { | ||
box-shadow: 0 0 1px #2196F3; | ||
} | ||
|
||
.light { | ||
min-height: 400px; | ||
background-color: #404040; | ||
} | ||
|
||
.light.on { | ||
background-color: rgb(242, 245, 53); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
use yew::html::{BindableRef, Scope}; | ||
use yew::prelude::*; | ||
|
||
#[derive(PartialEq, Properties)] | ||
struct SwitchProps { | ||
on_toggle: Callback<MouseEvent>, | ||
} | ||
|
||
#[function_component] | ||
fn Switch(SwitchProps { on_toggle }: &SwitchProps) -> Html { | ||
html! { | ||
<div class="switchlabel"> | ||
<label class="switch"> | ||
<input type="checkbox" onclick={on_toggle} /> | ||
<span class="drive"> | ||
<span class="buffer" /> | ||
<span class="pin" /> | ||
</span> | ||
</label> | ||
{"Light switch"} | ||
</div> | ||
} | ||
} | ||
|
||
enum LightMessage { | ||
Toggle, | ||
} | ||
|
||
struct Light { | ||
is_on: bool, | ||
} | ||
|
||
impl ComponentWithRef for Light { | ||
type Message = LightMessage; | ||
type Properties = (); | ||
type Reference = Scope<Self>; | ||
|
||
fn create(ctx: &Context<Self>, bindable_ref: BindableRef<'_, Self::Reference>) -> Self { | ||
bindable_ref.bind(ctx.link().clone()); | ||
Light { is_on: false } | ||
} | ||
|
||
fn update(&mut self, _ctx: &Context<Self>, LightMessage::Toggle: LightMessage) -> bool { | ||
self.is_on = !self.is_on; | ||
true | ||
} | ||
|
||
fn view(&self, _ctx: &Context<Self>) -> Html { | ||
html! { | ||
<div class={classes!["light", self.is_on.then(|| "on")]} /> | ||
} | ||
} | ||
} | ||
|
||
#[function_component] | ||
fn Room() -> Html { | ||
let light_ref: ComponentRef<Light> = use_html_ref(); | ||
let on_toggle = { | ||
let light_ref = light_ref.clone(); | ||
Callback::from(move |_| { | ||
light_ref | ||
.get() | ||
.expect("a light to have rendered") | ||
.send_message(LightMessage::Toggle) | ||
}) | ||
}; | ||
html! { | ||
<> | ||
<Light ref={&light_ref} /> | ||
<Switch {on_toggle} /> | ||
</> | ||
} | ||
} | ||
|
||
fn main() { | ||
yew::Renderer::<Room>::new().render(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Whilst I was originally suggesting to not classify
ref
as a normal prop, however, after @hamza1311 brought up the idea of making theref
as a normal prop, I actually think this is a better approach and leads to a much simpler design.Suppose that if there are 2 refs (e.g.:
ref
andinputRef
) for a single component, how to handle these 2 at the same time?(It's not always the case to simply forward the
inputRef
to the inner input element, as it can be enhanced by adding additional methods to it.)In addition, I don't think it actually provides any benefit of actually separating the handling of these 2 refs especially if you have to set it manually.
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.
There's a semantic difference:
ref
means that this component is responsible for binding the referenced value. Normally, you get a currently referenced value from aHtmlRef
. Hence whycrate
accepts a specialBindableRef
type that allows you to bind (and maybe rebind, I'm not sure at this point, but it could make sense) a value.BindableRef
also could have a method to decay it to a ref the component binds to one of its children, i.e. to forward the ref.But being the
HtmlRef
that this and only this component is in charge of binding, is imo special enough to deserve separate treatment.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.
If you wrap a third party component library that is linked with
VRef
and you have 2 props (ref
andinput_ref
) withref
linked to the container div.How do you expose
input_ref
to the inner input element created byVRef
without creating another component?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.
Added
BindableRef::forward
so the component can capture aHtmlRef
it can itself bind on children returned fromview
.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 does not quite solve the question I mentioned.
Suppose there is a component with 2 refs,
input_ref
andref
.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 suppose
VRef
should accept aNodeRef
, too?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 don't see how this solves the example in previous comment.
VRef
is registered onel
butinput_ref
is supposed to be registered oninput_el
which is queried from theel
byquery_selector
.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.
Ah I didn't quite get what you were asking: The best strategy for this case, imo, would be to expose a struct containing all the referenced elements. Such as
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 forgot to mention that usually a custom element usually will not render its content until its
connectedCallback
is invoked. Which means thatel
andinput_el
are actually made available in different component lifecycle, theinput_el
element will not become available untilrendered()
cycle when asel
must be passed asVRef
for it to become rendered.