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

Only animate elements inside a frame #8

Open
YummYume opened this issue May 4, 2023 · 11 comments
Open

Only animate elements inside a frame #8

YummYume opened this issue May 4, 2023 · 11 comments

Comments

@YummYume
Copy link
Contributor

YummYume commented May 4, 2023

Hello, thanks for making this library, it's a blast 🔥

One thing I am wondering is, would it be possible to only animate the elements inside a Turbo frame (when navigation or form submit happens and targets a frame)?

From what I understand, it doesn't look like it's possible at the moment looking at the disabling animations section. But would it be possible once that is fixed? It would help a lot as right now, the only solution I found is to disable Turn when the navigation starts, show some skeleton and then re-enable Turn when the frame is rendered.

By the way, I think there's a typo in the Tailwind usage :

<main data-turn-exit="turn-exit:animate-exit" data-turn-enter="turn-exit:animate-enter">

should be

<main data-turn-exit="turn-exit:animate-exit" data-turn-enter="turn-enter:animate-enter">
@domchristie
Copy link
Owner

Thanks for this, and glad you're having fun with it :)

I've not yet implemented animations for turbo frames as their lifecycle events are a little different (e.g. there isn't a turbo:frame-visit event).

From my brief experiments it seems like turbo:visit isn't fired on standard frame navigations and therefore Turn won't animate. However, when a frame visit is promoted to a page visit via a data-turbo-action attribute, then it will trigger a turbo:visit. Unfortunately there's currently no way to determine when a turbo:visit, originates from a frame visit or a standard full link click. hotwired/turbo#750 might help with that, but I reckon adding a turboFrameId attribute to the turbo:visit event details could also be another option.

@domchristie
Copy link
Owner

…oh and feel free to add a PR for that tailwind typo. Thanks!

@YummYume
Copy link
Contributor Author

YummYume commented May 5, 2023

Thanks for your answer @domchristie !

Indeed, I forgot to mention it happens when the frame's navigations are promoted to visits (which makes sense).
It would make sense for Turn to handle frames separately from simple visits with drive, whenever Turbo makes this possible. It would make it very easy to handle state change within a frame, even if it's not promoted to a visit.

For a quick summary of how I currently disable the animations within a frame, I use a Stimulus controller on the root html element, which exposes Turn's start and stop methods. Then simple :

<turbo-frame data-action="turbo:before-fetch-request->turn#stop turbo:visit@window->turn#start" data-turbo-action="advance" id="some-frame">
    ...
</turbo-frame>

I made a PR to fix the small typo!

@domchristie
Copy link
Owner

I use a Stimulus controller on the root html element, which exposes Turn's start and stop methods.

That looks great! Thanks for sharing.

I had a look at adding a frameId attribute to the turbo:visit event details. Unfortunately it's not straightforward. We'd need to pass the frame ID into the frame options when creating the visit here: https://github.com/hotwired/turbo/blob/4593d06ce58d17af5b17495ad8524eaa9bc2f5d2/src/core/frames/frame_controller.ts#L379-L406. The frame option already exists and it's used for programmatic visits: Turbo.visit(url, { frame: 'frame-id' }). Session#visit uses this option to alter it's behaviour, which I don't think we want in this case. (Although I could be wrong!)

@YummYume
Copy link
Contributor Author

Hi @domchristie ,

The best turn (lol) of event here would be for Turbo to add specific events to frames. Or pass a frameId as you mentioned in the visit event. If frames could be handled separately from a simple "visit" action (much like a stream) it would allow for a better handling of things (because they would all have their own logic in what to animate).

For example, visits would animate the whole page. Frames would only animate child nodes. And streams could animate re-rendered nodes but that may be too complicated (or not).

That's only my opinion though!

@domchristie
Copy link
Owner

There might be some other ways to determine when a link click targets a frame. A combination of the following might help:

  • turbo:before-fetch-request is dispatched on the frame before turbo:click, so we can start tracking a frame visit from then
  • Turbo's frame navigation uses its own event system and calls preventDefault on the turbo:click event, so we might be able to check event.defaultPrevented
  • We could reverse engineer / copy the logic that checks if a link will trigger a (promoted) frame visit

@YummYume
Copy link
Contributor Author

Hey @domchristie, checking the event.defaultPrevented sounds like a good idea, but it could lead to false positives if something else prevented the event? At this point, I still don't understand why we can't just have a simple way of knowing when a visit is done from a frame.

@domchristie
Copy link
Owner

At this point, I still don't understand why we can't just have a simple way of knowing when a visit is done from a frame.

The code for tracking a click through to creating a Visit and then dispatching the lifecycle events is quite complex. Changes must also consider the impact on the iOS/Android adapters which have restrictions on what type of JavaScript object type can be sent to them (e.g. you can't send them a <turbo-frame> DOM node). So adding a frame detail to lifecycle events is not straightforward.
Also, it doesn't look like external pull requests are being merged at the moment. Feel free to dig around though :) You might find a nice way to patch it.

@YummYume
Copy link
Contributor Author

YummYume commented Sep 2, 2023

@domchristie Thanks for making it clearer. I will perhaps try to dig around or find a workaround for it... All I could think of was that, Turbo adds a turbo-frame header to the request it sends when navigating using a Turbo frame, so would it not be possible to somehow check if this header is present and do something with it? But it doesn't seem like Turbo dispatches anything else than the URL being navigated to (and the action).

@domchristie
Copy link
Owner

All I could think of was that, Turbo adds a turbo-frame header to the request it sends when navigating using a Turbo frame, so would it not be possible to somehow check if this header is present and do something with it?

Oh nice idea! Yeh, it’ll be interesting to see if that could work. Off the top of my head, it still might be tricky to accurately match up the fetch event with the visit event, but an estimate might be doable

@YummYume
Copy link
Contributor Author

YummYume commented Sep 3, 2023

I tried playing around with it a bit, and it seems that only POST requests include the Turbo-Frame header... Or at least, the turbo:before-fetch-request event doesn't include the header on visits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants