UNPKG

@nex-ui/react

Version:

🎉 A beautiful, modern, and reliable React component library.

181 lines (178 loc) • 5.97 kB
"use client"; import { jsxs, jsx } from 'react/jsx-runtime'; import { useControlledState, useEvent } from '@nex-ui/hooks'; import { __DEV__ } from '@nex-ui/utils'; import { useRef, useId, useMemo } from 'react'; import { useRadioGroup } from './RadioGroupContext.mjs'; import { useDefaultProps } from '../utils/useDefaultProps.mjs'; import { useNexUI } from '../provider/Context.mjs'; import { useStyles } from '../utils/useStyles.mjs'; import { useSlotClasses } from '../utils/useSlotClasses.mjs'; import { useSlot } from '../utils/useSlot.mjs'; import { InputBase } from '../inputBase/InputBase.mjs'; import { radioRecipe } from '../../theme/recipes/radioGroup.mjs'; const slots = [ 'root', 'input', 'dot', 'label' ]; const useSlotAriaProps = (ownerState)=>{ const { children, slotProps, role, as, type, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy } = ownerState; const id = useId(); return useMemo(()=>{ const hasLabel = !!children; const stringChildren = typeof children === 'string'; const labelSlotProps = slotProps?.label || {}; const labelId = labelSlotProps.id ?? (hasLabel ? id : undefined); return { input: { 'aria-label': ariaLabel ?? (stringChildren ? children : undefined), 'aria-labelledby': ariaLabelledBy ?? labelId, role: !role && as !== 'input' && type === 'radio' ? 'radio' : role }, label: { id: labelId } }; }, [ children, slotProps?.label, id, ariaLabel, ariaLabelledBy, role, as, type ]); }; const Radio = (inProps)=>{ const props = useDefaultProps({ name: 'Radio', props: inProps }); const groupCtx = useRadioGroup(); const inGroup = !!groupCtx; const { primaryThemeColor } = useNexUI(); const { children, value, onCheckedChange, sx, className, classNames, slotProps, checked: checkedProp, tabIndex: tabIndexProp = 0, as = 'input', type = 'radio', disabled = groupCtx?.disabled ?? false, name = groupCtx?.name, size = groupCtx?.size ?? 'md', defaultChecked = false, color = groupCtx?.color ?? primaryThemeColor, ...remainingProps } = props; if (__DEV__ && inGroup) { if ('checked' in props) { console.warn('[Nex UI] Radio: The RadioGroup is being used, `checked` will be ignored. Use the `value` of the RadioGroup instead.'); } if ('defaultChecked' in props) { console.warn('[Nex UI] Radio: The RadioGroup is being used, `defaultChecked` will be ignored. Use the `defaultValue` of the RadioGroup instead.'); } if (!('value' in props)) { console.error('[Nex UI] Radio: The `value` prop is required when using Radio inside a RadioGroup'); } } const [rawChecked, setRawChecked] = useControlledState(checkedProp, defaultChecked, onCheckedChange); const checked = inGroup ? groupCtx.isChecked(value) : rawChecked; // Use ref to avoid repeated adding in strict mode. const radioStateRef = useRef({ value, disabled }); if (inGroup) { radioStateRef.current.value = value; radioStateRef.current.disabled = disabled; groupCtx.setGroupState(radioStateRef.current); } const tabIndex = groupCtx?.isTabbable(value) ?? true ? tabIndexProp : -1; const ownerState = { ...props, tabIndex, as, inGroup, disabled, type, name, color, size, checked, defaultChecked }; const styles = useStyles({ name: 'Radio', recipe: radioRecipe, ownerState }); const slotClasses = useSlotClasses({ name: 'Radio', slots, classNames }); const slotAriaProps = useSlotAriaProps(ownerState); const handleChange = useEvent((newChecked)=>{ if (inGroup && value !== undefined) { groupCtx.setValue(value); } if (!inGroup) { setRawChecked(newChecked); } }); const [RadioRoot, getRadioRootProps] = useSlot({ elementType: 'label', style: styles.root, externalSlotProps: slotProps?.root, classNames: slotClasses.root, additionalProps: { sx, className }, dataAttrs: { color, size, checked, disabled } }); const [RadioInput, getRadioInputProps] = useSlot({ elementType: InputBase, externalForwardedProps: remainingProps, style: styles.input, classNames: slotClasses.input, a11y: slotAriaProps.input, shouldForwardComponent: false, additionalProps: { tabIndex, as, disabled, type, name, checked, value, onCheckedChange: handleChange } }); const [RadioLabel, getRadioLabelProps] = useSlot({ elementType: 'span', style: styles.label, externalSlotProps: slotProps?.label, classNames: slotClasses.label, a11y: slotAriaProps.label }); const [RadioDot, getRadioDotProps] = useSlot({ elementType: 'span', externalSlotProps: slotProps?.dot, style: styles.dot, classNames: slotClasses.dot }); return /*#__PURE__*/ jsxs(RadioRoot, { ...getRadioRootProps(), children: [ /*#__PURE__*/ jsx(RadioInput, { ...getRadioInputProps() }), /*#__PURE__*/ jsx(RadioDot, { ...getRadioDotProps() }), children && /*#__PURE__*/ jsx(RadioLabel, { ...getRadioLabelProps(), children: children }) ] }); }; Radio.displayName = 'Radio'; export { Radio };