Files
headlessui/playgrounds
Robin Malfait 6ac6930116 Implement sibling <Dialog /> components (#3242)
* add `DefaultMap` implementation

* add `useHierarchy` hook

* start `FocusTrapFeatures.None` with `0` instead of `1`

* simplify `Dialog`'s implementation

By making use of the new `useHierarchy` hook.

* delete `StackContext` and `StackProvider` components

They are now replaced by the new `useHierarchy` hook.

* use `useHierarchy` in `useOutsideClick` hook

This way we can scope the hierarchy inside of the `useOutsideClick`
hook. This now ensures that we only enable the `useOutsideClick` on the
top-most element (the one that last enabled it).

* use `useHierarchy` in `useInertOthers` hook

* add new `useEscape` hook

* use new `useEscape` hook

* use `useHierarchy` in `useEscape` hook

* use `useHierarchy` in `useScrollLock` hook

* pass features instead of `enabled` boolean

* simplify demo mode feature flags

No need to setup focus feature flags and then disable it all again if
demo mode is enabled.

* use similar signature for hooks with `enabled` parameter

Whenever a hook requires an `enabled` state, the `enabled` parameter is
moved to the front. Initially this was the last argument and enabled by
default but everywhere we use these hooks we have to pass a dedicated
boolean anyway.

This makes sure these hooks follow a similar pattern. Bonus points
because Prettier can now improve formatting the usage of these hooks.
The reason why is because there is no additional argument after the
potential last callback.

Before:
```ts
let enabled = data.__demoMode ? false : modal && data.comboboxState === ComboboxState.Open
useInertOthers(
  {
    allowed: useEvent(() => [
      data.inputRef.current,
      data.buttonRef.current,
      data.optionsRef.current,
    ]),
  },
  enabled
)
```

After:
```ts
let enabled = data.__demoMode ? false : modal && data.comboboxState === ComboboxState.Open
useInertOthers(enabled, {
  allowed: useEvent(() => [
    data.inputRef.current,
    data.buttonRef.current,
    data.optionsRef.current,
  ]),
})
```

* move `focusTrapFeatures` parameter to the front

* drop `FocusTrapFeatures.All`, list them explicitly

The `All` feature didn't include all feature flags (didn't include
`FocusTrapFeatures.AutoFocus` for example).

* always enable `FocusTrapFeatures.RestoreFocus` when enabled

This way we can get rid of the `Position.HasChild` check, which will
allow us to do more cleanup soon in the `useHierarchy` hook.

* use `useHierarchy` in `<FocusTrap>` component

* drop `useHierarchy` from `<Dialog>` component

* simplify focusTrapFeatures setup

* simplify `useHierarchy`

The `useHierarchy` hook allowed us to determine whether we are in the
root, in the middle, a leaf, have a parent, have a child, ... but we
only ever checked whether we are a leaf node or not.

In other words, you can think of it like "are we the top layer"

This simplifies the implementation and usage of this new hook.

* move `enabled`-like argument to front

Just to be consistent with the other hooks.

* polyfill `toSpliced` for older Node versions

* add sibling dialogs playground

* rename `useHierarchy` to `useIsTopLayer`

* inline variable

* remove `unstable_batchedUpdates`

This is not necessary.

* add tiny bit of information to dialog

Because it might not be super obvious that we are going to close the
`<Dialog />`.

* update changelog

* re-add internal `PortalGroup`

This is necessary to make sure that a component like a `<MenuItems
anchor />` is rendered inside of the `<Dialog />` and not as a sibling.

While this all works from a functional perspective, if you rely on a
CSS variable that was defined on the `<Dialog />` and you use it in the
`<MenuItems />` then without this change it wouldn't work.
2024-05-27 23:35:38 +02:00
..