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.

277 lines (275 loc) 8.09 kB
'use client'; import * as React from 'react'; import PropTypes from 'prop-types'; import { useSelectRootContext } from '../root/SelectRootContext.js'; import { useSelectIndexContext } from '../root/SelectIndexContext.js'; import { useCompositeListItem } from '../../composite/list/useCompositeListItem.js'; import { useForkRef } from '../../utils/useForkRef.js'; import { useComponentRenderer } from '../../utils/useComponentRenderer.js'; import { useSelectItem } from './useSelectItem.js'; import { useEnhancedEffect } from '../../utils/useEnhancedEffect.js'; import { useLatestRef } from '../../utils/useLatestRef.js'; import { SelectItemContext } from './SelectItemContext.js'; import { jsx as _jsx } from "react/jsx-runtime"; const InnerSelectItem = /*#__PURE__*/React.forwardRef(function InnerSelectItem(props, forwardedRef) { const { className, disabled = false, highlighted, selected, getRootItemProps, render, setOpen, typingRef, selectionRef, open, value, setValue, selectedIndexRef, indexRef, setActiveIndex, popupRef, ...otherProps } = props; const state = React.useMemo(() => ({ disabled, open, highlighted, selected }), [disabled, open, highlighted, selected]); const { getItemProps, rootRef } = useSelectItem({ open, setOpen, disabled, highlighted, selected, ref: forwardedRef, typingRef, handleSelect: () => setValue(value), selectionRef, selectedIndexRef, indexRef, setActiveIndex, popupRef }); const mergedRef = useForkRef(rootRef, forwardedRef); const { renderElement } = useComponentRenderer({ propGetter(externalProps = {}) { const rootProps = getRootItemProps({ ...externalProps, active: highlighted, selected }); // With our custom `focusItemOnHover` implementation, this interferes with the logic and can // cause the index state to be stuck when leaving the select popup. delete rootProps.onFocus; return getItemProps(rootProps); }, render: render ?? 'div', ref: mergedRef, className, state, extraProps: otherProps }); const contextValue = React.useMemo(() => ({ selected, indexRef }), [selected, indexRef]); return /*#__PURE__*/_jsx(SelectItemContext.Provider, { value: contextValue, children: renderElement() }); }); process.env.NODE_ENV !== "production" ? InnerSelectItem.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. * @default false */ disabled: PropTypes.bool, /** * @ignore */ getRootItemProps: PropTypes.func.isRequired, /** * @ignore */ highlighted: PropTypes.bool.isRequired, /** * @ignore */ indexRef: PropTypes.shape({ current: PropTypes.number.isRequired }).isRequired, /** * Overrides the text label to use on the trigger when this item is selected * and when the item is matched during keyboard text navigation. */ label: PropTypes.string, /** * @ignore */ open: PropTypes.bool.isRequired, /** * @ignore */ popupRef: PropTypes.shape({ current: PropTypes.object }).isRequired, /** * 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]), /** * @ignore */ selected: PropTypes.bool.isRequired, /** * @ignore */ selectedIndexRef: PropTypes.shape({ current: PropTypes.number }).isRequired, /** * @ignore */ selectionRef: PropTypes.shape({ current: PropTypes.shape({ allowSelect: PropTypes.bool.isRequired, allowSelectedMouseUp: PropTypes.bool.isRequired, allowUnselectedMouseUp: PropTypes.bool.isRequired }).isRequired }).isRequired, /** * @ignore */ setActiveIndex: PropTypes.func.isRequired, /** * @ignore */ setOpen: PropTypes.func.isRequired, /** * @ignore */ setValue: PropTypes.func.isRequired, /** * @ignore */ typingRef: PropTypes.shape({ current: PropTypes.bool.isRequired }).isRequired, /** * @ignore */ value: PropTypes.any.isRequired } : void 0; const MemoizedInnerSelectItem = /*#__PURE__*/React.memo(InnerSelectItem); /** * An individual option in the select menu. * Renders a `<div>` element. * * Documentation: [Base UI Select](https://base-ui.com/react/components/select) */ const SelectItem = /*#__PURE__*/React.forwardRef(function SelectItem(props, forwardedRef) { const { value: valueProp = null, label, ...otherProps } = props; const listItem = useCompositeListItem({ label }); const { activeIndex, selectedIndex, setActiveIndex } = useSelectIndexContext(); const { getItemProps, setOpen, setValue, open, selectionRef, typingRef, valuesRef, popupRef } = useSelectRootContext(); const selectedIndexRef = useLatestRef(selectedIndex); const indexRef = useLatestRef(listItem.index); const mergedRef = useForkRef(listItem.ref, forwardedRef); useEnhancedEffect(() => { if (listItem.index === -1) { return undefined; } const values = valuesRef.current; values[listItem.index] = valueProp; return () => { delete values[listItem.index]; }; }, [listItem.index, valueProp, valuesRef]); const highlighted = activeIndex === listItem.index; const selected = selectedIndex === listItem.index; return /*#__PURE__*/_jsx(MemoizedInnerSelectItem, { ref: mergedRef, highlighted: highlighted, selected: selected, getRootItemProps: getItemProps, setOpen: setOpen, open: open, selectionRef: selectionRef, typingRef: typingRef, value: valueProp, setValue: setValue, selectedIndexRef: selectedIndexRef, indexRef: indexRef, setActiveIndex: setActiveIndex, popupRef: popupRef, ...otherProps }); }); process.env.NODE_ENV !== "production" ? SelectItem.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, /** * Whether the component should ignore user interaction. * @default false */ disabled: PropTypes.bool, /** * Overrides the text label to use on the trigger when this item is selected * and when the item is matched during keyboard text navigation. */ label: PropTypes.string, /** * A unique value that identifies this select item. * @default null */ value: PropTypes.any } : void 0; export { SelectItem };