Commit Graph

303 Commits

Author SHA1 Message Date
Robin Malfait fdd4dd1b01 Remove focus() from Listbox Option (#1218)
* cleanup auto-scrolling

We keep the actual container focused, so we don't require the invidiual
option to be focused as well. We do want to scroll it into view but
that's part of another piece of code.

Also cleaned up some manual `document.querySelector` calls now that we
keep track of a `ref`.

* update changelog
2022-03-09 17:34:56 +01:00
Robin Malfait 7bb89871ba Add <form> compatibility (#1214)
* implement `objetToFormEntries` functionality

If we are working with more complex data structures then we have to
encode those data structures into a syntax that the HTML can understand.

This means that we have to use `<input type="hidden" name="..." value="...">` syntax.

To convert a simple array we can use the following syntax:
```js
// Assuming we have a `name` of `person`
let input = ['Alice', 'Bob', 'Charlie']
```

Results in:
```html
<input type="hidden" name="person[]" value="Alice" />
<input type="hidden" name="person[]" value="Bob" />
<input type="hidden" name="person[]" value="Charlie" />
```

Note: the additional `[]` in the name attribute.

---

A more complex object (even deeply nested) can be encoded like this:
```js
// Assuming we have a `name` of `person`
let input = {
  id: 1,
  name: {
    first: 'Jane',
    last: 'Doe'
  }
}
```

Results in:
```html
<input type="hidden" name="person[id]" value="1" />
<input type="hidden" name="person[name][first]" value="Jane" />
<input type="hidden" name="person[name][last]" value="Doe" />
```

* implement VisuallyHidden component

* implement and export some extra helper utilities

* implement form element for Switch

* implement form element for Combobox

* implement form element for RadioGroup

* implement form element for Listbox

* add combined forms example to the playground

* update changelog

* enable support for iterators

* ensure to compile dom iterables

* remove unused imports
2022-03-09 11:24:45 +01:00
Robin Malfait 2414bbd127 Ignore "outside click" on removed elements (#1193)
* ignore "outside click" on removed elements

Co-authored-by: Colin King <me@colinking.co>

* update changelog

Co-authored-by: Colin King <me@colinking.co>
2022-03-04 16:10:39 +01:00
Robin Malfait 694fbd5513 only activate the Tab on mouseup (#1192) 2022-03-04 13:05:41 +01:00
Robin Malfait 4f52d8336a Fix Dialog cycling (#553)
* add tests to verify that tabbing around when using `initialFocus` works

* add nesting example to `playground-vue`

* fix nested dialog and initialFocus cycling

* make React dialog consistent

- Disable FocusLock on leaf Dialog's

* update changelog
2022-03-03 23:59:41 +01:00
Robin Malfait 27dece107b Fix re-focusing element after close (#1186)
* fix restoreElement logic

The code for React already worked, let's update the Vue code to make it
similar which properly restores focus.

* update changelog
2022-03-03 00:07:30 +01:00
Robin Malfait 8208c07572 Adjust active {item,option} index (#1184)
* adjust active {item,option} index

We had various ordering issues, and now we properly sort all the notes
which is awesome. However, there is this case where we still use the
`activeOptionIndex` / `activeItemIndex` from _before_ the sort happens.

Now we will ensure that this is properly adjusted when performing the
sort of the items.

In addition, we will also properly adjust these values when
`registering` and `unregistering` items, not only when performing
actions.

* update changelog
2022-03-02 22:01:14 +01:00
Robin Malfait 8e7478d1d2 Fix double beforeEnter due to SSR (#1183)
* prevent initial transitioning in SSR environment

Due to SSR and the hydration step, the transition code was already
called even if we were not ready yet. This caused an issue where the
`beforeEnter` callback got fired twice intead of once.

Fixes: #311

* update changelog
2022-03-02 16:30:17 +01:00
Robin Malfait 67995b6961 Reset Combobox Input when the value gets reset (#1181)
* reset input if value is reset

Fixes: #1177

* update changelog
2022-03-02 11:50:51 +01:00
Robin Malfait c4d82890bb Ensure that appear works regardless of multiple rerenders (#1179)
* ensure that `appear` works regardless of multiple rerenders

* remove incorrect `afterLeave` call

* update changelog

* only set the prevShow when using the unmount strategy
2022-03-02 01:15:07 +01:00
Robin Malfait cefb8990a1 Improve outside click support (#1175)
* improve outside click support

We used to use `pointerdown`, but some older devices with iOS 12 didn't
have support for that. Instead we used `mousedown`. But now it turns out
that some devices only properly use `pointerdown` and not the `mousedown` event.

Instead, we will listen to both, but make sure to only handle the event
once.

* update changelog
2022-03-01 17:49:08 +01:00
Jordan Pittman 1b3837baf2 Fix React <Transition> flicker issue (#1118)
* Fix React transition bug

* use a ref instead of a useCallback (#1108)

This allows us to guarantee that the ref is always referencing the
latest callback. This also allows us to re-run fewer effects because we
don't really care about intermediate callback values, just the last one.

* Fix tests

* Update changelog

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2022-03-01 10:20:46 -05:00
Robin Malfait a63ca93aae Guarantee DOM sort order when performing actions (#1168)
* ensure proper sort order

We already fixed a bug in the past where the order of DOM nodes wasn't
stored in the correct order when performing operations (e.g.: using your
keyboard to go to the next option).

We fixed this by ensuring that when we register/unregister an
option/item, that we sorted the list properly. This worked fine, until
we introduced the Combobox components. This is because items in a
Combobox are continuously filtered and because of that moved around.

Moving a DOM node to a new position _doesn't_ require a full
unmount/remount. This means that the sort gets messed up and the order
is wrong when moving around again.

To fix this, we will always perform a sort when performing actions. This
could have performance drawbacks, but the alternative is to re-sort when
the component gets updated. The bad part is that you can update a
component via many ways (like changes on the parent), in those
scenario's you probably don't care to properly re-order the internal
list. Instead we do it while performing an action (`goToOption` / `goToItem`).

To make things a bit more efficient, instead of querying the DOM all the
time using `document.querySelectorAll`, we will keep track of the
underlying DOM node instead. This does increase memory usage a bit but I
think that this is a fine trade-off.

Performance wise this could also be a bottleneck to perform the sorting
if you have a lot of data. But this problem already exists today,
therefore I consider this a complete new problem instead to solve. Maybe
we don't solve it in Headless UI itself, but figure out a way to make it
composable with existing virtualization libraries.

* update changelog
2022-02-28 14:58:17 +01:00
Robin Malfait ca56a15152 Fix hover scroll (#1161)
* disable scroll when hover list item

* change API a bit

* fix scroll into view

For keyboard only for Combobox, Listbox and Menu for both React and Vue.

* update changelog

Co-authored-by: yuta-ike <38308823+yuta-ike@users.noreply.github.com>
2022-02-27 01:11:30 +01:00
Robin Malfait 57e1ec877e Improve SSR for Tab component (#1155)
* improve SSR for Tabs

* update changelog
2022-02-25 20:07:55 +01:00
Robin Malfait 2aaa293811 Ensure links are triggered inside Popover Panel components (#1153)
* ensure links are triggered inside `Popover Panel` components

* update changelog
2022-02-25 12:44:37 +01:00
Robin Malfait f3c70aa9d1 Fix Dialog usage in Tabs (#1149)
* only record the restoreElement once enabled

Currently we are collecting the `restoreElement` even if the focus trap
is not enabled. When we unmount we try to restore it.

The problem is the moment you unmount you want to restore but only if
the focus trap was enabled.

Another issue is that the dialog state will be `closed` before we get to
the `onUmount` hook. So there is probably a cleaner way to fix this, but
this does the trick as well where we only record the restoreElement the
moment the focus trap gets enabled.

* update changelog
2022-02-24 17:27:28 +01:00
Robin Malfait 26670d2768 Forward the ref to all components (#1116)
* forward ref to all components

* fix playground pages

This isn't a perfect fix of course. But the TypeScript changes required
to do it properly are a bit bigger and require more work.

Having this ready is a good step forward.

* update changelog
2022-02-24 16:13:56 +01:00
Robin Malfait 336faabcab Ensure that you can close the Combobox initially (#1148)
* ensure that you can close the combobox initially

The issue is that `onInput` fires on every keystroke, and we also
handled `onChange` which is triggered on blur in Vue.

This means that the moment we blur, we also called the `handleChange`
code to re-open the combobox because we want to open the combobox if
something changes when the user starts typing.

To fix this, we will splitup the logic so that it will only open the
combobox on input but not on change.

* update changelog
2022-02-24 14:43:08 +01:00
Robin Malfait 475568bcff Make sure that the input syncs when the combobox closes (#1137)
* make sure that the input syncs when the combobox closes

* update changelog
2022-02-23 11:29:51 +01:00
Robin Malfait 0c213b514d Improve concurrency of GitHub Actions (#1128)
* improve concurrency of GitHub Actions

This will allow you to cancel older running actions for the current PR /
branch. This saves you some resources, but more importantly hopefully
frees up some spots in the queue a bit faster.

Saw this on the Node.js repo: https://github.com/nodejs/node/pull/42017

* empty commit to trigger cancellation of previous commit
2022-02-21 23:37:36 +01:00
Robin Malfait 5deddef40b improve demo mode 2022-02-21 15:37:52 +01:00
Robin Malfait 12ddee8766 add demo mode (__demoMode) (#1126) 2022-02-21 14:16:18 +01:00
Robin Malfait ead5ff851a 1.5.0 2022-02-17 21:51:15 +01:00
Robin Malfait 5fe6679265 update changelog 2022-02-17 21:50:13 +01:00
Robin Malfait bd8e88dd33 Trigger scrollIntoView effect when position changes (#1113)
* trigger scrollIntoView effect when position changes

This is important otherwise it could happen that the current active item
is still the active item even if we inserted X items before the current
one. This will result in the active item being out of the current
viewport. To fix this, we will also make sure to trigger the effect if
the position of the active item changes.

* update changelog
2022-02-17 14:55:13 +01:00
Jordan Pittman fd0575b0a7 Fix off-by-one frame issue causing flicker (#1111)
* Fix active item flicker

* apply `nextFrame` -> `requestAnimationFrame` fix to other components

* update changelog

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2022-02-17 07:43:00 -05:00
Robin Malfait 53af7fa861 Move hold prop to the Combobox Options (#1109)
* move `hold` prop to the `Combobox Options`

* update changelog
2022-02-16 17:18:33 +01:00
Robin Malfait 0bf325dae1 Ensure ComboboxInput syncs correctly (#1106)
* ensure ComboboxInput syncs correctly

* update changelog
2022-02-15 11:25:43 +01:00
Robin Malfait 639d8d2d60 only call onChange if it exists 2022-02-11 17:39:50 +01:00
Robin Malfait 706f42b9d7 Bubble Escape event even if Combobox.Options is not rendered at all (#1104)
* bubble Escape event even if `Combobox.Options` is not rendered at all

If you use `<Combobox.Options static />` it means that you are in
control of rendering and in that case we also bubble the `Escape`
because you are in control of it.

However, if you do something like this:
```js
{filteredList.length > 0 && (
  <Combobox.Options static>
    ...
  </Combobox.Options>
)}
```
Then whenever the `filteredList` is empty, the Combobox.Options are not
rendered at all which means that we can't look at the `static` prop. To
fix this, we also bubble the `Escape` event if we don't have a
`Combobox.Options` at all so that the above example works as expected.

* update changelog
2022-02-11 15:24:30 +01:00
Robin Malfait 4ed344aa87 Combobox improvements (#1101)
* ensure combobox option gets activated on hover (while static)

* rename combobox test file

* remove leftover `horizontal` prop

* remove unnecessary handleLeave calls

These are implemented on the `Combobox.Option` instead of the
`Combobox.Options`. This allows you to have additional visual padding
between `Combobox.Options` and `Combobox.Option` and if you hover over
that area then the option becomes inactive.

If we implement it on the `Combobox.Options` instead then this isn't
_that_ easy to do. We can do it by checking the target and whether or
not it is inside a headlessui-combobox-option. This would only have a
single listener instead of `N` listeners though. Potential improvements!

* implement `hold` in favor of `latestActiveOption`

* update changelog

* Allow Escape to bubble when options is static

You’ve taken control of the open/close state yourself in which case this should be allowed to be handled by other event handlers

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2022-02-10 14:55:56 +01:00
Robin Malfait dcf2f7508a Ensure typeahead stays on same item if it still matches (#1098)
* ensure typeahead stays on same item if it still matches

Fixes: #1090

* update changelog
2022-02-08 19:54:09 +01:00
Jordan Pittman 554d04b01c Fix Combobox issues (#1099)
* Add combobox to Vue playground

* Update input props

* Wire up input event for changes

This fires changes whenever you type, not just on blur

* Fix playground

* Don't fire input event when pressing escape

The input event is only supposed to fire when the .value of the input changes. Pressing escape doesn't change the value of the input directly so it shouldn't fire.

* Add latest active option render prop

* Add missing active option props to Vue version

* cleanup

* Move test

* Fix error

* Add latest active option to Vue version

* Tweak active option to not re-render

* Remove refocusing on outside mousedown

* Update tests

* Forward refs on combobox to children

* Cleanup code a bit

* Fix lint problems on commit

* Fix typescript issues

* Update changelog
2022-02-08 12:59:39 -05:00
Robin Malfait 6fc28c610f temporarily target es2019 instead of es2020 (#1083)
The Headless UI docs require some bumps in packages because it currently
can't handle es2020 features like `??`. This tempory workaround should
fix this in the mean time.
2022-02-02 18:55:36 +01:00
Robin Malfait 719cac5366 Ignore non-option roles (#1081)
* rename `ComboboxState` to `comboboxState` for consistency

* ensure all elements between `role: listbox` and `role: option` are marked as `role: none`

* add test to demonstrate the `role: none`
2022-02-02 18:04:39 +01:00
Robin Malfait f04d460382 Remove orientation for Combobox (#1080)
* remove orientation in Combobox (React)

* remove orientation in Combobox (Vue)
2022-02-02 15:09:57 +01:00
Robin Malfait b3e7302e45 Merge pull request #1078 from tailwindlabs/improve-build
Improve build files
2022-01-31 17:48:52 +01:00
Robin Malfait 47756e2f5a make vue & react playground pages similar
A bit out of scope for this issue, but bothered me while testing around
🙃
2022-01-31 12:40:19 +01:00
Robin Malfait 6174c47291 convert .js to .cjs 2022-01-31 12:29:27 +01:00
Robin Malfait 39751cda4b update changelog 2022-01-31 12:17:02 +01:00
Robin Malfait c65b87ee68 improve build files
Based on: https://nodejs.org/api/packages.html#conditional-exports
2022-01-31 00:10:06 +01:00
Robin Malfait fbaa1ae9da Merge pull request #1071 from tailwindlabs/develop
Next release
2022-01-28 15:46:25 +01:00
Robin Malfait 947bf7ac8b only build @headlessui/react and @headlessui/vue
This used to also build the individual playground packages but that's
not needed on CI (nor locally). Because Vercel will build them for us.
This will safe us some time on CI, since we can run them in parallel now
and only build the actual libraries.
2022-01-28 13:31:24 +01:00
Robin Malfait bef07b3c50 ensure that a path is passed to lint.sh, otherwise default to the current directory 2022-01-28 13:26:04 +01:00
Robin Malfait 8e0e114153 document all scripts 2022-01-28 12:58:11 +01:00
Robin Malfait 885c3b3b4a move prettier-plugin-tailwindcss to dev dependencies 2022-01-28 12:07:41 +01:00
Jordan Pittman d8424fe311 Update type of omit 2022-01-27 18:40:37 -05:00
Jordan Pittman f48bb4659e Fix circular component types 2022-01-27 18:39:18 -05:00
Jordan Pittman 89fd4b202e Update minimum Vue to 3.2 (#1072)
* Remove vercel json file

* Don't use provide/inject outside of setup

* Upgrade minimum vue version

* Mark vue as an external

* Update lockfile

* WIP move render functions into setup

* WIP

* WIP

* Use setup returning render fns for tests
2022-01-27 13:49:26 -05:00