yewstack / yew

Rust / Wasm framework for creating reliable and efficient web applications
https://yew.rs
Apache License 2.0
30.53k stars 1.42k forks source link

Resize handlers #333

Closed Boscop closed 4 years ago

Boscop commented 6 years ago

Yew should have a way to notify components when they are resized, and allow them to execute their own resize handler, similar to IronResizableBehavior in Polymer. (It also has other behaviors for similar things.) But maybe there's an even better solution? How do other js frameworks do this?

kradical commented 6 years ago

There is a global "resize" event in javascript but there is no "this component got resized" event. I know there are wrapper libraries for React components that will notify your component when it changes size. I think this is something that possibly isn't the responsibility of the framework but a library. Also apparently there is "Resize Observers" which I haven't looked into much.. not even sure if it is implemented in any browsers...

hgzimmerman commented 6 years ago

Hey, here is the following code I am using for handling resize events. Since I am testing using the i3 window manager on linux, it appears that the WM already limits the window resize to when I perform a mouse-up when dragging a window around. For other WMs/Desktop Environments, the windowing system may very rapidly tell the browser to resize itself, inundating your application with resize events, causing performance hitching. Workarounds to rate-limit the event firing are present in the @kradical's "resize event" link, or you could use Yew's existing timeout service to toggle some resizing: bool value to indicate a resize occurred recently to prevent redraws.

pub struct ResizeService {}

pub struct ResizeTask(Option<Value>);

impl ResizeService {
    pub fn new() -> ResizeService {
        ResizeService {}
    }

    pub fn register(&mut self, callback: Callback<()>) -> ResizeTask {
        let callback = move || {
            callback.emit(());
        };
        let handle = js! {
            var callback = @{callback};
            var action = function() {
                callback();
            };
            return window.addEventListener("resize", action);
        };
        return ResizeTask(Some(handle))
    }
}

impl Drop for ResizeTask {
    fn drop(&mut self) {
        let handle = self.0.take().expect("Resize task already empty.");
        js! {
            @(no_return)
            var handle = @{handle};
            handle.callback.drop();
        }
    }
}
Boscop commented 6 years ago

Thanks! Could I do it more "typed", similar to this? https://github.com/DenisKolodin/yew/blob/master/examples/routing/src/routing.rs#L47

But what should I use instead of PopStateEvent?

Boscop commented 6 years ago

@kradical Thanks, do you know how these React libs detect when a component gets resized (if the viewport stays the same so that window's resize handler is not triggered)?

hgzimmerman commented 6 years ago

There does exist a resize event in stdweb, but that doesn't contain any meaningful data about the viewport, so () is just about as useful as ResizeEvent.

Boscop commented 6 years ago

Ah, it's necessary to use a type that impls ConcreteEvent here, such as ResizeEvent. () doesn't impl ConcreteEvent. It's identified by the EVENT_TYPE associated constant: https://docs.rs/stdweb/*/src/stdweb/webapi/events/dom.rs.html#100

Thanks, I can work with this for now, but I'm still curious how I could detect when a component is resized without the window/viewport being resized..

kradical commented 6 years ago

@Boscop The specific example I linked uses a polyfill for ResizeObserver which I believe it uses MutationObservers in the background. It really is not a simple problem and you will have to write a non-trivial amount of filler code to make it work properly. Or there might be some way to "include" the javascript and interact with it. I am not super familiar with Yew. Goodluck!

Edit: mdn has documentation about MutationObservers and W3 has a spec for ResizeObservers.

jstarry commented 4 years ago

A ResizeService has been added to Yew 0.9 via https://github.com/yewstack/yew/pull/577