-
-
Notifications
You must be signed in to change notification settings - Fork 564
fix(vitepress-twoslash): fix popper positions being recomputed too early within vitepress code groups #1116
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
fix(vitepress-twoslash): fix popper positions being recomputed too early within vitepress code groups #1116
Conversation
…rly when handling tab clicks
✅ Deploy Preview for shiki-next ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for shiki-matsu ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1116 +/- ##
=======================================
Coverage 95.21% 95.21%
=======================================
Files 92 92
Lines 7936 7936
Branches 1695 1694 -1
=======================================
Hits 7556 7556
Misses 374 374
Partials 6 6 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
My current solution feels kind of hacky and should probably be fixed upstream. We should ideally be able to rely on VitePress notifying "hey, the user switched to that tab of the code group!", but that's not something VitePress does at the moment. I've created this issue on their side to get some feedback Also, I've fixed the link to repro which was outdated EDIT: VitePress should expose a |
…code group tab changes
|
As stated in my previous comment, I've created an upstream issue on VitePress side that was addressed. As of 2.0.0-alpha.14, they emit a It would be even better to be able to only recompute poppers within the relevant code group. VitePress passes it along with the CustomEvent, but floating-vue doesn't expose nor support such method. I'm still not sure on how to suggest such change on floating-vue's side. |
| const path = e.composedPath() | ||
| if (path.some((el: any) => el?.classList?.contains?.('vp-code-group') || el?.classList?.contains?.('tabs'))) | ||
| window.addEventListener('vitepress:codeGroupTabActivate', async (e) => { | ||
| if (e instanceof CustomEvent) { |
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.
vitepress:codeGroupTabActivate obviously is a CustomEvent. This assertion is only here to satisfy TypeScript through control flow, but in the end it's a pretty pointless runtime condition. Should I prefer a type-only approach?
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 think a type only approach would do
antfu
left a comment
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 wonder if we should keep both approaches?
For backward compatibility reasons? Or is there any other reason I'm not aware of? In VitePress I'd love to get your insight on my own review as well if you feel like it :) |
|
Yes, for compatibility reasons. I just think |
… recomputes of poppers
| }, | ||
| "dependencies": { | ||
| "@shikijs/twoslash": "workspace:*", | ||
| "@vueuse/core": "catalog:docs", |
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.
Oh, thank you, but is this too much to introduce VueUse for only the throttle function. We can implement a simple one in-house instead.
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.
Hmm just realized throttling those recomputeAllPoppers calls actually reintroduces (inconsistently) my original issue since your click listener might fire right before vitepress' new event (i.e. during the specified delay).
I wonder if trying to obsessively avoid these duplicate calls is in fact relevant and even realistic? The only approach I see is to only start throttling whenever vitepress:codeGroupTabActivate is received since it guarantees it's supported by the used VitePress version and that we won't need the older click listener's callback, but is it worth it?
I'll simply remove the throttle for now, I can go for the above approach if you feel like it's worth it or keep it that way if you feel like it's not!
Note
This PR only relates to the
vitepress-twoslashpackageThe issue I'm trying to address
I'm using Twoslash within VitePress code-groups. A few months ago, @antfu added this to improve tabs experience. However, despite that change, I often run into the same issue where switching to a "code-group" that either includes a completion or a query popper would actually not show that popper.
Here's a very simple repro, on the
/issuespage.Here's an overview of the issue
When switching on the second group, completion popup doesn't show up until the whole block gets re-rendered.
In this video I click on a tab in an unrelated code-group to trigger a re-render and actually show my second group's completion popper.
Here's an ever more annoying consequence of this issue
You can see how playing around with the active tab messes up what actual popper should be displayed. I'd rather have the popper not showing up (like in the previous example) than displaying wrong data!
The cause
It turns out it is just a timing-related issue.
Keep in mind that:
activeclass.activeclasses with a "click" listener.vitepress-twoslashprovides its own tab "click" listener to re-compute poppers positions.This happens in my scenario:
vitepress-twoslash's click handler is fired first and callsfloating-vue'srecomputeAllPopppers.activeclass to the popper reference.See original suggested fix
The suggested fix
On
vitepress-twoslashside, when handling a "tab click", therecomputeAllPoppersis called whenever target element's classList contains eithervp-group-codeortabsclass. Whilevp-group-codeobviously targets VitePress code groups, I wasn't sure abouttabsso I preferred to leave that logic untouched,Essentially, my suggested fix consists in waiting, if needed, for the "active" class to be added to the target code-group (and therefore for the relevant code-block to be displayed) before recomputing its poppers positions.
This is done by spawning a simple MutationObserver that waits for this "active" class to be included then self-destructs. That's the best approach I came up with because
vitepress-twoslashand VitePress do not have a direct communication channel and I wouldn't expect this to have much performance impacts (unless maybe end-users start unreasonably spam-switching between code groups?).About escape hatches…
recomputeAllPoppersif the click matches.tabs(or VitePress code blocks).waitForActiveClass.