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.

113 lines (110 loc) 4.36 kB
'use client'; import * as React from 'react'; import { useStore } from '@base-ui-components/utils/store'; import { useStableCallback } from '@base-ui-components/utils/useStableCallback'; import { useRenderElement } from "../../utils/useRenderElement.js"; import { useComboboxDerivedItemsContext, useComboboxFloatingContext, useComboboxRootContext } from "../root/ComboboxRootContext.js"; import { useComboboxPositionerContext } from "../positioner/ComboboxPositionerContext.js"; import { selectors } from "../store.js"; import { ComboboxCollection } from "../collection/ComboboxCollection.js"; import { CompositeList } from "../../composite/list/CompositeList.js"; import { stopEvent } from "../../floating-ui-react/utils.js"; /** * A list container for the items. * Renders a `<div>` element. */ import { jsx as _jsx } from "react/jsx-runtime"; export const ComboboxList = /*#__PURE__*/React.forwardRef(function ComboboxList(componentProps, forwardedRef) { var _ComboboxCollection; const { render, className, children, ...elementProps } = componentProps; const store = useComboboxRootContext(); const floatingRootContext = useComboboxFloatingContext(); const hasPositionerContext = Boolean(useComboboxPositionerContext(true)); const { filteredItems } = useComboboxDerivedItemsContext(); const items = useStore(store, selectors.items); const labelsRef = useStore(store, selectors.labelsRef); const listRef = useStore(store, selectors.listRef); const selectionMode = useStore(store, selectors.selectionMode); const grid = useStore(store, selectors.grid); const popupProps = useStore(store, selectors.popupProps); const disabled = useStore(store, selectors.disabled); const readOnly = useStore(store, selectors.readOnly); const virtualized = useStore(store, selectors.virtualized); const multiple = selectionMode === 'multiple'; const empty = filteredItems.length === 0; const setPositionerElement = useStableCallback(element => { store.set('positionerElement', element); }); const setListElement = useStableCallback(element => { store.set('listElement', element); }); // Support "closed template" API: if children is a function, implicitly wrap it // with a Combobox.Collection that reads items from context/root. // Ensures this component's `popupProps` subscription does not cause <Combobox.Item> // to re-render on every active index change. const resolvedChildren = React.useMemo(() => { if (typeof children === 'function') { return _ComboboxCollection || (_ComboboxCollection = /*#__PURE__*/_jsx(ComboboxCollection, { children: children })); } return children; }, [children]); const state = React.useMemo(() => ({ empty }), [empty]); const floatingId = floatingRootContext.useState('floatingId'); const element = useRenderElement('div', componentProps, { state, ref: [forwardedRef, setListElement, hasPositionerContext ? null : setPositionerElement], props: [popupProps, { children: resolvedChildren, tabIndex: -1, id: floatingId, role: grid ? 'grid' : 'listbox', 'aria-multiselectable': multiple ? 'true' : undefined, onKeyDown(event) { if (disabled || readOnly) { return; } if (event.key === 'Enter') { const activeIndex = store.state.activeIndex; if (activeIndex == null) { // Allow form submission when no item is highlighted. return; } stopEvent(event); const nativeEvent = event.nativeEvent; const listItem = store.state.listRef.current[activeIndex]; if (listItem) { store.state.selectionEventRef.current = nativeEvent; listItem.click(); store.state.selectionEventRef.current = null; } } }, onKeyDownCapture() { store.state.keyboardActiveRef.current = true; }, onPointerMoveCapture() { store.state.keyboardActiveRef.current = false; } }, elementProps] }); if (virtualized) { return element; } return /*#__PURE__*/_jsx(CompositeList, { elementsRef: listRef, labelsRef: items ? undefined : labelsRef, children: element }); }); if (process.env.NODE_ENV !== "production") ComboboxList.displayName = "ComboboxList";