Skip to content

Commit b9af614

Browse files
authored
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
1 parent 7ecf832 commit b9af614

File tree

6 files changed

+58
-0
lines changed

6 files changed

+58
-0
lines changed

packages/@headlessui-react/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- Ensure we handle `null` dataRef values correctly ([#2258](https://github.com/tailwindlabs/headlessui/pull/2258))
1313
- Move `aria-multiselectable` to `[role=listbox]` in the `Combobox` component ([#2271](https://github.com/tailwindlabs/headlessui/pull/2271))
14+
- Re-focus `Combobox.Input` when a `Combobox.Option` is selected ([#2272](https://github.com/tailwindlabs/headlessui/pull/2272))
1415

1516
## [1.7.10] - 2023-02-06
1617

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import { Keys } from '../keyboard'
4444
import { useControllable } from '../../hooks/use-controllable'
4545
import { useWatch } from '../../hooks/use-watch'
4646
import { useTrackedPointer } from '../../hooks/use-tracked-pointer'
47+
import { isMobile } from '../../utils/platform'
4748

4849
enum ComboboxState {
4950
Open,
@@ -1277,6 +1278,22 @@ let Option = forwardRefWithAs(function Option<
12771278
if (data.mode === ValueMode.Single) {
12781279
actions.closeCombobox()
12791280
}
1281+
1282+
// We want to make sure that we don't accidentally trigger the virtual keyboard.
1283+
//
1284+
// This would happen if the input is focused, the options are open, you select an option (which
1285+
// would blur the input, and focus the option (button), then we re-focus the input).
1286+
//
1287+
// This would be annoying on mobile (or on devices with a virtual keyboard). Right now we are
1288+
// assuming that the virtual keyboard would open on mobile devices (iOS / Android). This
1289+
// assumption is not perfect, but will work in the majority of the cases.
1290+
//
1291+
// Ideally we can have a better check where we can explicitly check for the virtual keyboard.
1292+
// But right now this is still an experimental feature:
1293+
// https://developer.mozilla.org/en-US/docs/Web/API/Navigator/virtualKeyboard
1294+
if (!isMobile()) {
1295+
requestAnimationFrame(() => data.inputRef.current?.focus())
1296+
}
12801297
})
12811298

12821299
let handleFocus = useEvent(() => {

packages/@headlessui-react/src/utils/platform.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// This file contains functions to detect the platform the app is running on. They aren't perfect,
2+
// and we are making assumptions here. But it's the best we can do for now.
3+
14
export function isIOS() {
25
// TODO: This is not a great way to detect iOS, but it's the best I can do for now.
36
// - `window.platform` is deprecated
@@ -12,3 +15,11 @@ export function isIOS() {
1215
(/Mac/gi.test(window.navigator.platform) && window.navigator.maxTouchPoints > 0)
1316
)
1417
}
18+
19+
export function isAndroid() {
20+
return /Android/gi.test(window.navigator.userAgent)
21+
}
22+
23+
export function isMobile() {
24+
return isIOS() || isAndroid()
25+
}

packages/@headlessui-vue/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- Don’t fire `afterLeave` event more than once for a given transition ([#2267](https://github.com/tailwindlabs/headlessui/pull/2267))
1313
- Move `aria-multiselectable` to `[role=listbox]` in the `Combobox` component ([#2271](https://github.com/tailwindlabs/headlessui/pull/2271))
14+
- Re-focus `Combobox.Input` when a `Combobox.Option` is selected ([#2272](https://github.com/tailwindlabs/headlessui/pull/2272))
1415

1516
## [1.7.9] - 2023-02-03
1617

packages/@headlessui-vue/src/components/combobox/combobox.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { Hidden, Features as HiddenFeatures } from '../../internal/hidden'
3636
import { objectToFormEntries } from '../../utils/form'
3737
import { useControllable } from '../../hooks/use-controllable'
3838
import { useTrackedPointer } from '../../hooks/use-tracked-pointer'
39+
import { isMobile } from '../../utils/platform'
3940

4041
function defaultComparator<T>(a: T, z: T): boolean {
4142
return a === z
@@ -1063,6 +1064,22 @@ export let ComboboxOption = defineComponent({
10631064
if (api.mode.value === ValueMode.Single) {
10641065
api.closeCombobox()
10651066
}
1067+
1068+
// We want to make sure that we don't accidentally trigger the virtual keyboard.
1069+
//
1070+
// This would happen if the input is focused, the options are open, you select an option
1071+
// (which would blur the input, and focus the option (button), then we re-focus the input).
1072+
//
1073+
// This would be annoying on mobile (or on devices with a virtual keyboard). Right now we are
1074+
// assuming that the virtual keyboard would open on mobile devices (iOS / Android). This
1075+
// assumption is not perfect, but will work in the majority of the cases.
1076+
//
1077+
// Ideally we can have a better check where we can explicitly check for the virtual keyboard.
1078+
// But right now this is still an experimental feature:
1079+
// https://developer.mozilla.org/en-US/docs/Web/API/Navigator/virtualKeyboard
1080+
if (!isMobile()) {
1081+
requestAnimationFrame(() => dom(api.inputRef)?.focus())
1082+
}
10661083
}
10671084

10681085
function handleFocus() {

packages/@headlessui-vue/src/utils/platform.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// This file contains functions to detect the platform the app is running on. They aren't perfect,
2+
// and we are making assumptions here. But it's the best we can do for now.
3+
14
export function isIOS() {
25
// TODO: This is not a great way to detect iOS, but it's the best I can do for now.
36
// - `window.platform` is deprecated
@@ -12,3 +15,11 @@ export function isIOS() {
1215
(/Mac/gi.test(window.navigator.platform) && window.navigator.maxTouchPoints > 0)
1316
)
1417
}
18+
19+
export function isAndroid() {
20+
return /Android/gi.test(window.navigator.userAgent)
21+
}
22+
23+
export function isMobile() {
24+
return isIOS() || isAndroid()
25+
}

0 commit comments

Comments
 (0)