Comments (5)
Hello!
Thank you for looking into it!
I've seen the fix.
I've spend so much time today again on it and also saw some strange behavior when removing nextTick() completely. I often - not always - got an endless update loop.
I tried three other solutions today
- Moving
const previouslyFocusedElement = document.activeElement as HTMLElement | null
between
const container = currentElement.value
//--> here
await nextTick()
That way we can remember the activeElemement, which gets changed by the MutationObserver. But this might break something else, when during nextTick the focus gets changed.
- Checking, if the container equals document.activeElement
const hasFocusedCandidate = container.contains(previouslyFocusedElement)
If yes, set hasFocusedCanditate to false
const hasFocusedCandidate = container.contains(previouslyFocusedElement) && document.activeElement !== container
That way we would rely on the implementation of the handleMutation, that it changes the focus to container on failure. Don't like it.
- Using a flag to disable the handleMutations.
initEffectsActive += 1
await nextTick().finally(() => initEffectsActive -= 1);
function handleMutations(mutations: MutationRecord[]) {
if(initEffectsAtive > 0)
return;
const isLastFocusedElementExist = container.contains(lastFocusedElementRef.value)
if (!isLastFocusedElementExist)
focus(container)
}
This would be actually my preferred way, as it is clear, that we don't want the MutationObserver to fix the focus, while we haven't init ourself during the watchEffect. As the watchEffect actually has the task to set the focus initially.
And if some user-code sets the focus during nextTick somewhere else the init routine wouldn't mess with it, as hasFocusedCandidate would be true as a result.
It is a counter, as the async function gets called twice before the first is actually finished.
from radix-vue.
Sorry for spamming, I've optimized the 3rd option.
I've never done a pull request before. But I could try tomorrow. It is already late here.
I wrote this code here blind without IDE, might have still typos.
function useAsyncExecuting<T>(promise: () => Promise<T>)
{
let counter = 0;
async function execute()
{
try
{
counter += 1;
return await promise();
}
finally
{
counter -= 1;
}
}
function isExecuting()
{
return counter > 0;
}
return {
execute,
isExecuting
}
}
const { execute: executeNextTick, isExecuting: isExecutingNextTick } = useAsyncExecuting(nextTick);
function handleMutations(mutations: MutationRecord[]) {
// Do not handle mutations for the duration of nextTick during the initialization
// The initialization will take care of finding and focusing the first element
if(isExecutingNextTick())
return;
const isLastFocusedElementExist = container.contains(lastFocusedElementRef.value)
if (!isLastFocusedElementExist)
focus(container)
}
During the async watchEffect:
await executeNextTick();
from radix-vue.
Thanks for digging into this @heladrion ! Instead of using a workaround for this.. I might need to might the root cause of why the fetch
is causing the nextTick
to behave differently, causing the previouslyFocusedElement
to be different.
FIY: I've tried your suggest solution and it's breaking tests. So it might not be feasible.
from radix-vue.
After testing for quite a bit, I believe your proposed solution #1 previouslyFocusedElement
should fix the issue. As it make sense to capture the previously focused element before we have the nextTick
.
from radix-vue.
I digged today into Vue itself with the SFC Playground, but couldn't find any differences between the calls. But it is super hard to debug.
Yes #1 previouslyFocusedElement
should do the trick. But the handleMutation is still happening and the handleMutation temporarily moves the focus to the body. Before the actual autofocus-onMount call. I'm scared, that this might just hide the problem, but I guess every solution has the problem in the end :/
I fixed version #3
https://github.com/heladrion/radix-vue/commits/fix(FocusScope)-autoFocus-not-working%2C-when-fetch-called-during-a-watcher/
All tests are ok, but I also had to change AlertDialogContent.vue
@open-auto-focus="
() => {
nextTick(() => {
cancelElement?.focus({
preventScroll: true,
});
});
}
"
to the following
@open-auto-focus.prevent="(event) => {
cancelElement?.focus({
preventScroll: true,
})
}"
which is actually more in line with radix-ui
https://github.com/radix-ui/primitives/blob/c31c97274ff357aea99afe6c01c1c8c58b6356e0/packages/react/alert-dialog/src/AlertDialog.tsx#L131
If someone wants to debug it deeper on Chrome, here is the Vue SFC Playground URL for localhost on port 5173. The files are encoded in the URL, so it should work.
localhost:5173 Playground
from radix-vue.
Related Issues (20)
- [Feature]: DateField locale vs language vs format HOT 6
- [Bug]: `radix-vue/resolver` type export error
- [Feature]: Grid System Support HOT 2
- [Feature]: Add unit / pixel unit for panel size HOT 1
- [Feature]: Expose Toast remaining duration
- [Bug]: Popover component misses aria-label HOT 1
- [Feature]: Consider switching tabs on `@mouseup.left` instead of `@mousedown.left` HOT 2
- [Bug]: Combobox filtering stops working with a large amount of items
- [Feature]: the ability to disable switching the focus of a document to a DropdownMenuItem on hover
- [Bug]: Tabs and Resizable components do not work when I set `e.preventDefault()` in the `pointerdown` function HOT 2
- [Bug]: Tooltip not showing up when rendered on the server HOT 2
- [Bug]: Multiple tooltips displayed at once because of grace area
- [Feature]: Add `data-placeholder` to date picker input HOT 3
- [Bug]: Tooltip not showing up when rendered on the server HOT 3
- [Bug]: Popover component tests in happy-dom HOT 6
- [Bug]: Tooltips inside Collapible (Precense) extremely slow to render. HOT 1
- [Bug]: Setting `disabled` for `RadioGroupRoot` won't disable individual `RadioGroupItem`
- [Bug]: Incompatibility between VueUse useTextDirection and the dir prop of ConfigProvider HOT 1
- [Feature]: Ability to select the first item with Enter keydown even if it is not highlighted on Listbox HOT 1
- [Bug][Combobox]: Incorrect `searchTerm` initialization HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from radix-vue.