Commit Graph

74 Commits

Author SHA1 Message Date
Robin Malfait bc4a744947 fix incorrect activeIndex when handling Focus.Previous
This is already available in the React version, now let's sync it in the
Vue version as well.
2024-01-06 01:51:07 +01:00
Robin Malfait e662f12398 2.0.0 Alpha prep (#2887)
* bump React & React DOM dependencies

* fix typo `TOmitableProps` → `TOmittableProps`

* bump prettier

* run prettier after prettier version bump

* bump TypeScript

* run prettier after TypeScript version bump

* enable `verbatimModuleSyntax`

This ensures all imported types are using the `type` keyword.

* add `type` to type related imports

* add common testing scenarios

Will be used in the new and existing components.

* add script to make Next.js happy

Right now Next.js does barrel file optimization and re-writing imports
to a real path in the `dist` folder. Most of those rewrites don't
actually exist because they have an assumption:

```js
import { FooBar } from '@headlessui/react'
```

is rewritten as:
```js
import { FooBar } from '@headlessui/react/dist/components/foo-bar/foo-bar'
```

This script will make sure these paths exist...

* improve `by` prop, introduce `useByComparator`

This hook has a default implementation when comparing objects. If the
object contains an `id`, then we will compare the objects by their
`id`'s without the user of the library needing to specify `by="id"`.

If the objects don't have an `id` prop, then the default is still to
compare by reference (unless specicified otherwise).

* sync yarn.lock

* rename `Features` to `HiddenFeatures` for `Hidden` component

* rename `Features` to `FocusTrapFeatures` in `FocusTrap` component

* rename `Features` to `RenderFeatures` in `render` util

* add `floating-ui` as a dependency + introduce internal floating related components

* bump Vue dependencies

* ensure scroll bar calculations can't go negative

* improve types in `@headlessui/vue`

* use snapshot tests for `Transition` tests in `@headlessui/vue`

* use snapshot tests for `portal` tests in `@headlessui/vue`

* rename `src/components/transitions/` to `src/components/transition/` (singular)

This is so that we can be consistent with the other components.

* drop custom `toMatchFormattedCss`, prefer snapshot tests instead

* use snapshot tests for `Label` tests in `@headlessui/vue`

* use snapshot tests for `Description` tests in `@headlessui/vue`

* sort exported components in tests for `@headlessui/vue`

* use snapshot tests in `@headlessui/tailwindcss`

* rename `mergeProps` to `mergePropsAdvanced`

This is a more complex version of a soon to be exported `mergeProps`
that we will be using in our components.

* do not expose `aria-labelledby` if it is only referencing itself

* expose boolean state as `kebab-case` instead of `camelCase`

These are the ones being exposed inside `data-headlessui-state="..."`

* expose boolean data attributes

A slot with `{active,focus,hover}` will be exposed as:
```html
<span data-headlessui-state="active focus hover"></span>
```

But also as boolean attributes:
```html
<span data-active data-focus data-hover></span>
```

* improve internal types for `className` in `render` util

* ensure we keep exposed data attributes into account when trying to forward them to the component inside the `Fragment`

* add small typescript type fix

This is internal code, and the public API is not influenced by this
`:any`. It does make TypeScript happy.

* introduce `mergeProps` util to be used in our components

This will help us to merge props, when event handlers are available they
will be merged by wrapping them in a function such that both (or more)
event handlers are called for the same `event`.

* add new internal `Modal` component

* fix: when using `Focus.Previous` with `activeIndex = -1` should start at the end

* prefer `window.scrollY` instead of `window.pageYOffset`

Because `window.pageYOffset` is deprecated.

* add `'use client'` directives on client only components

These components use hooks that won't work in server components and you
will receive an error otherwise.

* drop `import 'client-only'` in favor of the `'use client'` directive

* add React Aria dependencies

* pin beta dependencies

* prettier bump formatting

* improve TypeScript types in tests

* use new Jest matchers instead of deprecated ones

* improve typescript types in Vue

* prefer `useLabelledBy` and `useDescribedBy`

* add internal `DisabledProvider`

* add internal `IdProvider`

* add internal `useDidElementMove` hook

* add internal `useElementSize` hook

* add internal `useIsTouchDevice` hook

* add internal `useActivePress` hook

* use snapshot tests for `Description` tests

* use snapshot tests for `Label` tests

* use snapshot tests for `Portal` tests

* use snapshot tests for `render` tests

* add (private) `Tooltip` component

Currently this one is not ready yet, so its not publicly exposed yet.

* add internal `FormFields` component

This one adds a component to render (hidden) inputs for native form
support. It also ensures that form fields can be hoisted to the end of
the nearest `Field`. If the components are not inside a `Field` they
will be rendered in place.

* add new `Button` component

* add new `Checkbox` component

* add new `DataInteractive` component

* add new `Field` component

* add new `Fieldset` component

* add new `Legend` component

* add new `Input` component

* add new `Select` component

* add new `Textarea` component

* export new components

* WIP

* remove `within: true`

This only makes sense if anything inside the current element receives
focus, which is not the case for `input`, `select`, `textarea` or
`Radio/RadioOption`.

* group focus/hover/active hooks together

* conditionally link anchor panel

* immediately focus the container

* prevent premature disabling of `Listbox`'s floating integration

+ Track whether the button moved or not when disabling such that we can
  disable the transitions earlier.

* improve scroll locking on iOS

* skip hydration tests for now

* skip certain focus trap tests for now

* update CHANGELOG.md

* add missing requires

* drop unused `@ts-expect-error`

* ignore type issues in playgrounds

These playgrounds are mainly test playgrounds. Lower priority for now,
we will get back to them.

* add yarn resolutions to solve swc bug
2023-12-20 19:57:57 +01:00
Jordan Pittman c25e2e6036 Fix CJS types (#2880)
* Fix Vue type error

* Add separate CTS types

* Add “Are The Types Wrong” CLI

* wip

* Bump node versions in workflows

* wip

* wip

* wip

* yolo

* yolo (again?)

* wip

* wip
2023-12-12 11:43:34 -05:00
Jordan Pittman 20a224aaa4 Fix state data attribute in Vue (#2787)
* Add tests

* Fix state data attribute in Vue
2023-10-04 13:39:26 -04:00
Robin Malfait 99cdf91631 Implement new virtual API for the Combobox component (#2779)
* add `(Vue)` or `(React)` to playground header

* show amount of items in virtualized example

* improve calculating the active index

* disable strict mode

* update virtualized playground examples with preferred API

* optimize `calculateActiveIndex`

* implement new `virtual` API

* update changelog
2023-10-02 11:55:20 +02:00
Robin Malfait f2179f36c0 further improve import sorting 2023-09-11 19:09:53 +02:00
Robin Malfait 76dd10ea55 Sort imports automatically (#2741)
* add `prettier-plugin-organize-imports` and `prettier-plugin-tailwindcss`

* format

* bump Tailwind CSS

* format playgrounds using updated Tailwind CSS and Prettier plugins

* use import syntax
2023-09-11 18:36:30 +02:00
Timur Zurbaev fa952626c7 Add immediate prop to <Combobox /> for immediately opening the Combobox when the input receives focus (#2686)
* Allow to open combobox on input focus

* Close focused combobox with openOnFocus prop when clicking the button

* ensure tabbing through a few fields, doesn't result in an incorrectly selected item

When you have a fwe inputs such as:

```html
<form>
   <input />
   <input />
   <input />
   <Combobox>
      <Combobox.Input />
   </Combobox>
   <input />
   <input />
   <input />
</form>
```

Tabbing through this list will open the combobox once you are on the
input field. When you continue tabbing, the first item would be
selected. However, if the combobox is not marked as nullable, it means
that just going through the form means that we set a value we can't
unset anymore.

We still want to open the combobox, we just don't want to select
anything in this case.

* only `openOnFocus` if the `<Combobox.Input />` is focused from the
outside

If the focus is coming from the `<Combobox.Button />` or as a side
effect of selecting an `<Combobox.Option />` then we don't want to
re-open the `<Combobox />`

* update tests to ensure that the `Combobox.Input` is the active element

* order `handleBlur` and `handleFocus` the same way in Vue & React

* only select the active option when the Combobox wasn't opened by focusing the input field

* convert to `immediate` prop on the `Combobox` itself

* update changelog

* ensure we see the "relatedTarget" in Safari

Safari doesn't fire a `focus` event when clicking a button, therefore it
does not become the `document.activeElement`, and events like `blur` or
`focus` doesn't set the button as the `event.relatedTarget`.

Keeping track of a history like this solves that problem. We already had
the code for the `FocusTrap` component.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2023-08-31 15:23:56 +02:00
Jordan Pittman 5a1e2e4e85 Don't overwrite user-defined template refs when rendering (#2720)
* Merge vnode refs when rendering

In some cases if we used our own ref (we do this in `<TransitionRoot>` for instance) and rendered slot children we would wipe out  user-specified refs. So we set a flag when calling `cloneVNode` to merge our refs and any user-specified refs.

* Update changelog
2023-08-30 12:52:17 -04:00
Jordan Pittman c92757dded Allow <button> to be in nested components in <PopoverButton> (#2715)
* Tweak `dom()` helper

The helper can currently return a component instance when it should only ever return a DOM element. So, we fix the implementation to return null if it’s not an `Element` _and_ adjust the types such that if a `ComponentPublicInstance` is passed we change the return type to `Element`.

* Specialize DOM helper to HTML elements

Technically it could be an SVG element but much of Headless UI assumes HTML elements all over. So we’ll adjust the types to assume HTMLElement instead.

* Allow `dom()` helper to return any `Node` type

It doesn’t actually always return an HTMLElement but we have behavior that relies on it returning and checking for `Comment` nodes

* Detect `<button>` nested in components inside `<PopoverButton>`

* Update changelog
2023-08-30 18:19:14 +02:00
Robin Malfait 6f9de8925e Disable smooth scrolling when opening/closing Dialog components on iOS (#2635)
* disable smooth scrolling when opening/closing Dialogs

For iOS workaround related purposes we have to capture the scroll
position and offset the margin top with that amount and then
`scrollTo(0,0,)` to prevent all kinds of funny UI jumps.

However, if you have `scroll-behavior: smooth` enabled on your `html`,
then offseting the margin-top and later `scrollTo(0,0)` would be
handled in a smooth way, which means that the actual position would be
off.

To solve this, we disable smooth scrolling entirely in order to make the
position of the Dialog correct. This shouldn't be a problem in practice
since the page itself isn't suppose to scroll anyway.

Once the Dialog closes we reset it such that everything else keeps
working as expected in a (smooth) way.

* add `microTask` to disposables

* ensure the fix works in React's double rendering dev mode

* update changelog
2023-08-02 17:45:06 +02:00
Robin Malfait 9b42dafd7f improve attemptSubmit submission (#2625)
When you pass in an element to the `attemptSubmit` that has a
`type="submit"`, then the `attemptSubmit` will just click this element.

We want to skip the current one and fallback to `form.requestSubmit()`
instead.
2023-07-28 16:33:55 +02:00
Robin Malfait ac859fe6b1 Submit form on Enter even if no submit-like button was found (#2613)
* `requestSubmit` when a submit-like button cannot be found

* add tests

* update changelog
2023-07-25 15:48:35 +02:00
Robin Malfait 67f3c4d824 Improve control over Menu and Listbox options while searching (#2471)
* add `get-text-value` helper

* use `getTextValue` in `Listbox` component

* use `getTextValue` in `Menu` component

* update changelog

* ensure we handle multiple values for `aria-labelledby`

* hoist regex

* drop child nodes instead of replacing its innerText

This makes it a bit slower but also more correct. We can use a cache on
another level to ensure that we are not creating useless work.

* add `useTextValue` to improve performance of `getTextValue`

This will add a cache and only if the `innerText` changes, only then
will we calculate the new text value.

* use better `useTextValue` hook
2023-05-04 14:41:44 +02:00
Kairui Liu d0888b03b5 Add FocusTrap event listeners once document has loaded (#2389)
* feat: addEventListener on document loaded

* Refactor

* Fix import

* Update changelog

* use function instead of arrow function

* make callback in `onDocumentReady` mandatory

---------

Co-authored-by: lkr <lkr@bytedance.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2023-03-22 13:37:07 +01:00
Robin Malfait 0c0601f87a Fix focus styles showing up when using the mouse (#2347)
* update playground examples to use a shared `Button`

* expose a `ui-focus-visible` variant

* keep track of a `data-headlessui-focus-visible` attribute

* do not set the `tabindex`

The focus was always set, but the ring wasn't showing up. This was also
focusing a ring when the browser decided not the add one.

Let's make the browser decide when to show this or not.

* update changelog
2023-03-10 22:00:35 +01:00
Robin Malfait 213dd529bf Ensure Transition component completes if nothing is transitioning (#2318)
* make `disposables` consistent

Also added a `group` function, this allows us to spawn a _sub_
disposables group that can be disposed on its own, but will also be
disposed the moment the "parent" is disposed.

* ensure Transition component works when nothing is transitioning

* update changelog
2023-02-28 12:38:11 +01:00
Robin Malfait 569cec7514 Fix change event incorrectly getting called on blur (#2296)
* drop `d.enqueue` & `d.workQueue`

This was only used in tests and doesn't seem to be necessary.

* drop `handleChange` from the `ComboboxInput` component

This only emitted a `change` event, which Vue already emits as well.

* drop `onChange` from incoming props

This is an odd one. In Chrome this means that the `@change` is still
being called, but if we keep it, then the `@change` is _also_ called on
blur resulting in odd bugs.

Droping it fixes that issue.

That said, the `@change` is _still_ emitted and therefore the callback
is properly called and the `ComboboxInput` still can interact with the
`@change` event.

* update changelog
2023-02-21 12:19:35 +01:00
Robin Malfait b9af614919 Re-focus Combobox.Input when a Combobox.Option is selected (#2272)
* re-focus `Combobox.Input` when a `Combobox.Option` is selected

Except on mobile devices (ideally devices using a virtual keyboard), so
that the virtual keyboard won't be triggered every single time we
re-focus that input field.

* update changelog
2023-02-10 16:30:53 +01:00
Jordan Pittman 2f99644ed7 Don't break overflow when multiple dialogs are open at the same time (#2215)
* Fix overflow when swapping dialogs that use transition

* Refactor

* refactor

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Inline shim for ESM support

Until the official package adds an ESM version with a wildcard import we can’t use it. This version was copied from Remix Router

* Add dialog shadow root examples

* Fix SSR error

* Add repro for iOS scrolling issue

* Try to fix vercel build

idk what’s wrong here

* Update repro

A transition is required to delay closing enough to demonstrate the bug

* Port global dialog state to Vue

* Add dialog test to Vue

* wip

* wip

* Workaround bug

This shouldn’t happen at all and we need to find the source of the bug but this should “fix” things for the time being

* wip

* Rebuild overflow locking with simpler API

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Update deps

* wip

* simplify

* Port to Vue

* wip

* wip

* Tweak tests

* Update changelog

* Ensure meta callbacks are cleaned up

* cleanup

* wip
2023-02-01 16:08:34 -05:00
Robin Malfait dbcfb23bc3 Fix FocusTrap in Dialog when there is only 1 focusable element (#2172)
* add tests to guarantee `FocusTrap` with a single element works as expected

* it should keep the focus in the Dialog

Even if there is only 1 element. We were skipping the current active
element so the container didn't have any elements anymore and just
continued to the next focusable element in line. This will prevent that
and ensure that we can only skip elements if there are multiple ones.

* update changelog
2023-01-25 13:18:51 +01:00
Robin Malfait d874e561a1 Ensure disabled="false" is not incorrectly passed to the underlying DOM Node (#2138)
* do not add `disabled` prop to `MenuItem`

We use the `aria-disabled` instead so that you can still style it and
that assistive techonology can read the disabled state. If it has the
`disabled` prop itself, then often you can't interact with it at all.

We also default to `disabled = false`, which means that the default
behaviour was a `<element disabled="false">` in the DOM. If you then
have CSS like `[disabled] { opacity: 0.8; }` then this also applies to
the elements with `disabled="false"`.

Fixes: #2134

* ensure Vue playground still works

* ensure Vue overrides the `onXXX` correctly

* update changelog
2023-01-02 16:53:35 +01:00
Jordan Pittman 865bd57357 Fix SSR tab rendering on React 17 (#2102)
* Allow clicks inside dialog panel when target is inside shadow root

* Introduce resettable “server” state

This will aid in testing

* Add SSR and hydration tests for react

* Fix server rendering of Tabs on React 17

* Fix CS

* Skip hydration tests

* Tweak SSR implementation in Vue

* Update changelog
2022-12-16 12:55:51 -05:00
Robin Malfait 962528c216 Improve scroll locking on iOS (#2100)
* improve types for addEventListener inside disposables

* improve scroll locking

Instead of using the "simple" hack with the `position: fixed;` we now
went back to the `touchmove` implementation.

The `position: fixed;` causes some annoying issues. For starters, on iOS
you will now get a strange gap (due to safe areas). Some applications
also saw "blank" screens based on how the page was implemented.

We also saw some issues internally, where clicking changing the scroll
position on the main page from within the Dialog.

Think about something along the lines of:
```html
<a href="#interesting-link-on-the-current-page">Interesting link on the page</a>
```

This doesn't work becauase the page is now fixed, and there is nothing
to scroll...

Instead, we now use the `touchmove` again. The problem with this last
time was that this disabled _all_ touch move events. This is obviously
not good.

Luckily, we already have a concept of "safe containers". This is what we
use for the `outside click` behaviour as well. Basically in a Dialog,
your `Dialog.Panel` is the safe container. But also third party DOM
elements that are rendered inside that Panel (or as a sibling of the
Dialog, but not your main app).

We can re-use this knowledge of "safe containers", and only cancel the
`touchmove` behaviour if this didn't happen in any of the safe
containers.

* update changelog
2022-12-15 16:09:33 +01:00
Robin Malfait d31bb5c08e Fix FocusTrap escape due to strange tabindex values (#2093)
* sort DOM nodes using tabIndex first

It will still keep the same DOM order if tabIndex matches, thanks to
stable sorts!

* refactor `focusIn` API

All the arguments resulted in usage like `focusIn(container,
Focus.First, true, null)`, and to make things worse, we need to add
something else to this list in the future.

Instead, let's keep the `container` and the type of `Focus` as known
params, all the other things can sit in an options object.

* fix FocusTrap escape due to strange tabindex values

This code will now ensure that we can't escape the FocusTrap if you use
`<tab>` and you happen to tab to an element outside of the FocusTrap
because the next item in line happens to be outside of the FocusTrap and
we never hit any of the focus guard elements.

How it works is as follows:

1. The `onBlur` is implemented on the `FocusTrap` itself, this will give
   us some information in the event itself.
   - `e.target` is the element that is being blurred (think of it as `from`)
   - `e.currentTarget` is the element with the event listener (the dialog)
   - `e.relatedTarget` is the element we are going to (think of it as `to`)
2. If the blur happened due to a `<tab>` or `<shift>+<tab>`, then we
   will move focus back inside the FocusTrap, and go from the `e.target`
   to the next or previous value.
3. If the blur happened programmatically (so no tab keys are involved,
   aka no direction is known), then the focus is restored to the
   `e.target` value.

Fixes: #1656

* update changelog
2022-12-14 16:26:38 +01:00
Jordan Pittman 17de0a29c7 Pass default slot instead of raw children to components (#1915)
* Pass default slot instead of raw children to components

This is essentially how the internal implementation of `<component>` works. This works even for element VNodes.

* Update changelog
2022-10-10 14:12:37 -04:00
Robin Malfait 6cddc0fc8a Fix crash when children are undefined (#1885)
* ensure children always exist

* update changelog
2022-09-29 12:00:15 +02:00
Robin Malfait 25a4e7f721 Improve scroll lock on iOS (#1824)
* improve `Dialog` scroll lock on iOS

* add Dialog example to playground that's scrollable

* update changelog
2022-09-05 23:54:54 +02:00
Robin Malfait b301f04c77 Only restore focus to the Menu.Button if necessary when activating a Menu.Option (#1782)
* only restore focus to the Menu Button if necessary

This will check whether the focus got moved to somewhere else or not
once we activate an item via click or pressing `enter`.

Pressing escape will still move focus to the Menu Button.

* update changelog
2022-08-22 17:00:15 +02:00
Robin Malfait 42db5b02d5 Improve event handler merging (#1715)
* improve event handler merging

This will ensure that an actual event is passed before checking the
`event.defaultPrevented`.

For React, we also have to make sure that we are not dealing with a
SyntehticEvent.

Thanks @Mookiepiece!

Co-authored-by: =?UTF-8?q?=E5=BD=BC=E8=A1=93=E5=90=91?= <48076971+Mookiepiece@users.noreply.github.com>

* update changelog

Co-authored-by: =?UTF-8?q?=E5=BD=BC=E8=A1=93=E5=90=91?= <48076971+Mookiepiece@users.noreply.github.com>
2022-07-26 12:40:06 +02:00
Robin Malfait 0e1599b464 Close Menu component when using tab key (#1673)
* menu should not trap focus for tab key

* introduce `focusFrom` focus management utility

This is internal API, and the actual API is not 100% ideal. I started
refactoring this in a separate branch but it got out of hand and touches
a bit more pieces of the codebase that aren't related to this PR at all.

The idea of this function is just so that we can go Next/Previous but
from the given element not from the document.activeElement. This is
important for this feature. We also bolted this ontop of the existing
code which now means that we have this API:

```js
focusIn([], Focus.Previouw, true, DOMNode)
```

Luckily it's internal API only!

* ensure closing via Tab works as expected

Just closing the Menu isn't 100% enough. If we do this, it means that
when the Menu is open, we press shift+tab, then we go to the
Menu.Button because the Menu.Items were the active element.

The other way is also incorrect because it can happen if you have an
`<a>` element as one of the Menu.Item elements then that `<a>` will
receive focus, then the `Menu` will close unmounting the focused `<a>`
and now that element is gone resulting in `document.body` being the
active element.

To fix this, we will make sure that we consider the `Menu` as 1 coherent
component. This means that using `<Tab>` will now go to the next element
after the `<Menu.Button>` once the Menu is closed.

Shift+Tab will go to the element before the `<Menu.Button>` even though
you are currently focused on the `Menu.Items` so depending on the timing
you go to the `Menu.Button` or not.

Considering the Menu as a single component it makes more sense use the
elements before / after the `Menu`

* update changelog

Co-authored-by: Enoch Riese <enoch.riese@gmail.com>
2022-07-14 21:57:58 +02:00
Sean Aye 6119cc202d Fix SSR support in Deno (#1671)
* check typeof document in addition to typeof window

* remove unused import

* Extract SSR check to a central spot

* Fix CS

* Update changelog

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2022-07-14 12:30:33 -04:00
Robin Malfait a294fdbc6c Revert "prepare 1.6.7"
This reverts commit 9807e2ba7e.
2022-07-12 16:04:24 +02:00
Robin Malfait 9807e2ba7e prepare 1.6.7 2022-07-12 15:59:01 +02:00
Robin Malfait fc7def3295 Revert "prepare 1.6.6"
This reverts commit 06df02a158.
2022-07-07 23:06:18 +02:00
Robin Malfait 06df02a158 prepare 1.6.6 2022-07-07 22:57:45 +02:00
Robin Malfait 0260afa2df Properly merge incoming props with own props (#1651)
* sort props in error message

This will make the error message consistent regardless which props (and
in what order) they are applied.

* WIP

* `click()` on a disabled element should no-op

* incomingProps was already merged

* cleanup tests a bit and make it consistent with the React tests

* cleanup unused code

* update changelog
2022-07-07 17:01:45 +02:00
Noel De Martin 90aa4780d4 Fix getting Vue dom elements (#1610)
* Fix getting Vue dom elements

* update changelog

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2022-06-21 23:54:24 +02:00
Robin Malfait bc0b64a8c9 Revert "prepare 1.6.5"
This reverts commit c31136032b.
2022-06-20 18:00:31 +02:00
Robin Malfait c31136032b prepare 1.6.5 2022-06-20 16:22:05 +02:00
Robin Malfait f2fc6d823b fix TypeScript issues (#1587)
We had a bunch of unused imports sitting around
2022-06-14 15:04:22 +02:00
Jordan Pittman 70333a9ccd Support <slot> children when using as="template" (#1548)
* Extract renderTemplate logic

* Flatten Fragments when rendering

* Update changelog
2022-06-03 09:58:11 -04:00
Robin Malfait 1d53ac312f Revert "prepare 1.6.4"
This reverts commit 842d07146e.
2022-05-29 14:57:35 +02:00
Robin Malfait 842d07146e prepare 1.6.4 2022-05-29 14:51:38 +02:00
Robin Malfait 08b419e9b7 Revert "prepare 1.6.3"
This reverts commit 3aaf20b9ac.
2022-05-25 15:25:48 +02:00
Robin Malfait 3aaf20b9ac prepare 1.6.3 2022-05-25 15:13:09 +02:00
Robin Malfait 39c5bd3230 Add @headlessui/tailwindcss plugin (#1487)
* 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
2022-05-24 22:51:02 +02:00
Robin Malfait e1ee36a6ea Simplify Popover Tab logic by using sentinel nodes instead of keydown event interception (#1440)
* improve `Popover` keyboard usage

Use `TabSentinel` instead of intercepting the `Tab` keydown events.

* use Buttons in Popover example

* update changelog
2022-05-13 00:21:10 +02:00
Robin Malfait bf0d1120d3 Improve FocusTrap behaviour (#1432)
* 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
2022-05-11 15:03:54 +02:00
Robin Malfait c475cab451 Allow Enter for form submit in RadioGroup, Switch and Combobox improvements (#1285)
* improve rendering of hidden form fields

* add `attemptSubmit` helper

This will allow us to _try_ and submit a form based on any element you
pass it. It will try and lookup the current form and if it is
submittable it will attempt to submit it.

Instead of submitting the form directly, we try to follow the native
browser support where it looks for the first `input[type=submit]`,
`input[type=image]`, `button` or `button[type=submit]`, then it clicks
it.

This allows you to disable your submit button, or have an `onClick` that
does an `event.preventDefault()` just like the native form in a browser
would do.

* ensure we can submit a form from a closed Combobox

When the Combobox is closed, then the `Enter` keydown event will be
ignored and thus not use `event.preventDefault()`.

With recent changes where we always have an active option, it means that
you will always be able to select an option.

If we have no option at all (some edge case) or when the combobox is
closed, then the `Enter` keydown event will just bubble, allowing you to
submit a form.

Fixes: #1282

This is a continuation of a PR ([#1176](https://github.com/tailwindlabs/headlessui/pull/1176)) provided by Alexander, so wanted to include
them as a co-author because of their initial work.

Co-authored-by: Alexander Lyon <arlyon@me.com>

* ensure we can submit a form from a RadioGroup

* ensure we can submit a form from a Switch

* simplify / refactor form playground example

* update changelog

Co-authored-by: Alexander Lyon <arlyon@me.com>
2022-03-31 21:42:34 +02:00