mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
fix(autocomplete): ignore pointer events when the clear button is hidden (#3000)
* fix(autocomplete): hide clear button with `visibility: hidden` * fix(autocomplete): clear button pointer-events * refactor(autocomplete): improve keyboard reopen issue on mobile * chore: add changeset * refactor(autocomplete): apply chain and add type to e --------- Co-authored-by: WK Wong <wingkwong.code@gmail.com>
This commit is contained in:
parent
19c331be75
commit
81da063d6a
6
.changeset/metal-jars-rhyme.md
Normal file
6
.changeset/metal-jars-rhyme.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
"@nextui-org/autocomplete": patch
|
||||
"@nextui-org/theme": patch
|
||||
---
|
||||
|
||||
Improve clear button pointer events, keyboard reopen issue on mobile
|
||||
@ -7,7 +7,7 @@ import {autocomplete} from "@nextui-org/theme";
|
||||
import {useFilter} from "@react-aria/i18n";
|
||||
import {FilterFn, useComboBoxState} from "@react-stately/combobox";
|
||||
import {ReactRef, useDOMRef} from "@nextui-org/react-utils";
|
||||
import {ReactNode, useCallback, useEffect, useMemo, useRef} from "react";
|
||||
import {ReactNode, useEffect, useMemo, useRef} from "react";
|
||||
import {ComboBoxProps} from "@react-types/combobox";
|
||||
import {PopoverProps} from "@nextui-org/popover";
|
||||
import {ListboxProps} from "@nextui-org/listbox";
|
||||
@ -318,10 +318,7 @@ export function useAutocomplete<T extends object>(originalProps: UseAutocomplete
|
||||
}, [inputRef.current]);
|
||||
|
||||
useEffect(() => {
|
||||
// set input focus
|
||||
if (isOpen) {
|
||||
onFocus(true);
|
||||
|
||||
// apply the same with to the popover as the select
|
||||
if (popoverRef.current && inputWrapperRef.current) {
|
||||
let rect = inputWrapperRef.current.getBoundingClientRect();
|
||||
@ -361,19 +358,6 @@ export function useAutocomplete<T extends object>(originalProps: UseAutocomplete
|
||||
[objectToDeps(variantProps), isClearable, disableAnimation, className],
|
||||
);
|
||||
|
||||
const onClear = useCallback(() => {
|
||||
state.setInputValue("");
|
||||
state.setSelectedKey(null);
|
||||
}, [state]);
|
||||
|
||||
const onFocus = useCallback(
|
||||
(isFocused: boolean) => {
|
||||
inputRef.current?.focus();
|
||||
state.setFocused(isFocused);
|
||||
},
|
||||
[state, inputRef],
|
||||
);
|
||||
|
||||
const getBaseProps: PropGetter = () => ({
|
||||
"data-invalid": dataAttr(isInvalid),
|
||||
"data-open": dataAttr(state.isOpen),
|
||||
@ -394,19 +378,23 @@ export function useAutocomplete<T extends object>(originalProps: UseAutocomplete
|
||||
({
|
||||
...mergeProps(buttonProps, slotsProps.clearButtonProps),
|
||||
// disable original focus and state toggle from react aria
|
||||
onPressStart: () => {},
|
||||
onPressStart: () => {
|
||||
// this is in PressStart for mobile so that touching the clear button doesn't remove focus from
|
||||
// the input and close the keyboard
|
||||
inputRef.current?.focus();
|
||||
},
|
||||
onPress: (e: PressEvent) => {
|
||||
slotsProps.clearButtonProps?.onPress?.(e);
|
||||
|
||||
if (state.selectedItem) {
|
||||
onClear();
|
||||
state.setInputValue("");
|
||||
state.setSelectedKey(null);
|
||||
} else {
|
||||
if (allowsCustomValue) {
|
||||
state.setInputValue("");
|
||||
state.close();
|
||||
}
|
||||
}
|
||||
inputRef?.current?.focus();
|
||||
},
|
||||
"data-visible": !!state.selectedItem || state.inputValue?.length > 0,
|
||||
className: slots.clearButton({
|
||||
@ -488,13 +476,19 @@ export function useAutocomplete<T extends object>(originalProps: UseAutocomplete
|
||||
className: slots.endContentWrapper({
|
||||
class: clsx(classNames?.endContentWrapper, props?.className),
|
||||
}),
|
||||
onClick: (e) => {
|
||||
const inputFocused = inputRef.current === document.activeElement;
|
||||
|
||||
if (!inputFocused && !state.isFocused && e.currentTarget === e.target) {
|
||||
onFocus(true);
|
||||
onPointerDown: chain(props.onPointerDown, (e: React.PointerEvent) => {
|
||||
if (e.button === 0 && e.currentTarget === e.target) {
|
||||
inputRef.current?.focus();
|
||||
}
|
||||
},
|
||||
}),
|
||||
onMouseDown: chain(props.onMouseDown, (e: React.MouseEvent) => {
|
||||
if (e.button === 0 && e.currentTarget === e.target) {
|
||||
// Chrome and Firefox on touch Windows devices require mouse down events
|
||||
// to be canceled in addition to pointer events, or an extra asynchronous
|
||||
// focus event will be fired.
|
||||
e.preventDefault();
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@ -14,12 +14,16 @@ const autocomplete = tv({
|
||||
"translate-x-1",
|
||||
"cursor-text",
|
||||
"opacity-0",
|
||||
"pointer-events-none",
|
||||
"text-default-500",
|
||||
"group-data-[invalid=true]:text-danger",
|
||||
"data-[visible=true]:opacity-100", // on mobile is always visible when there is a value
|
||||
"data-[visible=true]:pointer-events-auto",
|
||||
"data-[visible=true]:cursor-pointer",
|
||||
"sm:data-[visible=true]:opacity-0", // only visible on hover
|
||||
"sm:data-[visible=true]:pointer-events-none",
|
||||
"sm:group-data-[hover=true]:data-[visible=true]:opacity-100",
|
||||
"sm:group-data-[hover=true]:data-[visible=true]:pointer-events-auto",
|
||||
],
|
||||
selectorButton: "text-medium",
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user