Correctly handle IME composition in <Combobox.Input> (#2426)
* Don’t try to open combobox when composing characters * wip * Delay IME composition end until after keydown events * Use `d.nextFrame` to handle `compositionend` event * Update changelog
This commit is contained in:
@@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Fix `className` hydration for `<Transition appear>` ([#2390](https://github.com/tailwindlabs/headlessui/pull/2390))
|
||||
- Improve `Combobox` types to improve false positives ([#2411](https://github.com/tailwindlabs/headlessui/pull/2411))
|
||||
- Merge `className` correctly when it’s a function ([#2412](https://github.com/tailwindlabs/headlessui/pull/2412))
|
||||
- Correctly handle IME composition in `<Combobox.Input>` ([#2426](https://github.com/tailwindlabs/headlessui/pull/2426))
|
||||
|
||||
### Added
|
||||
|
||||
|
||||
@@ -821,12 +821,19 @@ function InputFn<
|
||||
)
|
||||
|
||||
let isComposing = useRef(false)
|
||||
let composedChangeEvent = useRef<React.ChangeEvent<HTMLInputElement> | null>(null)
|
||||
let handleCompositionStart = useEvent(() => {
|
||||
isComposing.current = true
|
||||
})
|
||||
let handleCompositionEnd = useEvent(() => {
|
||||
setTimeout(() => {
|
||||
d.nextFrame(() => {
|
||||
isComposing.current = false
|
||||
|
||||
if (composedChangeEvent.current) {
|
||||
actions.openCombobox()
|
||||
onChange?.(composedChangeEvent.current)
|
||||
composedChangeEvent.current = null
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -953,6 +960,10 @@ function InputFn<
|
||||
})
|
||||
|
||||
let handleChange = useEvent((event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (isComposing.current) {
|
||||
composedChangeEvent.current = event
|
||||
return
|
||||
}
|
||||
actions.openCombobox()
|
||||
onChange?.(event)
|
||||
})
|
||||
|
||||
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Add `FocusTrap` event listeners once document has loaded ([#2389](https://github.com/tailwindlabs/headlessui/pull/2389))
|
||||
- Don't scroll-lock `<Dialog>` when wrapping transition isn't showing ([#2422](https://github.com/tailwindlabs/headlessui/pull/2422))
|
||||
- Ensure DOM `ref` is properly handled in the `RadioGroup` component ([#2424](https://github.com/tailwindlabs/headlessui/pull/2424))
|
||||
- Correctly handle IME composition in `<Combobox.Input>` ([#2426](https://github.com/tailwindlabs/headlessui/pull/2426))
|
||||
|
||||
### Added
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import { objectToFormEntries } from '../../utils/form'
|
||||
import { useControllable } from '../../hooks/use-controllable'
|
||||
import { useTrackedPointer } from '../../hooks/use-tracked-pointer'
|
||||
import { isMobile } from '../../utils/platform'
|
||||
import { disposables } from '../../utils/disposables'
|
||||
|
||||
function defaultComparator<T>(a: T, z: T): boolean {
|
||||
return a === z
|
||||
@@ -763,12 +764,19 @@ export let ComboboxInput = defineComponent({
|
||||
})
|
||||
|
||||
let isComposing = ref(false)
|
||||
let composedChangeEvent = ref<(Event & { target: HTMLInputElement }) | null>(null)
|
||||
function handleCompositionstart() {
|
||||
isComposing.value = true
|
||||
}
|
||||
function handleCompositionend() {
|
||||
setTimeout(() => {
|
||||
disposables().nextFrame(() => {
|
||||
isComposing.value = false
|
||||
|
||||
if (composedChangeEvent.value) {
|
||||
api.openCombobox()
|
||||
emit('change', composedChangeEvent.value)
|
||||
composedChangeEvent.value = null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -891,6 +899,10 @@ export let ComboboxInput = defineComponent({
|
||||
}
|
||||
|
||||
function handleInput(event: Event & { target: HTMLInputElement }) {
|
||||
if (isComposing.value) {
|
||||
composedChangeEvent.value = event
|
||||
return
|
||||
}
|
||||
api.openCombobox()
|
||||
emit('change', event)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user