* splitup CHANGELOG.md file
Scope each changelog per package
* simplify CHANGELOG.md files
We don't need to scope them anymore, they are already scoped.
* remove leftover code
This code existed before we had the option to make the first option the
"active" one.
This also contains a bug in the React code where pressing "ArrowDown" in
a closed Combobox opens the combobox and goes to the second item instead
of the first option.
* update changelog
* add `@headlessui/tailwindcss` plugin
* expose `data-headlessui-state="..."` data attribute
All components that expose boolean props in their render prop / v-slot
will receive a `data-headlessui-state="..."` attribute.
If it exposes boolean values but all are false, then there will be an
empty `data-headlessui-state=""`. If the current component is rendering
a `Fragment` then we don't expose those attributes.
* use tailwindcss in `playground-react` and `playground-vue`
We were using the CDN, but now that we have the
`@headlessui/tailwindcss` plugin, it's a bit easier to configure it
natively and import the plugin.
* ensure to build the `@headlessui/tailwindcss` package before starting the playground
* refactor `listbox` example to use the @headlessui/tailwindcss plugin
* update changelog
* bump Tailwind CSS to latest insiders version
* correctly generate types
* type `tailwind.config.js` files for playgrounds
* add todo for when `:has()` is available
In the cleanup PR, we added the `Data` and `Actions` type, but we
already had a `Actions` type so had to rename it to something. Chose
`Command` but this is now inconsistent with the rest of the codebase.
Instead, let's revert that change and use these shorthands:
- `Data` -> `_Data`
- `Actions` -> `_Actions`
- `Commands` -> `Actions`
- `CommandTypes` -> `ActionTypes`
The `_` prefix is a little bit strange, but it is a private type and not
exposed so fine for now.
* sort React imports
* improve type signature of the `useEvent` hook
* use more correct `useIsoMorphicEffect` check in `useEvent`
* refactor `useCallback` to cleaner `useEvent`
* convert `const` to `let`
Just for consistency..
* cleanup `Tabs` code
Created explicit functions that can be called from child components
instead of calling `dispatch` directly. Introduced a `useData` and
`useActions` hook to make child components easier.
The seperation of `useData` allows us to pass down props directly
instead of going via the `useReducer` hook and dispatching actions to
make values up to date.
* cleanup `Combobox` code
* cleanup `RadioGroup` code
* make the ref optional in the `Popover` component
We "required" the prop to calculate the `ownerDocument`. But if you
don't provide a ref, then we will use the `Popover.Button` to calculate
it. If that's not defined, then we can fallback to the default
`document`.
* update changelog
* improve scroll lock, scrollbarWidth
The idea is as follow:
If you currently have a scrollbar, and you open a Dialog then we enable
a "Scroll lock" so that you can't scroll in the background behind the
modal. We can achieve this by adding a `overflow: hidden;` to the
`html`.
The issue is that by doing this, we lose the scrollbar and therefore the
page will jump to right because now there is a bit more room.
To account for this, we set a `padding-right` on the `html` of the
scrollbarWidth in pixels. This counteracts the visual jump you would
see.
The issue with this approach is that there could *still* be a scrollbar
once we add the `overflow: hidden`. This can happen if you use new css
features like the `scrollbar-gutter: stable;`.
To take this into account, we will measure the scrollbar again after we
set the `overflow: hidden`. Now we will only apply that counteracting
offset if there would actually be a jump by measuring the before and
after widths and applying the diff if there is one.
* update changelog
* improve `Popover` keyboard usage
Use `TabSentinel` instead of intercepting the `Tab` keydown events.
* use Buttons in Popover example
* update changelog
* refactor `VisuallyHidden` to `Hidden` component
This new component will also make sure that it is visually hidden to
sighted users. However, it contains a few more features that are going
to be useful in other places as well. These features include:
1. Make visually hidden to sighted users (default)
2. Hide from assistive technology via `features={Features.Hidden}`
(will add `display: none;`)
3. Hide from assistive technology but make the element focusable via
`features={Features.Focusable}` (will add `aria-hidden="true"`)
* add `useEvent` hook
This will behave the same (roughly) as the new to be released `useEvent`
hook in React 18.X
This hook allows you to have a stable function that can "see" the latest
data it is using. We already had this concept using:
```js
let handleX = useLatestValue(() => {
// ...
})
```
But this returned a stable ref so you had to call `handleX.current()`.
This new hook is a bit nicer to work with but doesn't change much in the
end.
* add `useTabDirection` hook
This keeps track of the direction people are tabbing in. This returns a
ref so no re-renders happen because of this hook.
* add `useWatch` hook
This is similar to the `useEffect` hook, but only executes if values are
_actually_ changing... 😒
* add `microTask` util
* refactor `useFocusTrap` hook to `FocusTrap` component
Using a component directly allows us to simplify the focus trap logic
itself. Instead of intercepting the <kbd>Tab</kbd> keydown event and
figuring out the correct element to focus, we will now add 2 "guard"
buttons (hence why we require a component now). These buttons will
receive focus and if they do, redirect the focus to the first/last
element inside the focus trap.
The sweet part is that all the tabs in between those buttons will now be
handled natively by the browser. No need to find the first non disabled,
non hidden with correct tabIndex element!
* refactor the `Dialog` component to use the `FocusTrap` component
Also added a hidden button so that we know the correct "main" tree of
the application. Before this we were assuming the previous active
element which will still be correct in most cases but we don't have
access to that anymore since the logic is encapsulated inside the
FocusTrap component.
* ensure `<Portal />` properly cleans up
We make sure that the Portal is cleaning up its `element` properly.
We also make sure to call the `target.appendChild(element)`
conditionally because I ran into a super annoying bug where a focused
element got blurred because I believe that this re-mounts the element
instead of 'moving' it or just ignoring it, if it already is in the
correct spot.
* refactor: use `useEvent` instead of `useLatestValue`
Not really necessary, just cleaner.
* update changelog
* ignore `Escape` when event got prevented
Some external libraries only use `event.preventDefault()` and not
`event.stopPropagation()`. This means that the Dialog can still receive
an `Escape` keydown event which closes the Dialog.
We can also think about the `Escape` behaviour inside the modal as the
"default behaviour" once the Dialog is open. Therefore, we can also
check the `event.defaultPrevented` and ignore this event when this is
the case.
* update changelog
* manually pass through `attrs`
Due to the return of the Fragment (for form compatibility) the
attributes will now be pass onto this Fragment instead of the underlying
DOM node. To fix this, we disable the `inheritAttrs` magic, and
passthrough the attributes to the correct component.
* update changelog
* cleanup README files
* ignore flakey tests for now
There are a handful of tests that are pretty flakey and fail every once
in a while on CI, I don't want to remove them yet, but rather ignore
them for now.
I am going to experiment with using Playwright/Puppeteer to use a real
browser instead.