Commit Graph

222 Commits

Author SHA1 Message Date
Robin Malfait dafcc2d1c0 Only render the FocusSentinel if required in the Tabs component (#1493)
* only render the `FocusSentinel` if required

Fixes: #1491

* update changelog
2022-05-23 12:13:42 +02:00
Robin Malfait e819c0a7b2 General/random internal cleanup (part 1) (#1484)
* 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
2022-05-23 11:26:22 +02:00
Robin Malfait d200be5f6f Add by prop for Listbox, Combobox and RadioGroup (#1482)
* Add `by` prop for `Listbox`, `Combobox` and `RadioGroup`

* update changelog
2022-05-20 23:01:10 +02:00
Robin Malfait cc6aaa234a Ensure the the <Popover.Panel focus> closes correctly (#1477)
* ensure the the `<Popover.Panel focus>` closes correctly

* update changelog
2022-05-19 20:49:48 +02:00
Robin Malfait 9280d92d24 Allow to override the type on the Combobox.Input (#1476)
* allow to override the `type` on the `Combobox.Input`

This still defaults to `text`.

* update changelog
2022-05-19 16:57:35 +02:00
Robin Malfait 46aab52ba5 1.6.2 - @headlessui/react 2022-05-19 16:33:21 +02:00
Robin Malfait b0ccc78f12 Ensure the ref is forwarded on the Transition.Child component (#1473)
* use `forwardRef` on the `Transition.Child` component

* update changelog
2022-05-19 12:43:11 +02:00
Robin Malfait 9885008357 Make the ref optional in the Popover component (#1465)
* 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
2022-05-18 13:46:45 +02:00
Robin Malfait 7344846fad Improve "Scroll lock" scrollbar width for Dialog component (#1457)
* 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
2022-05-16 12:47:19 +02:00
Robin Malfait 9a39fc349f Ensure the Popover.Panel is clickable without closing the Popover (#1443)
* ensure the `Popover.Panel` is clickable without closing

* update changelog
2022-05-13 16:04:08 +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 c494fa36e9 Ignore Escape when event got prevented in Dialog component (#1424)
* 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
2022-05-09 15:07:57 +02:00
Robin Malfait c4e35f3879 Fix closing of Popover.Panel in React 18 (#1409)
* remove unnecessary `SetPanel` action

* update changelog
2022-05-05 23:16:39 +02:00
Robin Malfait 695bf299ed 1.6.1 - @headlessui/react 2022-05-03 13:19:16 +02:00
Robin Malfait b71fcb3b46 temporary ignore flaky tests 2022-05-02 20:52:02 +02:00
Robin Malfait 1ce86e2184 Fix hydration issue with Tab component (#1393)
* fix hydration issues with Tabs component

* update changelog
2022-05-02 20:30:30 +02:00
Robin Malfait 1e14d5159c 1.6.0 2022-04-25 15:58:55 +02:00
Robin Malfait cb7a969951 General cleanup of README files (#1361)
* 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.
2022-04-24 01:05:17 +02:00
Robin Malfait c8cf26ffcb Prefer incoming open prop over OpenClosed state (#1360)
* prefer incoming `open` prop over OpenClosed state

* update changelog
2022-04-23 23:27:19 +02:00
Robin Malfait 0c34fe802c Add explicit multiple prop (#1355)
* add explicit `multiple` prop to the `Combobox`

This allows you to set the value to a **tuple** in `single-value` mode,
which was not possible before the `multiple` prop was introduced,
because then it resulted in `multi-value` mode instead of `single-value`
mode.

* add explicit `multiple` prop to the `Listbox`

This allows you to set the value to a **tuple** in `single-value` mode,
which was not possible before the `multiple` prop was introduced,
because then it resulted in `multi-value` mode instead of `single-value`
mode.

* update changelog

* update playground to use `multiple` prop
2022-04-22 18:55:55 +02:00
Robin Malfait 591b32861a use older syntax instead of .at()
Fixes: #1344
2022-04-18 22:32:17 +02:00
Robin Malfait b4a4e0b307 Add Dialog.Backdrop and Dialog.Panel components (#1333)
* implement `Dialog.Backdrop` and `Dialog.Panel`

* cleanup TypeScript warnings

* update changelog
2022-04-14 17:15:43 +02:00
Robin Malfait 0162c57d88 add React 18 compatibility (#1326)
* bump dev dependencies to React 18

* setup Jest to include `IS_REACT_ACT_ENVIRONMENT`

* prefer `useId` from React 18 if it exists

In React 16 & 17, where `useId` doesn't exist, we will fallback to our
implementation we have been using up until now.

The `useId` exposed by React 18, ensures stable references even in SSR
environments.

* update expected events

React 18 now uses the proper events:
- `blur` -> `focusout`
- `focus` -> `focusin`

* ensure to wait a bit longer

This is a bit unfortunate, but since React 18 now does an extra
unmount/remount in `StrictMode` to ensure that your code is
ConcurrentMode ready, it takes a bit longer to settle what the DOM sees.

That said, this is a temporary "hack". We are going to experiment with
using tools like Puppeteer/Playwright to run our tests in an actual
browser instead to eliminate all the weird details that we have to keep
in mind.

* prefer `.focus()` over `fireEvent.focus(el)`

* abstract `microTask` polyfill code

* prefer our `focus(el)` function over `el.focus()`

Internally we would still use `el.focus()`, but this allows us to have
more control over that `focus` function.

* add React 18 to the React Playground

* improve hooks for React 18

- Improving the cleanup of useEffect hooks
- useIsoMorphicEffect instead of normal useEffect, so that we can use
  useLayoutEffect to be a bit quicker.

* improve disposables

- This allows us to add event listeners on a node, and get automatic
  cleanup once `dispose` gets called.
- We also return all the `d.add` calls, so that we can cleanup specific
  parts only instead of everything or nothing.

* reimplement the Transition component to be React 18 ready

* wait an additional frame for everything to settle

* update playground examples

* suppressConsoleLogs for RadioGroup components

* update changelog

* keep the `to` classes for a smoother transition

In the next transition we will remove _all_ classes provided and re-add
the once we need.

---

Some extra special thanks:

- Thanks @silvenon for your initial work on the `transition` events in #926
- Thanks @thecrypticace for doing late-night debugging sessions

Co-authored-by: =?UTF-8?q?Matija=20Marohni=C4=87?= <matija.marohnic@gmail.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2022-04-13 22:07:01 +02:00
Robin Malfait ab6310c278 Implement nullable mode on Combobox in single value mode (#1295)
* implement `backspace` behaviour in tests

* add `Delete` Key

* implement `nullable` mode on Combobox in single value mode

If you pass a `nullable` prop to the Combobox, then it's possible to
unset the Combobox value by setting it to `null`.
This is triggered by removing all text from the input which will reset
the value itself as well.

* update changelog
2022-03-31 23:35:04 +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
Robin Malfait 6897d2ccf1 Fix required double ArrowDown requirement (#1281)
* fix double arrow down requirement

If the `activeOptionIndex` is set to `null`, then we default to the very
first non-disabled option. This data is _not_ stored in state because if
you as the user go to a specific option, then start searching then we
will maintain the active option. This means that we have to **update**
the `activeOptionIndex` when options are moving around.

While making the first option the active one, we can't store that in
state directly otherwise the very first option becomes the active one.
If we then inject combobox options _before_ the current one then all of
a sudden your active option would jump around a bit.

We don't want this jumping to happen, we want the very first option to
be the one that's active no matter which option it is.

Since this is not stored in state, our keydown handler was a bit borked.
Internally it thinks we are still at `activeOptionIndex === null`
therefore pressing arrow down would move us to `activeOptionIndex ===
0`. To go to the second option, we can press down again which would move
us to `activeOptionIndex === 1`. The only issue is that visually we were
already at `0`.

This fixes that by making sure that if we have `activeOptionIndex ===
null` that we fallback to the very first non disabled option _before_ we
execute the `goToOption()` code.

 ### Before:

**Open combobox**, `activeOptionIndex === null`

| Combobox                |
| ----------------------- |
| **Option A** _(active)_ |
| Option B                |
| Option C                |

**Arrow Down**, `activeOptionIndex === 0`

| Combobox                |
| ----------------------- |
| **Option A** _(active)_ |
| Option B                |
| Option C                |

**Arrow Down**, `activeOptionIndex === 1`

| Combobox                |
| ----------------------- |
| Option A                |
| **Option B** _(active)_ |
| Option C                |

 ### After:

**Open combobox**, `activeOptionIndex === null`

| Combobox                |
| ----------------------- |
| **Option A** _(active)_ |
| Option B                |
| Option C                |

**Arrow Down**, `activeOptionIndex === 1`

| Combobox                |
| ----------------------- |
| Option A                |
| **Option B** _(active)_ |
| Option C                |

* update changelog
2022-03-29 18:52:44 +02:00
Robin Malfait 419ffdac2d Ensure that there is always an active option in the Combobox (#1279)
* ensure that the first option is always active

This will ensure that the first non-disabled option is the active one if
no other active options exist. This means that any time you search for
something that the first result is the active one and you can just press
<kbd>Enter</kbd> to activate the option.

However, there are a few rules that we have to take into account:
- If you just open the Combobox, and there is a `selected`
  Combobox.Option, then we can't make the first option the active one.
  The first selected Combobox.Option has precedence over this one. This
  is important and rather tricky because Combobox.Option's register
  themselves at some point (later) in time.
- If you already have an active option, then that option should stay
  active. If it changes position, then the activeOptionIndex is adjusted
  to account for that.
- If you "mouse leave" an option, then no option should be active. It
  will be re-enabled the moment you start typing OR if you re-open the
  Combobox. Otherwise, it can happen that you are at the bottom of the
  list, mouse leave, and we scroll all the way back up to make the first
  item the active one which is not good for UX reasons.

* filter list based on query in the playground

* update changelog
2022-03-29 15:30:10 +02:00
smacpherson64 6d8235e059 Mimic browser select on focus when navigating via Tab (#1272)
* mimic browser select on focus

When calling focusIn if the next node is selectable select all the text.

* refactor browser `select` behaviour for React and Vue

* update changelog

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2022-03-28 16:21:11 +02:00
Robin Malfait c1023f7934 Fix incorrect closing while interacting with third party libraries in Dialog component (#1268)
* ensure to keep the Dialog open when clicking on 3rd party elements

* update playground with a Flatpickr example

* update changelog
2022-03-24 20:02:57 +01:00
Robin Malfait 3e19aa5c97 Properly merge incoming props (#1265)
* rename inconsistent `passThroughProps` and `passthroughProps` to more
concise `incomingProps`

This is going to make a bit more sense in the next commits of this
branch, hold on!

* split props into `propsWeControl` and `propsTheyControl`

This will allow us to merge the props with a bit more control. Instead
of overriding every prop from the user' props with our props, we can now
merge event listeners.

* update `render` API to accept `propsWeControl` and `propsTheyControl`

* improve the merge logic

This will essentially do the exact same thing we were doing before:
```js
let props = { ...propsTheyControl, ...propsWeControl }
```

But instead of overriding everything, we will merge the event listener
related props like `onClick`, `onKeyDown`, ...

* fix typo in tests

* simplify naming

- Rename `propsWeControl` to `ourProps`
- Rename `propsTheyControl` to `theirProps`

* update changelog
2022-03-22 17:32:11 +01:00
Robin Malfait 4f8c615245 Fix incorrect active option in the Listbox/Combobox component (#1264)
* update tests to expose bug in React implementation

* fix incorrect `active` state on mouseLeave

The React code had a bug in the Listbox and Combobox components where it
incorrectly made the first selected value the active value.

The first selected option should be the active option when you open the
listbox. However when you already had the component in an `open` state,
hovered over a non-selected item and them left the option by moving it
to the body then the first selected option became the active one again.

This made sense because we used a `useEffect` in each option to make it
the active one if it was also selected. Since every component
re-renders, code got called and the bug arises.

Now, instead we moved the logic to make it the active option to the
reducer logic. We will check it when we register an option and doesn't
have an active option index yet or when we open the Listbox/Combobox.

This should also solve the strange scrolling behaviour where the options
scroll up if you have more options than you display.

* update changelog
2022-03-21 18:32:49 +01:00
Robin Malfait c92feaa3b3 Stop propagation on the Popover Button (#1263)
* stop propagation on Popover Button

This is only done on buttons that are **not** inside the Popover Panel.

* update changelog

* trigger CI
2022-03-21 17:09:04 +01:00
Robin Malfait 2dbc38c17a add tests to prove guarding against infinite loops is important (#1253) 2022-03-17 23:53:53 +01:00
Robin Malfait c9883f6611 Improve Combobox Input value (#1248)
* improve Combobox Input value

* update changelog
2022-03-16 18:40:03 +01:00
Robin Malfait 40fee45afe Add multi value support for Listbox & Combobox (#1243)
* First attempt at a multi-listbox

* implement `multiple` mode on Listbox

* add multiple Listbox example to playground

* implement `multiple` mode on Combobox

* make sure groupContext is not undefined or null

On vercel, getting a strange issue like `TypeError: undefined is not an
object (evaluating 'r.resolveTarget')` which doesn't happen locally or
once published. Would expect it to be `null` since we default to `null`.
Hopefully this fixes things.

* bump all the dependencies

* make sure that `@types/react` use set to the correct version

`@types/react-dom` hardcoded the `@types/react` to version `16.14.21`
instead of using the latest `16.14.24` resulting in type mismatches.

*cries in inconsistency*

* update changelog

* add multiple Combobox example to playground

* refactor Combobox, use actions

* use combobox data

This is a first step in refactoring everything where we use dedicated
actions and data instead of accessing the reducer state directly.

It also allows us to get rid of mutations in render where we updated
some values in render directly which is not ideal.

Co-authored-by: pvanliefland <pierre.vanliefland@gmail.com>
2022-03-16 15:14:47 +01:00
Robin Malfait 273719cb5d Ensure focus trap, Tabs and Dialog play well together (#1231)
* add internal FocusSentinel component

This component will allow you to catch the focus and forward it to a new
element. The catch is that it will retry to do that because sometimes
components won't be available yet.

E.g.: We want to focus the first Tab component if it is rendered inside
the Dialog. However, a Tab will register itself in the next tick,
triggering a re-render and only then will it be `selected`. This is a
bit too late for the FocusTrap component.

The FocusSentinel should fix this by catching the focus, and forwarding
it to the correct component. Once that is done, it will remove itself
from the DOM tree so that you can't ever focus that element anymore.
This should fix potential `<tab>` and `<shift+tab>` behaviour.

* find the selectedIndex asap

* use the FocusSentinel and forward it to the correct Tab

* add example Tab in Dialog example

* suppress console warnings

Because we are firing `setState` calls within the component, React is
yelling at us for not using `act(() => { ... })`. Welp, not going to add
those calls inside the component just for tests...

* update changelog
2022-03-10 19:11:54 +01:00
Robin Malfait c219d87a69 Use ownerDocument instead of document (#1158)
* use `ownerDocument` instead of `document`

This should ensure that in iframes and new windows the correct document
is being used.

* update changelog
2022-03-10 13:37:50 +01:00
Robin Malfait 07c3a61b5c Improve some internal code (#1221)
* remove raw `document.getElementById` calls

When we introduced the `forwardRef` for all components, we also made
sure that internal `ref`s were used to keep track of the actual DOM
node.

This code prefers the `internalXXRef` refs in favor of the
`document.getElementById` calls. This is way more React-ish, and also
fixes a few issues:

- Potential performance improvements (no need to re-query the DOM, since
  we already have a reference to the DOM node). Note: this is a *guess*,
  I didn't measure this.
- It could be that the element is rendered in another `document`, the
  correct would involve something like
  `someDOMNode.ownerDocument.getElementById(...)` but that should not be
  necessary anymore now.

* make Disclosure implementation between React & Vue consistent

* use a similar convention for DOM refs to other components

* update changelog
2022-03-09 17:48:08 +01:00
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 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