Commit Graph

725 Commits

Author SHA1 Message Date
Robin Malfait cb9cda7bd0 update changelog with latest v1.7 release 2024-04-15 17:38:05 +02:00
Robin Malfait b86737b698 Add new CloseButton component and useClose hook (#3096)
* add `useClose` hook and `CloseButton` component

* expose `useClose` hook and `CloseButton` components

* use `CloseProvider` in the `Popover` component

* use `CloseProvider` in the `Dialog` component

* use `CloseProvider` in the `Disclosure` component

* update changelog
2024-04-12 15:54:01 +02:00
Robin Malfait 00f84acb21 Render hidden form input fields for Checkbox, Switch and RadioGroup components (#3095)
* add `overrides` prop to internal `FormFields`

By default, the hidden form fields render an `<input type="hidden" />`.
However, we have some components where we explicitly want to change the
type for example `checkbox` or `radio` types.

I first tried to encode this information in the data itself. That
requires us to use symbols so that we don't accidentally choose a key
that actually exists in the data.

An overrides key solves this for our use cases. The component is also
internal, so nothing is exposed to the user.

* always render hidden form elements

In case of checkboxes and radio elements, we will only render the hidden
inputs if:
1. You pass a `name` prop to make it form-aware
2. When the checkbox / radio is "checked"

This is now changed to always render the hidden input as long as the
`name` prop is passed to make it form-aware.

* update changelog
2024-04-11 18:03:16 +02:00
Robin Malfait 92a69ef687 Ensure import actions use the correct paths (#3093)
* drop unnecessary rootDir / paths in tsconfig

This was causing issues with automatic imports that pointed to
`utils/foo` instead of `../utils/foo` and caused the build to break.

* drop unnecessary tsconfig configuration from `@headlessui/vue`
2024-04-11 17:28:57 +02:00
Robin Malfait ed98bad7ae Use native useId and useSyncExternalStore hooks (#3092)
* use native `useSyncExternalStore` hook from React 18

* use native `useId` hook from React 18

* update changelog
2024-04-11 11:26:57 +02:00
Robin Malfait 8652f806eb replace as unknown as XYZ with as XYZ (#3091) 2024-04-11 11:26:19 +02:00
Robin Malfait b3377eb550 Deprecate the entered prop on the Transition component (#3089)
* mark `entered` as deprecated

- We keep the `enterTo` classes once the `enter` transition finishes
- We keep the `leaveTo` classes once the `leave` transition finishes

* update changelog
2024-04-09 20:08:31 +02:00
Robin Malfait ae8c253c21 Fix typos (#3086)
* fix a bunch of typos

* fix typos in `@headlessui/vue`
2024-04-08 23:31:50 +02:00
Robin Malfait c8037fc96e Fix enter transitions for the Transition component (#3074)
* improve `transition` logic

I spend a lot of time trying to debug this, and I'm not 100% sure that
I'm correct yet. However, what we used to do is apply the "before" set
of classes, wait a frame and then apply the "after" set of classes which
should trigger a transition.

However, for some reason, applying the "before" classes already start a
transition. My current thinking is that our component is doing a lot of
work and therfore prematurely applying the classes and actually
triggering the transition.

For example, if we want to go from `opacity-0` to `opacity-100`, it
looks like setting `opacity-0` is already transitioning (from 100%
because that's the default value). Then, we set `opacity-100`, but our
component was just going from 100 -> 0 and we were only at let's say 98%.
It looks like we cancelled the transition and only went from 98% ->
100%.

I can't fully explain it purely because I don't 100% get what's going
on.

However, this commit fixes it in a way where we first prepare the
transition by explicitly cancelling all in-flight transitions. Once that
is done, we can apply the "before" set of classes, then we can apply the
"after" set of classes.

This seems to work consistently (even though we have failing tests,
might be a JSDOM issue).

This tells me that at least parts of my initial thoughts are correct
where some transition is already happening (for some reason). I'm not
sure what the reason is exactly. Are we doing too much work and blocking
the main thread? That would be my guess...

* simplify check

* updating playgrounds to improve transitions

* update changelog

* track in-flight transitions

* document `disposables()` and `useDisposables()` functions

* ensure we alway return a cleanup function

* move comment

* apply `enterTo` or `leaveTo` classes once we are done transitioning

* cleanup & simplify logic

* update comment

* fix another bug + update tests

* add failing test as playground

* simplify event callbacks

Instead of always ensuring that there is an event, let's use the `?.`
operator and conditionally call the callbacks instead.

* use existing `useOnDisappear` hook

* remove repro

* only unmount if we are not transitioning ourselves

* `show` is already guaranteed to be a boolean

In a previous commit we checked for `undefined` and threw an error.
Which means that this part is unreachable if `show` is undefined.

* cleanup temporary test changes

* Update packages/@headlessui-react/src/components/transition/utils/transition.ts

Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>

* Update packages/@headlessui-react/src/components/transition/utils/transition.ts

Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>

* Update packages/@headlessui-react/src/components/transition/utils/transition.ts

Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>

* Update packages/@headlessui-react/src/components/transition/utils/transition.ts

Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>

* Update packages/@headlessui-react/src/utils/disposables.ts

Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>

* Update packages/@headlessui-react/src/components/transition/transition.tsx

Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>

* Update packages/@headlessui-react/src/components/transition/transition.tsx

Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>

* run prettier

---------

Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>
2024-04-05 16:05:06 +02:00
Robin Malfait c1d3b7ecda Close the Combobox, Dialog, Listbox, Menu and Popover components when the trigger disappears (#3075)
* add `useOnDisappear` hook

This hook allows us to trigger a callback if the element becomes
"hidden". We use the bounding client rect and check the dimensions to
know wether we are "hidden" or not.

* use new `useOnDisappear` hook in components with the `anchor` prop

* update changelog

* document `useOnDisappear`
2024-04-03 15:10:58 +02:00
Robin Malfait 4ed69f640c Keep focus inside of the <ComboboxInput /> component (#3073)
* bail the refocus if focus is already on the correct element

* use `mousedown` instead of `click` event

The `mousedown` event happens before the `focus` event. When we
`e.preventDefault()` in this listener, the `focus` event will not fire.

This also means that the focus is not lost on the actual `input`
component which in turn means that we can maintain the selection /
cursor position inside the `input`.

We still use the `refocusInput()` as a fallback in case something else
goes wrong.

* add comments to describe _why_ we use `mousedown`

* ensure we handle mouse buttons correctly

* ensure we handle `Enter` and `Space` explicitly

Now that we use the `mousedown` event instead of the `click` event, we
have to make sure that we handle the `enter` and `space` keys
explicitly.

This used to be covered by the `click` event, but not for the `mousedown` event.

* ensure we focus the first element when using `ArrowDown` on the `ComboboxButton`

We go to the last one on `ArrownUp`, but we forgot to do this on
`ArrowDown`.

* fix tiny typo

Not related to this PR, but noticed it and fixed it anyway.

* update changelog

* ensure we reset the `isTyping` flag

While we are typing, the flag can remain true. But once we stop typing,
the `nextFrame` handler will kick in and set it to `false` again.

It currently behaves as a debounce-like function such that the
`nextFrame` callbacks are cancelled once a new event is fired.

* ensure unique callbacks in the `_disposables` array

This allows us to keep re-adding dispose functions and only register the
callbacks once.

Ideally we can use a `Set`, but we also want to remove a single callback
if the callback is disposed on its own instead of disposing the whole
group. For this we do require an `idx` which is not available in a
`Set` unless you are looping over all disposable functions.

* Update packages/@headlessui-react/src/components/combobox/combobox.tsx

Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>

* Update packages/@headlessui-react/src/components/combobox/combobox.tsx

Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>

* update comments

* abstract confusing logic to a `useFrameDebounce()` hook

* use correct path import

* add some breathing room

---------

Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>
2024-04-03 15:06:41 +02:00
Robin Malfait 4f89588239 Fix cursor position when re-focusing the ComboboxInput component (#3065)
* add `useRefocusableInput` hook

* use the new `useRefocusableInput` hook

* update changelog

* infer types of the `ref`
2024-03-29 16:15:29 +01:00
Robin Malfait d03fbb19f5 Make the Combobox component nullable by default (#3064)
* remove `nullable` prop

* prevent selecting active option on blur

+ cleanup and adjust comments

* remove nullable from comments

* bump TypeScript to 5.4

This gives us `NoInfer<T>`!

* simplify types of `Combobox`

Now that `nullable` is gone, we can take another look at the type
definition. This in combination with the new `NoInfer` type makes types
drastically simpler and more correct.

* re-add `nullable` to prevent type issues

But let's mark it as deprecated to hint that something changed.

* update changelog

* improve `ByComparator` type

If we are just checking for `T extends null`, then
`{id:1,name:string}|null` will also be true and therefore we would
eventually return `string` instead of `"id" | "name"`.

To solve this, we first check if `NonNullable<T> extends never`, this
would be the case if `T` is `null`.

Otherwise, we know it's not just `null` but it can be something else
with or without `null`. To be sure, we use `keyof NonNullable<null>` to
get rid of the `null` part and to only keep the rest of the object (if
it's an object).

* ensure the `by` prop type handles `multiple` values correctly

This way the `by` prop will still compare single values that are present
inside the array.

This now also solves a pending TypeScript issue that we used to `//
@ts-expect-error` before.

* type uncontrolled `Combobox` components correctly

We have some tests that use uncontrolled components which means that we
can't infer the type from the `value` type.

* simplify `onChange` calls

Now that we don't infer the type when using the generic inside of
`onChange`, it means that we can use `onChange={setValue}` directly
because we don't have to worry about the updater function of `setValue`
anymore.

* correctly type `onChange`, by adding `null`

If you are in single value mode, then the `onChange` can (and will)
receive `null` as a value (when you clear the input field). We never
properly typed it so this fixes that.

In multiple value mode this won't happen, if anything the value will be
`[]` but not `null`.

* remove `nullable` prop from playground

* drop `nullable` mentions in tests
2024-03-29 15:41:12 +01:00
Robin Malfait 6d44a8d049 Expose missing data-disabled and data-focus attributes on the TabsPanel, MenuButton, PopoverButton and DisclosureButton components (#3061)
* expose `data-focus` on the `TabsPanel` component

* expose `data-disabled` on the `MenuButton` component

* expose `data-disabled` on the `PopoverButton` component

* expose `data-disabled` on the `DisclosureButton` component

* cleanup repetition

* update changelog

* add `satisfies` statements to ensure all data is present

* update changelog entry
2024-03-27 17:04:28 +01:00
Robin Malfait bf4dc77f7a Expose the --button-width CSS variable on the PopoverPanel component (#3058) 2024-03-26 20:00:01 +01:00
Robin Malfait 056b311522 Expose --input-width and --button-width CSS variables on the ComboboxOptions component (#3057)
* add both `--input-width` and `--button-width` to `ComboboxOptions`

* use `--input-width` and `--button-width` in combobox countries example

* update changelog
2024-03-26 17:58:19 +01:00
Robin Malfait 000e0c0192 Prevent unnecessary execution of the displayValue callback in the ComboboxInput component (#3048)
* memoize the `currentDisplayValue`

This used to be re-executed every single render. This should typically
not be an issue, but if you use non-deterministic code (E.g.:
`Math.random`, `Date.now`, …) then it could result in incorrect values.

Using `useMemo` allows us to only re-run it if the `data.value` or thte
`displayValue` actually changes.

* add test to verify `currentDisplayValue` is stable

* update changelog
2024-03-21 13:46:17 +01:00
Robin Malfait 834dbf423e Respect selectedIndex for controlled <Tab/> components (#3037)
* ensure controlled `<Tab>` components respect the `selectedIndex`

* update changelog

* use older syntax in tests

* run prettier to fix lint step
2024-03-15 14:37:16 +01:00
Robin Malfait 8c1c42bc5a Prefer incoming data-* attributes, over the ones set by Headless UI (#3035)
* prefer `data-*` props that already exist

If the component already had `data-foo`, and we set `data-foo` then the
`data-foo` that was already there should stay.

* update changelog
2024-03-14 22:17:50 +01:00
Robin Malfait 8a9867cd58 Accept optional strategy for the anchor prop (#3034)
* accept `strategy` for the `anchor` prop

* update changelog
2024-03-14 22:16:56 +01:00
Robin Malfait 626a253dcf copy License from the root (#3030) 2024-03-12 16:25:13 +01:00
Robin Malfait b3fed0348a add link to documentation 2024-03-12 15:33:00 +01:00
saibotk 5a85d8089a CI: Add provenance to publised packages (#3023)
This commit adds provenance for all published packages. See the NPM documentation [0].

Provenance will allow people to verify that the headlessui packages were actually built on GH Actions and with the content of the corresponding commit. This will help with supply chain security.

For this to work, the `id-token` permission was added only where necessary.

[0]: https://docs.npmjs.com/generating-provenance-statements
2024-03-12 15:32:15 +01:00
Tony Narlock 79b03303c5 chore: Remove tsbuildinfo (#3021)
via b83b5b6ffc
2024-03-12 15:20:16 +01:00
Robin Malfait 043edb8319 Replace deprecated Jest methods with the new methods (#3005)
* replace deprecated `lastCalledWith` with `toHaveBeenLastCalledWith`

* replace deprecated `toThrowError` with `toThrow`
2024-02-21 14:28:19 +01:00
Robin Malfait a50be9255a Forward disabled state to hidden inputs in form-like components (#3004)
* make hidden inputs disabled if the wrapping component is disabled

* add tests to verify disabled hidden form elements

* update changelog
2024-02-21 14:16:31 +01:00
Jordan Pittman 08baf094d2 Update changelog 2024-02-07 17:12:43 -05:00
Jonathan Reinink 4156c45f78 Fix Combobox activeOption render prop (#2973)
* Fix Combobox `activeOption` render prop

* Update changelog
2024-02-07 17:09:42 -05:00
Jordan Pittman 25ba013ba1 Tweak wording in changelog 2024-02-06 16:16:44 -05:00
Jordan Pittman 08d380aed1 Update changelog 2024-02-06 16:16:01 -05:00
Jordan Pittman c71c6763f1 Make Listbox submit form on Enter when not open (#2972) 2024-02-06 16:06:35 -05:00
Jordan Pittman 680db08b00 Remove outdated esbuild deps 2024-02-06 15:04:02 -05:00
Robin Malfait 0e0277a684 Allow setting custom tabIndex on the <Switch /> component (#2966)
* allow setting a custom `tabIndex` on the `<Switch />` component

* update changelog
2024-02-03 17:31:02 +01:00
Robin Malfait da94b80860 Attempt form submission when pressing Enter on Checkbox component (#2962)
* attempt to submit form when pressing `Enter` on the `Checkbox` component

* add test to verify form submissions when pressing enter works

* update changelog
2024-02-02 15:51:58 +01:00
Robin Malfait edbcb81ead Add hidden attribute to internal <Hidden /> component when the Features.Hidden feature is used (#2955)
* add `hidden` attribute for `<Hidden features={Features.Hidden}>`

* update changelog
2024-01-31 13:29:50 +01:00
Robin Malfait ce17c6d15f Ensure children prop of Field component can be a render prop (#2941)
* ensure `children` prop can be a render prop

* update changelog
2024-01-23 14:01:05 +01:00
Robin Malfait f2bc6fdd40 Use isFocused instead of isFocusVisible for Input and Textarea components (#2940)
* use `isFocused` in `Input` and `Textarea`

We do this because we always want to show the focus ring regardless of
whether you used the mouse or the keyboard.

* update changelog
2024-01-23 12:13:55 +01:00
Jordan Pittman 3b2a102e8d Don’t override explicit disabled prop for components inside <MenuItem> (#2929)
* Don’t override explicit disabled prop inside `<MenuItem>`

* Update changelog
2024-01-16 15:34:41 -05:00
Robin Malfait aff438eb06 Prevent default behaviour when clicking outside of a Dialog.Panel (#2919)
* use `event.preventDefault()` in the `useOutsideClick` on Dialog's

When using a `Dialog`, we should prevent the default behaviour of the
event that triggered the "close" in the `useOutsideClick` call.

We recently made improvements to improve outside click behaviour on
touch devices (https://github.com/tailwindlabs/headlessui/pull/2572) but
due to the `touchend` event, the touch is still forwarded and therefore
a potential button _behind_ the "backdrop" will also be clicked. This is
not what we want.

Added the `event.preventDefault()` for the Dialog specifically because
there are other places where we use `useOutsideClick` and where we _do_
want the behaviour where the click just continues. A concrete example of
this is 2 `Menu`'s next to eachother where you open the first one, and
then click on the second one. This should close first one (outside
click) and open the second one (by not preventing the event)

* update changelog
2024-01-09 20:06:55 +01:00
Robin Malfait 2b7a57e337 Expose disabled state on <Tab /> component (#2918)
* expose `disabled` on `<Tab/>` component

This will expose it such that you can use it with `ui-disabled`. In the
Alpha version of React, you can also use `data-[disabled]` because it
will be exposed as `data-disabled` over there as well.

Fixes: #2864

* update changelog
2024-01-09 15:45:30 +01:00
Robin Malfait b83b5b6ffc sync CHANGELOG 2024-01-08 16:14:28 +01:00
Robin Malfait f6a99cad26 update changelog 2024-01-06 02:05:13 +01:00
Robin Malfait 799e98a56e improve iOS scroll locking
The scroll locking on iOS was flickering in some scenario's due to the
`window.scrollTo(0, 0)` related code. Instead of that, we now cancel
touch moves instead but still allow it in scrollable containers inside
the Dialog itself.

This was already applied in the React version, but this adds the same
improvement to the Vue version as well.
2024-01-06 01:58:22 +01:00
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 a3276570d2 sync package-lock.json 2024-01-05 12:54:54 +01:00
Robin Malfait 33286d0c80 2.0.0-alpha.4 — @headlessui/react 2024-01-03 15:06:09 +01:00
Robin Malfait a73007388f Ensure playgrounds work + switch to npm workspaces (#2907)
* bump Next in playground

* convert legacy Link after Next.js bump

* update yarn.lock

* switch to npm workspaces

* move `packages/playground-*` to `playgrounds/*`

* use `npm` instead of `yarn`

* sync package-lock.json

* use node 20 for insiders releases
2024-01-03 14:26:12 +01:00
Robin Malfait 3b961a690f Ensure Input, Select and Textarea expose Ref related types (#2902)
* ensure `Input`, `Select` and `Textarea` expose `Ref` related types

* update changelog
2023-12-27 20:34:32 +01:00
Robin Malfait 8c882e047a Make playgrounds build again (#2894)
* sync yarn.lock

* drop resolutions from root package.json

This is done in the `package.json` from the `playground-react` package

* update next.config.js

Use `buildActivity` instead of `autoPrerender`

* ignore type checking in playground files

This is not ideal, but it is the first step in making the build pass
again.
2023-12-21 16:02:35 +01:00
Robin Malfait 847976a310 fix typo 2023-12-21 02:55:46 +01:00