(props: AriaRadioGroupProps, state: RadioGroupState)
| 53 | * @param state - State for the radio group, as returned by `useRadioGroupState`. |
| 54 | */ |
| 55 | export function useRadioGroup(props: AriaRadioGroupProps, state: RadioGroupState): RadioGroupAria { |
| 56 | let { |
| 57 | name, |
| 58 | form, |
| 59 | isReadOnly, |
| 60 | isRequired, |
| 61 | isDisabled, |
| 62 | orientation = 'vertical', |
| 63 | validationBehavior = 'aria' |
| 64 | } = props; |
| 65 | let {direction} = useLocale(); |
| 66 | |
| 67 | let {isInvalid, validationErrors, validationDetails} = state.displayValidation; |
| 68 | let {labelProps, fieldProps, descriptionProps, errorMessageProps} = useField({ |
| 69 | ...props, |
| 70 | // Radio group is not an HTML input element so it |
| 71 | // shouldn't be labeled by a <label> element. |
| 72 | labelElementType: 'span', |
| 73 | isInvalid: state.isInvalid, |
| 74 | errorMessage: props.errorMessage || validationErrors |
| 75 | }); |
| 76 | |
| 77 | let domProps = filterDOMProps(props, {labelable: true}); |
| 78 | |
| 79 | // When the radio group loses focus, reset the focusable radio to null if |
| 80 | // there is no selection. This allows tabbing into the group from either |
| 81 | // direction to go to the first or last radio. |
| 82 | let {focusWithinProps} = useFocusWithin({ |
| 83 | onBlurWithin(e) { |
| 84 | props.onBlur?.(e); |
| 85 | if (!state.selectedValue) { |
| 86 | state.setLastFocusedValue(null); |
| 87 | } |
| 88 | }, |
| 89 | onFocusWithin: props.onFocus, |
| 90 | onFocusWithinChange: props.onFocusChange |
| 91 | }); |
| 92 | |
| 93 | function getNextElement(nextDir: 'next' | 'prev', e) { |
| 94 | let walker = getFocusableTreeWalker(e.currentTarget, { |
| 95 | from: getEventTarget(e) as Element, |
| 96 | accept: node => node instanceof getOwnerWindow(node).HTMLInputElement && node.type === 'radio' |
| 97 | }); |
| 98 | let nextElem; |
| 99 | if (nextDir === 'next') { |
| 100 | nextElem = walker.nextNode(); |
| 101 | if (!nextElem) { |
| 102 | walker.currentNode = e.currentTarget; |
| 103 | nextElem = walker.firstChild(); |
| 104 | } |
| 105 | } else { |
| 106 | nextElem = walker.previousNode(); |
| 107 | if (!nextElem) { |
| 108 | walker.currentNode = e.currentTarget; |
| 109 | nextElem = walker.lastChild(); |
| 110 | } |
| 111 | } |
| 112 |
no test coverage detected