From 729ede8de7f18c7b49fbdb736aac300b5b98f239 Mon Sep 17 00:00:00 2001 From: Maharshi Alpesh Date: Wed, 4 Dec 2024 16:27:10 +0530 Subject: [PATCH] fix(touch): fixing the selection functionality on touch (#4220) * fix(touch): fixing the selection functionality on touch * fix: radio, checkbox & switch interactions --------- Co-authored-by: Junior Garcia --- .changeset/happy-guests-warn.md | 7 ++++ .../components/checkbox/src/use-checkbox.ts | 34 +++---------------- packages/components/radio/src/use-radio.ts | 33 +++--------------- packages/components/switch/src/use-switch.ts | 32 +++-------------- 4 files changed, 22 insertions(+), 84 deletions(-) create mode 100644 .changeset/happy-guests-warn.md diff --git a/.changeset/happy-guests-warn.md b/.changeset/happy-guests-warn.md new file mode 100644 index 0000000000..d856b20a00 --- /dev/null +++ b/.changeset/happy-guests-warn.md @@ -0,0 +1,7 @@ +--- +"@nextui-org/checkbox": patch +"@nextui-org/switch": patch +"@nextui-org/radio": patch +--- + +Fix #4210 radio, checkbox & switch interaction diff --git a/packages/components/checkbox/src/use-checkbox.ts b/packages/components/checkbox/src/use-checkbox.ts index f9f4baec20..395381adb4 100644 --- a/packages/components/checkbox/src/use-checkbox.ts +++ b/packages/components/checkbox/src/use-checkbox.ts @@ -3,12 +3,12 @@ import type {AriaCheckboxProps} from "@react-types/checkbox"; import type {HTMLNextUIProps, PropGetter} from "@nextui-org/system"; import {useProviderContext} from "@nextui-org/system"; -import {ReactNode, Ref, useCallback, useId, useState} from "react"; +import {ReactNode, Ref, useCallback, useId} from "react"; import {useMemo, useRef} from "react"; import {useToggleState} from "@react-stately/toggle"; import {checkbox} from "@nextui-org/theme"; import {useCallbackRef} from "@nextui-org/use-callback-ref"; -import {useHover, usePress} from "@react-aria/interactions"; +import {useHover} from "@react-aria/interactions"; import {useFocusRing} from "@react-aria/focus"; import {mergeProps, chain} from "@react-aria/utils"; import {__DEV__, warn, clsx, dataAttr, safeAriaLabel} from "@nextui-org/shared-utils"; @@ -182,13 +182,7 @@ export function useCheckbox(props: UseCheckboxProps = {}) { const toggleState = useToggleState(ariaCheckboxProps); - const { - inputProps, - isSelected, - isDisabled, - isReadOnly, - isPressed: isPressedKeyboard, - } = isInGroup + const {inputProps, isSelected, isDisabled, isReadOnly, isPressed} = isInGroup ? // eslint-disable-next-line useReactAriaCheckboxGroupItem({...ariaCheckboxProps}, groupContext.groupState, inputRef) : // eslint-disable-next-line @@ -196,24 +190,7 @@ export function useCheckbox(props: UseCheckboxProps = {}) { const isInteractionDisabled = isDisabled || isReadOnly; - // Handle press state for full label. Keyboard press state is returned by useCheckbox - // since it is handled on the element itself. - const [isPressed, setPressed] = useState(false); - const {pressProps} = usePress({ - isDisabled: isInteractionDisabled, - onPressStart(e) { - if (e.pointerType !== "keyboard") { - setPressed(true); - } - }, - onPressEnd(e) { - if (e.pointerType !== "keyboard") { - setPressed(false); - } - }, - }); - - const pressed = isInteractionDisabled ? false : isPressed || isPressedKeyboard; + const pressed = isInteractionDisabled ? false : isPressed; const {hoverProps, isHovered} = useHover({ isDisabled: inputProps.disabled, @@ -277,7 +254,7 @@ export function useCheckbox(props: UseCheckboxProps = {}) { "data-readonly": dataAttr(inputProps.readOnly), "data-focus-visible": dataAttr(isFocusVisible), "data-indeterminate": dataAttr(isIndeterminate), - ...mergeProps(hoverProps, pressProps, otherProps), + ...mergeProps(hoverProps, otherProps), }; }, [ slots, @@ -292,7 +269,6 @@ export function useCheckbox(props: UseCheckboxProps = {}) { inputProps.readOnly, isFocusVisible, hoverProps, - pressProps, otherProps, ]); diff --git a/packages/components/radio/src/use-radio.ts b/packages/components/radio/src/use-radio.ts index 6e9c20593b..b171b1e006 100644 --- a/packages/components/radio/src/use-radio.ts +++ b/packages/components/radio/src/use-radio.ts @@ -1,10 +1,10 @@ import type {AriaRadioProps} from "@react-types/radio"; import type {RadioVariantProps, RadioSlots, SlotsToClasses} from "@nextui-org/theme"; -import {Ref, ReactNode, useCallback, useId, useState} from "react"; +import {Ref, ReactNode, useCallback, useId} from "react"; import {useMemo, useRef} from "react"; import {useFocusRing} from "@react-aria/focus"; -import {useHover, usePress} from "@react-aria/interactions"; +import {useHover} from "@react-aria/interactions"; import {radio} from "@nextui-org/theme"; import {useRadio as useReactAriaRadio} from "@react-aria/radio"; import {HTMLNextUIProps, PropGetter, useProviderContext} from "@nextui-org/system"; @@ -115,12 +115,7 @@ export function useRadio(props: UseRadioProps) { descriptionId, ]); - const { - inputProps, - isDisabled, - isSelected, - isPressed: isPressedKeyboard, - } = useReactAriaRadio( + const {inputProps, isDisabled, isSelected, isPressed} = useReactAriaRadio( { value, children: typeof children === "function" ? true : children, @@ -135,29 +130,11 @@ export function useRadio(props: UseRadioProps) { }); const interactionDisabled = isDisabled || inputProps.readOnly; - - // Handle press state for full label. Keyboard press state is returned by useCheckbox - // since it is handled on the element itself. - const [isPressed, setPressed] = useState(false); - const {pressProps} = usePress({ - isDisabled: interactionDisabled, - onPressStart(e) { - if (e.pointerType !== "keyboard") { - setPressed(true); - } - }, - onPressEnd(e) { - if (e.pointerType !== "keyboard") { - setPressed(false); - } - }, - }); - const {hoverProps, isHovered} = useHover({ isDisabled: interactionDisabled, }); - const pressed = interactionDisabled ? false : isPressed || isPressedKeyboard; + const pressed = interactionDisabled ? false : isPressed; const slots = useMemo( () => @@ -189,7 +166,7 @@ export function useRadio(props: UseRadioProps) { "data-hover-unselected": dataAttr(isHovered && !isSelected), "data-readonly": dataAttr(inputProps.readOnly), "aria-required": dataAttr(isRequired), - ...mergeProps(hoverProps, pressProps, otherProps), + ...mergeProps(hoverProps, otherProps), }; }, [ diff --git a/packages/components/switch/src/use-switch.ts b/packages/components/switch/src/use-switch.ts index 59ad998229..34adb52e54 100644 --- a/packages/components/switch/src/use-switch.ts +++ b/packages/components/switch/src/use-switch.ts @@ -2,11 +2,11 @@ import type {ToggleVariantProps, ToggleSlots, SlotsToClasses} from "@nextui-org/ import type {AriaSwitchProps} from "@react-aria/switch"; import type {HTMLNextUIProps, PropGetter} from "@nextui-org/system"; -import {ReactNode, Ref, useCallback, useId, useRef, useState} from "react"; +import {ReactNode, Ref, useCallback, useId, useRef} from "react"; import {mapPropsVariants, useProviderContext} from "@nextui-org/system"; import {mergeRefs} from "@nextui-org/react-utils"; import {useSafeLayoutEffect} from "@nextui-org/use-safe-layout-effect"; -import {useHover, usePress} from "@react-aria/interactions"; +import {useHover} from "@react-aria/interactions"; import {toggle} from "@nextui-org/theme"; import {chain, mergeProps} from "@react-aria/utils"; import {clsx, dataAttr, objectToDeps} from "@nextui-org/shared-utils"; @@ -155,36 +155,14 @@ export function useSwitch(originalProps: UseSwitchProps = {}) { state.setSelected(isInputRefChecked); }, [inputRef.current]); - const { - inputProps, - isPressed: isPressedKeyboard, - isReadOnly, - } = useReactAriaSwitch(ariaSwitchProps, state, inputRef); + const {inputProps, isPressed, isReadOnly} = useReactAriaSwitch(ariaSwitchProps, state, inputRef); const {focusProps, isFocused, isFocusVisible} = useFocusRing({autoFocus: inputProps.autoFocus}); const {hoverProps, isHovered} = useHover({ isDisabled: inputProps.disabled, }); const isInteractionDisabled = ariaSwitchProps.isDisabled || isReadOnly; - - // Handle press state for full label. Keyboard press state is returned by useSwitch - // since it is handled on the element itself. - const [isPressed, setPressed] = useState(false); - const {pressProps} = usePress({ - isDisabled: isInteractionDisabled, - onPressStart(e) { - if (e.pointerType !== "keyboard") { - setPressed(true); - } - }, - onPressEnd(e) { - if (e.pointerType !== "keyboard") { - setPressed(false); - } - }, - }); - - const pressed = isInteractionDisabled ? false : isPressed || isPressedKeyboard; + const pressed = isInteractionDisabled ? false : isPressed; const isSelected = inputProps.checked; const isDisabled = inputProps.disabled; @@ -202,7 +180,7 @@ export function useSwitch(originalProps: UseSwitchProps = {}) { const getBaseProps: PropGetter = (props) => { return { - ...mergeProps(hoverProps, pressProps, otherProps, props), + ...mergeProps(hoverProps, otherProps, props), ref: domRef, className: slots.base({class: clsx(baseStyles, props?.className)}), "data-disabled": dataAttr(isDisabled),