@base-ui-components/react
Version:
Base UI is a library of headless ('unstyled') React components and low-level hooks. You gain complete control over your app's CSS and accessibility features.
112 lines (111 loc) • 3.55 kB
JavaScript
'use client';
import * as React from 'react';
import { useControlled } from '../../utils/useControlled.js';
import { useForkRef } from '../../utils/useForkRef.js';
import { visuallyHidden } from '../../utils/visuallyHidden.js';
import { mergeReactProps } from '../../utils/mergeReactProps.js';
import { useEventCallback } from '../../utils/useEventCallback.js';
import { useFieldRootContext } from '../../field/root/FieldRootContext.js';
import { useBaseUiId } from '../../utils/useBaseUiId.js';
import { useEnhancedEffect } from '../../utils/useEnhancedEffect.js';
import { useFieldControlValidation } from '../../field/control/useFieldControlValidation.js';
import { useField } from '../../field/useField.js';
export function useSwitchRoot(params) {
const {
id: idProp,
checked: checkedProp,
onCheckedChange: onCheckedChangeProp = () => {},
defaultChecked,
name,
readOnly,
required,
disabled = false,
inputRef: externalInputRef
} = params;
const {
labelId,
setControlId,
setTouched,
setDirty,
validityData
} = useFieldRootContext();
const {
getValidationProps,
getInputValidationProps,
inputRef: inputValidationRef,
commitValidation
} = useFieldControlValidation();
const onCheckedChange = useEventCallback(onCheckedChangeProp);
const id = useBaseUiId(idProp);
useEnhancedEffect(() => {
setControlId(id);
return () => {
setControlId(undefined);
};
}, [id, setControlId]);
const inputRef = React.useRef(null);
const handleInputRef = useForkRef(inputRef, externalInputRef, inputValidationRef);
const buttonRef = React.useRef(null);
const [checked, setCheckedState] = useControlled({
controlled: checkedProp,
default: Boolean(defaultChecked),
name: 'Switch',
state: 'checked'
});
useField({
id,
commitValidation,
value: checked,
controlRef: buttonRef
});
const getButtonProps = React.useCallback((otherProps = {}) => mergeReactProps(getValidationProps(otherProps), {
id,
ref: buttonRef,
type: 'button',
role: 'switch',
disabled,
'aria-checked': checked,
'aria-readonly': readOnly,
'aria-labelledby': labelId,
onBlur() {
const element = inputRef.current;
if (!element) {
return;
}
setTouched(true);
commitValidation(element.checked);
},
onClick(event) {
if (event.defaultPrevented || readOnly) {
return;
}
inputRef.current?.click();
}
}), [getValidationProps, id, disabled, checked, readOnly, labelId, setTouched, commitValidation]);
const getInputProps = React.useCallback((otherProps = {}) => mergeReactProps(getInputValidationProps(otherProps), {
checked,
disabled,
name,
required,
style: visuallyHidden,
tabIndex: -1,
type: 'checkbox',
'aria-hidden': true,
ref: handleInputRef,
onChange(event) {
// Workaround for https://github.com/facebook/react/issues/9023
if (event.nativeEvent.defaultPrevented) {
return;
}
const nextChecked = event.target.checked;
setDirty(nextChecked !== validityData.initialValue);
setCheckedState(nextChecked);
onCheckedChange?.(nextChecked, event.nativeEvent);
}
}), [getInputValidationProps, checked, disabled, name, required, handleInputRef, setDirty, validityData.initialValue, setCheckedState, onCheckedChange]);
return React.useMemo(() => ({
checked,
getButtonProps,
getInputProps
}), [checked, getButtonProps, getInputProps]);
}