headlessui icon indicating copy to clipboard operation
headlessui copied to clipboard

--button-width incorrect when parent is rendered with an animation

Open RyanMcDonald opened this issue 1 year ago • 3 comments

What package within Headless UI are you using?

For example: @headlessui/react

What version of that package are you using?

For example: v2.2.0

What browser are you using?

Chrome

Reproduction URL

https://codesandbox.io/p/devbox/stupefied-rosalind-cfyp2k

Describe your issue You can see in the repro URL I'm animating a Dialog that contains a Listbox, and the ListboxOptions don't take up the full width of the ListboxButton.

If an animation involving scale is used to show the content, --button-width is calculated before the animation finishes so it will be incorrect. The implementation of useElementSize uses a ResizeObserver, but that doesn't call the callback when its size changes via animation. It looks like the ResizeObserver's entry.borderBoxSize has the correct values, so maybe that would be an acceptable solution?

RyanMcDonald avatar Dec 19 '24 06:12 RyanMcDonald

I have the same issue as well, I have a form rendered in a dialog

abolajibisiriyu avatar Dec 27 '24 02:12 abolajibisiriyu

Same issue, does anyone have a solution?

BratislavZdravkovic avatar Feb 05 '25 15:02 BratislavZdravkovic

Has anyone had a solution for this?

fusionfitdevs avatar Apr 23 '25 03:04 fusionfitdevs

Probably a bad solution, but you can access the state when the menu opens and recalculate it manually. Probably can make it more efficient by also tracking if the width was updated.

Very simplified version (keep rest of your props, including anchor, etc):

Combobox:

const comboboxInputRef = useRef<HTMLInputElement>(null);
const comboboxOptionsRef = useRef<HTMLDivElement>(null);

const setInputWidth = () => {
  if (comboboxInputRef.current && comboboxOptionsRef.current) {
    const inputWidth = comboboxInputRef.current.getBoundingClientRect().width;
    comboboxOptionsRef.current.style.setProperty('--input-width', `${inputWidth}px`);
  }
}

...

<Headless.Combobox {...props}>
      {({ open }) => {
        if (open) {
          setInputWidth();
        }
...

// set ref to ComboboxInput
<Headless.ComboboxInput ref={comboboxInputRef}

...

// set ref to ComboboxOptions
<Headless.ComboboxOptions ref={comboboxOptionsRef} className="w-[var(--input-width)]"

Select:

const listboxOptionsRef = useRef<HTMLDivElement>(null);
const listboxButtonRef = useRef<HTMLButtonElement>(null);

const setButtonWidth = () => {
  if (listboxButtonRef.current && listboxOptionsRef.current) {
    const buttonWidth = listboxButtonRef.current.getBoundingClientRect().width;
    listboxOptionsRef.current.style.setProperty('--button-width', `${buttonWidth}px`);
  }
}

...

<Headless.Listbox {...props}>
      {({ open }) => {
        if (open) {
          setButtonWidth();
        }

...

// Set ref to ListboxButton
<Headless.ListboxButton ref={listboxButtonRef}

...

// Set ref to ListboxOptions
 <Headless.ListboxOptions ref={listboxOptionsRef} className="w-[var(--button-width)]"

cc: @abolajibisiriyu @BratislavZdravkovic @fusionfitdevs

tarasvarshava avatar Sep 08 '25 07:09 tarasvarshava

This should be fixed by #3786, and will be available in the next release.

You can already try it using:

  • npm install @headlessui/react@insiders.

RobinMalfait avatar Sep 08 '25 14:09 RobinMalfait