Closed vincerubinetti closed 5 months ago
Nevermind, I restarted my dev server and the problem was gone, sorry.
I have the same problem. Restrting dev server is not helping.
An element ref is always null
when inside <Float>
and works fine if moved outside <Float>
.
I also tried to use the function ref
syntax to check if the ref setter is ever called.
<script lang="ts" setup>
const outsideInputRef = ref<HTMLInputElement | null>(null)
function setOutsideInputRef(input: HTMLInputElement | null) {
console.log('setOutsideInputRef', input)
outsideInputRef.value = input
}
const floatRefInputRef = ref<HTMLInputElement | null>(null)
function setFloatRefInputRef(input: HTMLInputElement | null) {
console.log('setFloatRefInputRef', input)
floatRefInputRef.value = input
}
</script>
<template>
<div>
<input
:ref="setOutsideInputRef"
type="text"
value="Outside input"
/>
<Float>
<input
:ref="setFloatRefInputRef"
type="text"
value="Float ref input"
/>
<div>Float div</div>
</Float>
</div>
</template>
When I tried it in a Vue playground on Stackblitz, it worked fine.
But when I tried it in the Nuxt playground (with @headlessui-float/nuxt
module), the second setter (setFloatRefInputRef
) is never called.
@ycs77 Could you please re-open this issue to investigate this problem?
here is the reproducible example.
A workaround:
Instead of:
<Float>
<input :ref="setFloatRefInputRef" type="text" value="Float ref input" />
<div>Float div</div>
</Float>
do this:
<Float>
<div>
<input :ref="setFloatRefInputRef" type="text" value="Float ref input" />
</div>
<div>Float div</div>
</Float>
I.e. wrap Float
reference into div
. This way :ref="setFloatRefInputRef"
starts to work.
setFloatRefInputRef
is called 3-30 times for some reason, but it is better than 0 times. :)
I wonder if the as
property could help you here? Like <Float as="template">
.
Perhaps the <Float>
component should pass down a ref
slot prop that you can merge with your own ref (like you would in react, not actually sure that's necessary or possible in Vue).
I tried <Float as="template">
. Unfortunately it doesn't help.
What I wonder is why it works fine in "pure Vue" environment, but doesn't work or behaves strange (if wrapped in div
) in Nuxt environment.
I tested the "pure Vue" version and it works both wrapped in div
and unwrapped. Moreover, even when wrapped then setFloatRefInputRef
is still called only once.
In Nuxt sample wrapped version works, but setFloatRefInputRef
is called 3 times on initial render and then 2 times more every time when the browser window is resized. In my local project setFloatRefInputRef
is called 25-30 times on initial render. 😨
With "pure Vue" example even resizing the browser window doesn't lead to calling setFloatRefInputRef
function again. It is called exactly 1 time: on the initial render of the referenced element.
Anyway, it looks like the problem appears only when headlessui-float
is used with Nuxt, or at least I didn't able to reproduce the problem with pure Vue setup. But, unfortunately, I can't find the root of the problem. Tried to inspect the source code of the headlessui-float library, but it is too much to me at the moment. :) I'm too unfamiliar with the low level manipulation of component children that this library does.
At least, the problem is perfectly reproduced and I linked the example, so I hope @ycs77 will take a look when have a time.
Hi @xak2000
First is your pure Vue example missing the import { Float } from '@headlessui-float/vue'
to import the <Float>
component, after added you will see the Vue example has the same problem and Nuxt example. And the Nuxt will work with no import component because the Headless UI Float's Nuxt module will auto-import the component.
Then the solution is still this:
<Float>
<div>
<input :ref="setFloatRefInputRef" type="text" />
</div>
<div>Float div</div>
</Float>
When mentioning the reason, we need to explain the working principle of the <Float>
component. The <Float>
component gets the 2 child elements in the slot, the reference element and the floating element, and binds the ref of 2 child elements to bring the elements dom, then can pass the 2 child dom elements for Floating UI to positioning.
The Vue can't merge refs like React. Therefore, it cannot be re-bind externally when it has been bind once internally (in the <Float>
component). So you will wrap an element like <div>
for the reference element and the floating element, this will solve it.
Hi @ycs77
Thank you very much for the explanation. This does make sense.
Do you think this trait (inability to use ref
on direct children of the Float
) should be documented? I spent literaly 2 days scratching my head and experimenting (shame on me that I didn't try the easiest solution with a wrapper earlier).
Probably it will be good to left a note in the documentation with description of limitations (ref
doesn't work, maybe something else too?). It would prevent long hours of head-scratching for other folks. :)
Do you think this trait (inability to use
ref
on direct children of theFloat
) should be documented? I spent literaly 2 days scratching my head and experimenting (shame on me that I didn't try the easiest solution with a wrapper earlier).Probably it will be good to left a note in the documentation with description of limitations (
ref
doesn't work, maybe something else too?). It would prevent long hours of head-scratching for other folks. :)
This would be great, and give an explanation of how to solve the real problem. I am trying to solve this same problem but on the floating element side. I place a ref ListboxOptions for example but when mounting the component always gives null
I tried to place a div wrapping the ref as the solution that you propose but still gives null. So it seems that it does not apply the same solution with the floating element.
Do you think this trait (inability to use
ref
on direct children of theFloat
) should be documented? I spent literaly 2 days scratching my head and experimenting (shame on me that I didn't try the easiest solution with a wrapper earlier).Probably it will be good to left a note in the documentation with description of limitations (
ref
doesn't work, maybe something else too?). It would prevent long hours of head-scratching for other folks. :)
@xak2000 I think it should be possible. I've added it https://headlessui-float.vercel.app/vue/render-wrapper.html#notice-of-template-ref
Hi @mreduar, if you has any problem, please open a new issue and provide enough information and a minimal reproducible example.
Versions "@headlessui-float/vue": "^0.13.2", "@headlessui/vue": "^1.7.19", "vue": "^3.4.21",
Describe the bug
Regular Vue refs to raw elements seem to break when inside the
<Float>
component.To Reproduce
An example, trimmed down to just the relevant parts: