UNPKG

@nex-ui/react

Version:

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

207 lines (203 loc) • 6.83 kB
"use client"; 'use strict'; var jsxRuntime = require('react/jsx-runtime'); var react = require('react'); var hooks = require('@nex-ui/hooks'); var utils = require('@nex-ui/utils'); var RadioGroupContext = require('./RadioGroupContext.cjs'); var useDefaultProps = require('../utils/useDefaultProps.cjs'); var useStyles = require('../utils/useStyles.cjs'); var useSlotClasses = require('../utils/useSlotClasses.cjs'); var useSlot = require('../utils/useSlot.cjs'); var radioGroup = require('../../theme/recipes/radioGroup.cjs'); const slots = [ 'root', 'label', 'wrapper' ]; const useSlotAriaProps = (ownerState)=>{ const id = react.useId(); const { slotProps, label, role = 'radiogroup', 'aria-labelledby': labelledBy } = ownerState; const stringLabel = utils.isString(label); const labelId = slotProps?.label?.id || (stringLabel ? id : undefined); return react.useMemo(()=>({ root: { role, 'aria-labelledby': labelledBy ?? labelId }, label: { id: labelId } }), [ role, labelledBy, labelId ]); }; const RadioGroup = (inProps)=>{ const props = useDefaultProps.useDefaultProps({ name: 'RadioGroup', props: inProps }); const rootRef = react.useRef(null); // Generate a unique name for the radio group to ensure that Tab moves to the correct position. const defaultName = react.useId(); // The DOM API casts value to a string, so need to record the original value. const radioGroupStateRef = react.useRef([]); radioGroupStateRef.current = []; const { color, disabled, defaultValue, onValueChange, children, label, slotProps, role, size, classNames, value: valueProp, name = defaultName, orientation = 'horizontal', ...remainingProps } = props; const [value, setValue] = hooks.useControlledState(valueProp, defaultValue, onValueChange); const ownerState = { ...props, role, orientation, value, name }; const styles = useStyles.useStyles({ name: 'RadioGroup', recipe: radioGroup.radioGroupRecipe, ownerState }); const slotClasses = useSlotClasses.useSlotClasses({ name: 'RadioGroup', slots, classNames }); const slotAriaProps = useSlotAriaProps(ownerState); const handleKeyDown = hooks.useEvent((e)=>{ switch(e.key){ case 'ArrowUp': case 'ArrowLeft': case 'ArrowDown': case 'ArrowRight': e.stopPropagation(); e.preventDefault(); break; } }); const handleKeyUp = hooks.useEvent((e)=>{ let nextDirection = null; switch(e.key){ case 'ArrowUp': case 'ArrowLeft': nextDirection = 'prev'; break; case 'ArrowDown': case 'ArrowRight': nextDirection = 'next'; break; } if (!nextDirection) return; e.preventDefault(); e.stopPropagation(); const rootElement = rootRef.current; // istanbul ignore if if (!rootElement) return; const radioNodes = Array.from(rootElement.querySelectorAll("input[type='radio'], [role='radio']")); let nextIndex = radioNodes.findIndex((node)=>node === e.target); let nextElement = null; while(!nextElement){ if (nextDirection === 'next') { nextIndex = (nextIndex + 1) % radioNodes.length; } else { nextIndex = (nextIndex - 1 + radioNodes.length) % radioNodes.length; } const element = radioNodes[nextIndex]; if (element === e.target) { return; } const currentRadioState = radioGroupStateRef.current[nextIndex]; if (currentRadioState && !currentRadioState.disabled) { nextElement = element; } } const { value } = radioGroupStateRef.current[nextIndex]; if (value !== undefined) { nextElement.focus(); setValue(value); } }); const [RadioGroupRoot, getRadioGroupRootProps] = useSlot.useSlot({ elementType: 'div', classNames: slotClasses.root, externalForwardedProps: remainingProps, style: styles.root, additionalProps: { ref: rootRef, onKeyUp: handleKeyUp, onKeyDown: handleKeyDown }, a11y: slotAriaProps.root, dataAttrs: { orientation } }); const [RadioGroupLabel, getRadioGroupLabelProps] = useSlot.useSlot({ elementType: 'h3', style: styles.label, classNames: slotClasses.label, externalSlotProps: slotProps?.label, a11y: slotAriaProps.label }); const [RadioGroupWrapper, getRadioGroupWrapperProps] = useSlot.useSlot({ elementType: 'div', style: styles.wrapper, classNames: slotClasses.wrapper, externalSlotProps: slotProps?.wrapper }); const ctx = react.useMemo(()=>{ const isChecked = (v)=>{ if (v === undefined) { return false; } return v === value; }; const isTabbable = (v)=>{ if (value === undefined) { const first = radioGroupStateRef.current.find((state)=>state.value !== undefined && !state.disabled); return v === first?.value; } return isChecked(v); }; const setGroupState = (state)=>{ if (radioGroupStateRef.current.some((s)=>s === state)) return; radioGroupStateRef.current.push(state); }; return { size, color, disabled, name, setValue, isChecked, isTabbable, setGroupState }; }, [ setValue, size, color, disabled, name, value ]); return /*#__PURE__*/ jsxRuntime.jsxs(RadioGroupRoot, { ...getRadioGroupRootProps(), children: [ label ? /*#__PURE__*/ jsxRuntime.jsx(RadioGroupLabel, { ...getRadioGroupLabelProps(), children: label }) : null, /*#__PURE__*/ jsxRuntime.jsx(RadioGroupContext.RadioGroupProvider, { value: ctx, children: /*#__PURE__*/ jsxRuntime.jsx(RadioGroupWrapper, { ...getRadioGroupWrapperProps(), children: children }) }) ] }); }; RadioGroup.displayName = 'RadioGroup'; exports.RadioGroup = RadioGroup;