UNPKG

@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.

97 lines (96 loc) 2.9 kB
'use client'; import * as React from 'react'; import { mergeReactProps } from '../../utils/mergeReactProps.js'; import { useEnhancedEffect } from '../../utils/useEnhancedEffect.js'; import { useBaseUiId } from '../../utils/useBaseUiId.js'; import { useFieldRootContext } from '../root/FieldRootContext.js'; import { useFieldControlValidation } from './useFieldControlValidation.js'; import { useFormContext } from '../../form/FormContext.js'; import { useField } from '../useField.js'; import { useControlled } from '../../utils/useControlled.js'; import { useEventCallback } from '../../utils/useEventCallback.js'; export function useFieldControl(params) { const { id: idProp, name, value: valueProp, defaultValue, onValueChange, disabled } = params; const { setControlId, labelId, setTouched, setDirty, validityData } = useFieldRootContext(); const { errors, onClearErrors } = useFormContext(); const { getValidationProps, getInputValidationProps, commitValidation, inputRef } = useFieldControlValidation(); const id = useBaseUiId(idProp); useEnhancedEffect(() => { setControlId(id); return () => { setControlId(undefined); }; }, [id, setControlId]); const [value, setValueUnwrapped] = useControlled({ controlled: valueProp, default: defaultValue, name: 'FieldControl', state: 'value' }); const setValue = useEventCallback((nextValue, event) => { setValueUnwrapped(nextValue); onValueChange?.(nextValue, event); }); useField({ id, commitValidation, value, getValue: () => inputRef.current?.value, controlRef: inputRef }); const getControlProps = React.useCallback((externalProps = {}) => mergeReactProps(getValidationProps(getInputValidationProps(externalProps)), { id, disabled, name, ref: inputRef, 'aria-labelledby': labelId, value, onChange(event) { if (value != null) { setValue(event.currentTarget.value, event.nativeEvent); } setDirty(event.currentTarget.value !== validityData.initialValue); if (name && {}.hasOwnProperty.call(errors, name)) { const nextErrors = { ...errors }; delete nextErrors[name]; onClearErrors(nextErrors); } }, onBlur(event) { setTouched(true); commitValidation(event.currentTarget.value); }, onKeyDown(event) { if (event.currentTarget.tagName === 'INPUT' && event.key === 'Enter') { setTouched(true); commitValidation(event.currentTarget.value); } } }), [getValidationProps, getInputValidationProps, id, disabled, name, inputRef, labelId, value, setValue, setDirty, validityData.initialValue, errors, onClearErrors, setTouched, commitValidation]); return React.useMemo(() => ({ getControlProps }), [getControlProps]); }