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.

187 lines (185 loc) 5.93 kB
'use client'; import * as React from 'react'; import PropTypes from 'prop-types'; import { useSelectRoot } from './useSelectRoot.js'; import { SelectRootContext } from './SelectRootContext.js'; import { SelectIndexContext } from './SelectIndexContext.js'; import { useFieldRootContext } from '../../field/root/FieldRootContext.js'; import { visuallyHidden } from '../../utils/visuallyHidden.js'; import { PortalContext } from '../../portal/PortalContext.js'; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; const inertStyle = ` [data-floating-ui-inert] { pointer-events: none !important; } `; /** * Groups all parts of the select. * Doesn’t render its own HTML element. * * Documentation: [Base UI Select](https://base-ui.com/react/components/select) */ const SelectRoot = function SelectRoot(props) { const { value: valueProp, defaultValue = null, onValueChange, open, defaultOpen = false, onOpenChange, alignItemToTrigger = true, name, disabled = false, readOnly = false, required = false, modal = true } = props; const selectRoot = useSelectRoot({ value: valueProp, defaultValue, onValueChange, open, defaultOpen, onOpenChange, alignItemToTrigger, name, disabled, readOnly, required, modal }); const { setDirty, validityData } = useFieldRootContext(); const { rootContext } = selectRoot; const value = rootContext.value; const serializedValue = React.useMemo(() => { if (value == null) { return ''; // avoid uncontrolled -> controlled error } if (typeof value === 'string') { return value; } return JSON.stringify(value); }, [value]); return /*#__PURE__*/_jsx(SelectRootContext.Provider, { value: selectRoot.rootContext, children: /*#__PURE__*/_jsxs(SelectIndexContext.Provider, { value: selectRoot.indexContext, children: [selectRoot.rootContext.open && modal && /*#__PURE__*/ /* eslint-disable-next-line react/no-danger */ _jsx("style", { dangerouslySetInnerHTML: { __html: inertStyle } }), /*#__PURE__*/_jsx(PortalContext.Provider, { value: rootContext.mounted, children: props.children }), /*#__PURE__*/_jsx("input", { ...rootContext.fieldControlValidation.getInputValidationProps({ onFocus() { // Move focus to the trigger element when the hidden input is focused. rootContext.triggerElement?.focus(); }, // Handle browser autofill. onChange(event) { // Workaround for https://github.com/facebook/react/issues/9023 if (event.nativeEvent.defaultPrevented) { return; } const nextValue = event.target.value; const exactValue = rootContext.valuesRef.current.find(v => v === nextValue || typeof value === 'string' && nextValue.toLowerCase() === v.toLowerCase()); if (exactValue != null) { setDirty(exactValue !== validityData.initialValue); rootContext.setValue?.(exactValue, event.nativeEvent); } }, id: rootContext.id, name: rootContext.name, disabled: rootContext.disabled, required: rootContext.required, readOnly: rootContext.readOnly, value: serializedValue, ref: rootContext.fieldControlValidation.inputRef, style: visuallyHidden, tabIndex: -1, 'aria-hidden': true }) })] }) }); }; process.env.NODE_ENV !== "production" ? SelectRoot.propTypes /* remove-proptypes */ = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ // └─────────────────────────────────────────────────────────────────────┘ /** * Determines if the selected item inside the popup should align to the trigger element. * @default true */ alignItemToTrigger: PropTypes.bool, /** * @ignore */ children: PropTypes.node, /** * Whether the select menu is initially open. * * To render a controlled select menu, use the `open` prop instead. * @default false */ defaultOpen: PropTypes.bool, /** * The uncontrolled value of the select when it’s initially rendered. * * To render a controlled select, use the `value` prop instead. * @default null */ defaultValue: PropTypes.any, /** * Whether the component should ignore user interaction. * @default false */ disabled: PropTypes.bool, /** * Whether the select should prevent outside clicks and lock page scroll when open. * @default true */ modal: PropTypes.bool, /** * Identifies the field when a form is submitted. */ name: PropTypes.string, /** * Event handler called when the select menu is opened or closed. */ onOpenChange: PropTypes.func, /** * Callback fired when the value of the select changes. Use when controlled. */ onValueChange: PropTypes.func, /** * Whether the select menu is currently open. */ open: PropTypes.bool, /** * Whether the user should be unable to choose a different option from the select menu. * @default false */ readOnly: PropTypes.bool, /** * Whether the user must choose a value before submitting a form. * @default false */ required: PropTypes.bool, /** * The value of the select. */ value: PropTypes.any } : void 0; export { SelectRoot };