-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 5596b31
Showing
5 changed files
with
260 additions
and
0 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/target | ||
Cargo.lock |
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,20 @@ | ||
[package] | ||
name = "yew-component-size" | ||
version = "0.1.0" | ||
authors = ["James Birtles <[email protected]>"] | ||
edition = "2018" | ||
description = "A Yew component that emits events when the parent component changes width/height." | ||
license = "MIT OR Apache-2.0" | ||
readme = "README.md" | ||
repository = "https://github.com/AircastDev/yew-component-size" | ||
keywords = ["yew", "component"] | ||
categories = ["wasm"] | ||
|
||
[features] | ||
default = [] | ||
|
||
[dependencies] | ||
yew = "0.17" | ||
wasm-bindgen = "0.2" | ||
web-sys = { version = "0.3", features = ["Element", "DomRect", "HtmlIFrameElement", "Window"] } | ||
serde = { version = "1.0", optional = true, features = ["derive"] } |
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,46 @@ | ||
# yew-component-size | ||
|
||
![Crates.io](https://img.shields.io/crates/l/yew-component-size) ![Crates.io](https://img.shields.io/crates/v/yew-component-size) | ||
|
||
A Yew component that emits events when the parent component changes width/height. | ||
Only compatible with Yew using web_sys. | ||
|
||
## Example: | ||
|
||
```rust | ||
let onsize = self.link.callback(|size: ComponentSize| { | ||
// Access to `size.width` and `size.height` | ||
}); | ||
|
||
html! { | ||
// Parent that you're tracking the size of must be `position: relative` | ||
<div style="position: relative;"> | ||
// ... | ||
<ComponentSizeObserver onsize=onsize /> | ||
</div> | ||
} | ||
``` | ||
|
||
## How it works | ||
|
||
This uses a trick borrowed from Svelte where we use an iframe that is positioned absolutely | ||
to fill it's parent element, and then we listen to the resize event of iframe's window. | ||
|
||
_**Note:** This incurs a small cost and so should not be used on a large number of elements at the same time._ | ||
|
||
## License | ||
|
||
Licensed under either of | ||
|
||
- Apache License, Version 2.0 | ||
([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) | ||
- MIT license | ||
([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) | ||
|
||
at your option. | ||
|
||
## Contribution | ||
|
||
Unless you explicitly state otherwise, any contribution intentionally submitted | ||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be | ||
dual licensed as above, without any additional terms or conditions. |
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,52 @@ | ||
use wasm_bindgen::prelude::*; | ||
use yew::prelude::*; | ||
use yew_component_size::{ComponentSize, ComponentSizeObserver}; | ||
|
||
struct MyComponent { | ||
link: yew::ComponentLink<Self>, | ||
size: ComponentSize, | ||
} | ||
|
||
enum Msg { | ||
OnComponentSize(ComponentSize), | ||
} | ||
|
||
impl Component for MyComponent { | ||
type Message = Msg; | ||
type Properties = (); | ||
|
||
fn create(_props: Self::Properties, link: yew::ComponentLink<Self>) -> Self { | ||
Self { | ||
link, | ||
size: Default::default(), | ||
} | ||
} | ||
|
||
fn update(&mut self, msg: Self::Message) -> yew::ShouldRender { | ||
match msg { | ||
Msg::OnComponentSize(size) => { | ||
self.size = size; | ||
true | ||
} | ||
} | ||
} | ||
|
||
fn change(&mut self, _props: Self::Properties) -> yew::ShouldRender { | ||
false | ||
} | ||
|
||
fn view(&self) -> yew::Html { | ||
let onsize = self.link.callback(Msg::OnComponentSize); | ||
html! { | ||
<div style="position:relative"> | ||
<span>{format!("width: {}px, height: {}px", self.size.width, self.size.height)}</span> | ||
<ComponentSizeObserver onsize=onsize /> | ||
</div> | ||
} | ||
} | ||
} | ||
|
||
#[wasm_bindgen] | ||
pub fn start_app() { | ||
App::<MyComponent>::new().mount_to_body(); | ||
} |
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,140 @@ | ||
#![deny(missing_docs)] | ||
|
||
//! ![Crates.io](https://img.shields.io/crates/l/yew-component-size) ![Crates.io](https://img.shields.io/crates/v/yew-component-size) | ||
//! | ||
//! A Yew component that emits events when the parent component changes width/height. | ||
//! Only compatible with Yew using web_sys. | ||
//! | ||
//! # Example: | ||
//! ```rust | ||
//! let onsize = self.link.callback(|size: ComponentSize| { | ||
//! // Access to `size.width` and `size.height` | ||
//! }); | ||
//! | ||
//! html! { | ||
//! // Parent that you're tracking the size of must be `position: relative` | ||
//! <div style="position: relative;"> | ||
//! // ... | ||
//! <ComponentSizeObserver onsize=onsize /> | ||
//! </div> | ||
//! } | ||
//! ``` | ||
//! | ||
//! # How it works | ||
//! | ||
//! This uses a trick borrowed from Svelte where we use an iframe that is positioned absolutely | ||
//! to fill it's parent element, and then we listen to the resize event of iframe's window. | ||
//! | ||
//! _**Note:** This incurs a small cost and so should not be used on a large number of elements at the same time._ | ||
//! | ||
//! # License | ||
//! | ||
//! Licensed under either of | ||
//! | ||
//! * Apache License, Version 2.0 | ||
//! ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) | ||
//! * MIT license | ||
//! ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) | ||
//! | ||
//! at your option. | ||
//! | ||
//! # Contribution | ||
//! | ||
//! Unless you explicitly state otherwise, any contribution intentionally submitted | ||
//! for inclusion in the work by you, as defined in the Apache-2.0 license, shall be | ||
//! dual licensed as above, without any additional terms or conditions. | ||
|
||
#[cfg(feature = "serde")] | ||
use serde::{Deserialize, Serialize}; | ||
use wasm_bindgen::{prelude::Closure, JsCast}; | ||
use web_sys::HtmlIFrameElement; | ||
use yew::{html, Callback, Component, NodeRef, Properties}; | ||
|
||
const IFRAME_STYLE: &str = "display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: hidden; border: 0; opacity: 0; pointer-events: none; z-index: -1;"; | ||
|
||
/// Yew component to observe changes to the size of the parent element. | ||
/// | ||
/// See the crate documentation for an example and more information. | ||
#[derive(Debug)] | ||
pub struct ComponentSizeObserver { | ||
props: Props, | ||
iframe_ref: NodeRef, | ||
on_resize: Option<Closure<dyn Fn()>>, | ||
} | ||
|
||
/// ComponentSizeObserver properties | ||
#[derive(Properties, Clone, PartialEq, Debug)] | ||
pub struct Props { | ||
/// A callback that is fired when the component size changes for any reason. | ||
pub onsize: Callback<ComponentSize>, | ||
} | ||
|
||
/// A struct containing the width and height of the component | ||
#[derive(Default, Clone, PartialEq, Debug)] | ||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] | ||
pub struct ComponentSize { | ||
/// Width of the component in pixels | ||
pub width: f64, | ||
|
||
/// Height of the component in pixels | ||
pub height: f64, | ||
} | ||
|
||
impl Component for ComponentSizeObserver { | ||
type Message = (); | ||
type Properties = Props; | ||
|
||
fn create(props: Self::Properties, _link: yew::ComponentLink<Self>) -> Self { | ||
Self { | ||
props, | ||
iframe_ref: Default::default(), | ||
on_resize: None, | ||
} | ||
} | ||
|
||
fn update(&mut self, _msg: Self::Message) -> yew::ShouldRender { | ||
false | ||
} | ||
|
||
fn change(&mut self, props: Self::Properties) -> yew::ShouldRender { | ||
if self.props != props { | ||
self.props = props; | ||
self.add_resize_listener(); | ||
false | ||
} else { | ||
false | ||
} | ||
} | ||
|
||
fn view(&self) -> yew::Html { | ||
html! { | ||
<iframe style=IFRAME_STYLE ref=self.iframe_ref.clone() /> | ||
} | ||
} | ||
|
||
fn rendered(&mut self, first_render: bool) { | ||
if first_render { | ||
self.add_resize_listener(); | ||
} | ||
} | ||
} | ||
|
||
impl ComponentSizeObserver { | ||
fn add_resize_listener(&mut self) { | ||
let iframe = self.iframe_ref.cast::<HtmlIFrameElement>().unwrap(); | ||
let window = iframe.content_window().unwrap(); | ||
|
||
let iframe_ref = self.iframe_ref.clone(); | ||
let size_callback = self.props.onsize.clone(); | ||
let on_resize = Closure::wrap(Box::new(move || { | ||
let iframe = iframe_ref.cast::<HtmlIFrameElement>().unwrap(); | ||
let bcr = iframe.get_bounding_client_rect(); | ||
size_callback.emit(ComponentSize { | ||
width: bcr.width(), | ||
height: bcr.height(), | ||
}); | ||
}) as Box<dyn Fn()>); | ||
window.set_onresize(Some(on_resize.as_ref().unchecked_ref())); | ||
self.on_resize = Some(on_resize); | ||
} | ||
} |