-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
proposal for better support for custom swap functions #10908
Conversation
|
Very interesting idea! I think that requiring the user to write over the function is not ideal though. I think something along this line would be nice: class BeforeSwapEvent {
// ...
public swap: (swapper?: CustomSwapper) => void;
public setSwapper(swapper: CustomSwapper) {};
} I think this can be made backwards compatible while providing a cleaner API: document.addEventListener('astro:before-swap', (e) => {
e.setSwapper(new CustomSwapper({
swapHeadElements(doc) {
const dynamicStyle = document.head.querySelector('style:not(:empty)');
swapHeadElements(doc); // reuse the built-in implementation
dynamicStyle && document.head.insertAdjacentElement('afterbegin', dynamicStyle);
},
}));
}); |
Hi Luiz! IIUC, you say there should be a better way than
Chance are good, that this is mainly a naming issue. The class does not really swap anything but generates a swap function, thus overriding How about:
Looking at your proposal, what I do not yet understand: Where does the custom swap function get |
My worry is more about the composability. If you want to add a behavior on top of the existing swap that can be composed with other components, it would break with this API, or you have to go back to the dance of function value swaps. For example, if I want to make a component that requires custom logic to persist, I need to do this today: document.addEventListener('astro:before-swap', (e) => {
const nextSwap = e.swap;
e.swap = () => {
// custom state persistence
nextSwap();
};
}); This way, I can add my logic without removing the logic from other components that do the same thing. You either clearly override everything that came before or you add something before/after it. With the I'd like a new API to make things easier without requiring those using it to override everything (being a good neighbor in the community has been my motto recently). That was the idea, not the naming. I sketched another idea (here) that I think is even simpler and solves a few problems:
And it follows the same design we have for middlewares, each one receiving the function to call the next. |
Oh dear, it really took me a while to realize what you had in mind! 🤦🏻♂️ To be honest, I didn't even think about composability when I came up with the solution for replacing parts of the built-in implementation, as I assumed that this would only be needed in special cases anyway. It was not meant as a replacement for decorating Of course, now that I see how elegant your solution is, I regret that we didn't come up with something like this in the original swap. |
Many thanks @Fryuni! A real improvement indeed. It's always a great pleasure to develop things together with you ;-) |
It would also be easy to to deprecate So the current story is:
Extensions are applied first, redefinitions of e.swap are applied second, see e2e chaining test. Could need some help with good wording. How about: rename |
I'm totally on board with deprecating
Yeah, sounds great!
Maybe also worth pointing out that decorations and redefinitions are applied in the order of the listeners, so:
What will run is D decoration -> C decoration -> B redefinition. A and the default logic won't run.
Some ideas without much thought:
|
That's the one! I'll take that! Merci! |
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 API seems nice to me 👍 I think patching .swap
is fine for me, but if it's common for users to modify how swapping works, then I think it's definitely worth making it nicer, like the .extends
.
I'll have to seat this one out, as I don't have much context. However, it's not very clear what's the plan of this PR. Is it something new? A patch/minor? |
@ematipico this would be a |
The Here's a suggestion:
e.swapWith({
swapHeadElements(doc) { ... }
}) If you call this method you don't need to replace What do you think? |
It breaks composability with one listener undoing what other is doing. Same problem as I pointed out in this comment (#10908 (comment)) with the added problem of the swap being applied more than once if more than one listener calls |
Let's take one step back and look at the original requirement. From the five events we have for view transitions, The solution does not require a high-end API, but a simple, maybe even a bit hidden, way to benefit from the built-in implementation. Imho we should just keep the current event and the possibilities to override |
Please note: From what I wanted to accomplish with this PR, I would also be perfectly fine with deleting |
Yeah great points all around @Fryuni, @martrapp. If multiple listeners are all doing swaps I think someone is going to have a bad time. Rather than dealing with the matrix of possibilities where multiple listeners are swapping a subset of parts and us trying to manage all of that, I do like the idea that you're proposing @martrapp of just exposing the raw functions and not being in the middle of making sure parts are only swapped once (removing the swap-steps in that case). I think this is a good starting point. |
Done! Thank you all very much for the fruitful discussions! |
Changes
Within the astro:before-swap event, people can replace the implementation of the swap function with their own version. By calling the original swap(), it is rather simple to add own code in front or after it.
But if you want to change one part of the implementation (e.g. how the body is replaced), you have to rewrite the whole swap() (handling of scripts, root attributes, and header elements) just to bring in your change for the handling of body elements.
This PR proposes to make the swap() blocks accessible for custom implementations.
The
defaultSwap()
implementation calls the blocks directly, without overhead compared to the previous handling.Edited: See test on how to use the reusable building blocks.
Docs
No official API yet.