@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.
158 lines (157 loc) • 5.47 kB
JavaScript
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
import { useComponentRenderer } from '../../utils/useComponentRenderer.js';
import { FieldRootContext } from './FieldRootContext.js';
import { DEFAULT_VALIDITY_STATE, STYLE_HOOK_MAPPING } from '../utils/constants.js';
import { useFieldsetRootContext } from '../../fieldset/root/FieldsetRootContext.js';
import { useEventCallback } from '../../utils/useEventCallback.js';
import { useFormContext } from '../../form/FormContext.js';
import { jsx as _jsx } from "react/jsx-runtime";
/**
* Groups all parts of the field.
* Renders a `<div>` element.
*
* Documentation: [Base UI Field](https://base-ui.com/react/components/field)
*/
const FieldRoot = /*#__PURE__*/React.forwardRef(function FieldRoot(props, forwardedRef) {
const {
render,
className,
validate: validateProp,
validationDebounceTime = 0,
validationMode = 'onBlur',
name,
disabled: disabledProp = false,
invalid: invalidProp,
...otherProps
} = props;
const {
disabled: disabledFieldset
} = useFieldsetRootContext();
const {
errors
} = useFormContext();
const validate = useEventCallback(validateProp || (() => null));
const disabled = disabledFieldset || disabledProp;
const [controlId, setControlId] = React.useState(undefined);
const [labelId, setLabelId] = React.useState(undefined);
const [messageIds, setMessageIds] = React.useState([]);
const [touched, setTouched] = React.useState(false);
const [dirty, setDirtyUnwrapped] = React.useState(false);
const markedDirtyRef = React.useRef(false);
const setDirty = React.useCallback(value => {
if (value) {
markedDirtyRef.current = true;
}
setDirtyUnwrapped(value);
}, []);
const invalid = Boolean(invalidProp || name && {}.hasOwnProperty.call(errors, name));
const [validityData, setValidityData] = React.useState({
state: DEFAULT_VALIDITY_STATE,
error: '',
errors: [],
value: null,
initialValue: null
});
const valid = !invalid && validityData.state.valid;
const state = React.useMemo(() => ({
disabled,
touched,
dirty,
valid
}), [disabled, touched, dirty, valid]);
const contextValue = React.useMemo(() => ({
invalid,
controlId,
setControlId,
labelId,
setLabelId,
messageIds,
setMessageIds,
name,
validityData,
setValidityData,
disabled,
touched,
setTouched,
dirty,
setDirty,
validate,
validationMode,
validationDebounceTime,
state,
markedDirtyRef
}), [invalid, controlId, labelId, messageIds, name, validityData, disabled, touched, dirty, setDirty, validate, validationMode, validationDebounceTime, state]);
const {
renderElement
} = useComponentRenderer({
render: render ?? 'div',
ref: forwardedRef,
className,
state,
extraProps: otherProps,
customStyleHookMapping: STYLE_HOOK_MAPPING
});
return /*#__PURE__*/_jsx(FieldRootContext.Provider, {
value: contextValue,
children: renderElement()
});
});
process.env.NODE_ENV !== "production" ? FieldRoot.propTypes /* remove-proptypes */ = {
// ┌────────────────────────────── Warning ──────────────────────────────┐
// │ These PropTypes are generated from the TypeScript type definitions. │
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
// └─────────────────────────────────────────────────────────────────────┘
/**
* @ignore
*/
children: PropTypes.node,
/**
* CSS class applied to the element, or a function that
* returns a class based on the component’s state.
*/
className: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
/**
* Whether the component should ignore user interaction.
* Takes precedence over the `disabled` prop on the `<Field.Control>` component.
* @default false
*/
disabled: PropTypes.bool,
/**
* Whether the field is forcefully marked as invalid.
*/
invalid: PropTypes.bool,
/**
* Identifies the field when a form is submitted.
* Takes precedence over the `name` prop on the `<Field.Control>` component.
*/
name: PropTypes.string,
/**
* Allows you to replace the component’s HTML element
* with a different tag, or compose it with another component.
*
* Accepts a `ReactElement` or a function that returns the element to render.
*/
render: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
/**
* A function for custom validation. Return a string or an array of strings with
* the error message(s) if the value is invalid, or `null` if the value is valid.
*/
validate: PropTypes.func,
/**
* How long to wait between `validate` callbacks if
* `validationMode="onChange"` is used. Specified in milliseconds.
* @default 0
*/
validationDebounceTime: PropTypes.number,
/**
* Determines when the field should be validated.
*
* - **onBlur** triggers validation when the control loses focus
* - **onChange** triggers validation on every change to the control value
* @default 'onBlur'
*/
validationMode: PropTypes.oneOf(['onBlur', 'onChange'])
} : void 0;
export { FieldRoot };