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),