c5f95b02af
This PR fixes an issue where the scroll locking logic was incorrectly re-enabled in Dialogs if you were using a `Transition` component or a `transition` prop _and_ you had nested components with the `transition` prop (or a nested `TransitionChild` component) _and_ the parent transition finishes before any of its children. To visualize this, it would happen in this situation: ```tsx <Dialog transition> /* No transition classes */ <DialogBackdrop transition className="duration-500" /> <DialogPanel transition className="duration-200" /> </DialogPanel> </Dialog> ``` With the `transition` prop, internally these components would render a wrapper `Transition` component. The `Dialog` will look at the open/closed state provided by the `Transition` component to know whether to unmount its children or not. The `Dialog` component also has some internal hooks to make it behave as a dialog. One of those hooks is the `useScrollLock` hook. This hook will be enabled if the `Dialog` is open and disabled when it's closed. If you are using the `Transition` component or the `transition` prop, then we have to make sure that the `useScrollLock` gets disabled immediate, and not wait until the transition completes. This is done by looking at the `Closing` state. The reason for this is that disabling the `useScrollLock` also means that we restore the scroll position. But if you in the meantime navigate to a different page which also changes the scroll position, then we would restore the scroll position on a totally different page. We already had this logic setup, but the problem is that the `Closing` state was incorrectly derived from the transition state. That state was only looking at the current component (in the example above, the `Dialog` component) but not at any of the child components. Since the `Dialog` didn't have any transitions itself, the `Closing` state was only briefly there. If there is no `Closing` state, then the `useScrollLock` is looking at the `open` state of the `Dialog`. Because other child components were still transitioning, the `Dialog` was still in an open state. This actually **re-enabled** the `useScrollLock` hook. Because from the `Dialog`s perspective no transitions were happening anymore. Eventually the transitions of all the children completed causing the `Transition` and thus the `Dialog` to unmount. This in turn caused the `useScrollLock` hook to also clean up and restore the scroll position. But as you might have guessed, now this second time, it's restoring _after_ the transition is done. Luckily, the fix is simple. Make sure that the `Closing` state also keeps the full hierarchy into account and not only the state of the current element.