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.

121 lines (119 loc) 4.12 kB
'use client'; import * as React from 'react'; import { useStore } from '@base-ui-components/utils/store'; import { useComboboxInputValueContext, useComboboxRootContext } from "../root/ComboboxRootContext.js"; import { useRenderElement } from "../../utils/useRenderElement.js"; import { selectors } from "../store.js"; import { useButton } from "../../use-button/index.js"; import { useFieldRootContext } from "../../field/root/FieldRootContext.js"; import { useTransitionStatus } from "../../utils/useTransitionStatus.js"; import { transitionStatusMapping } from "../../utils/stateAttributesMapping.js"; import { useOpenChangeComplete } from "../../utils/useOpenChangeComplete.js"; import { createChangeEventDetails } from "../../utils/createBaseUIEventDetails.js"; import { REASONS } from "../../utils/reasons.js"; import { triggerOpenStateMapping } from "../../utils/popupStateMapping.js"; const stateAttributesMapping = { ...transitionStatusMapping, ...triggerOpenStateMapping }; /** * Clears the value when clicked. * Renders a `<button>` element. */ export const ComboboxClear = /*#__PURE__*/React.forwardRef(function ComboboxClear(componentProps, forwardedRef) { const { render, className, disabled: disabledProp = false, nativeButton = true, keepMounted = false, ...elementProps } = componentProps; const { disabled: fieldDisabled } = useFieldRootContext(); const store = useComboboxRootContext(); const selectionMode = useStore(store, selectors.selectionMode); const comboboxDisabled = useStore(store, selectors.disabled); const readOnly = useStore(store, selectors.readOnly); const open = useStore(store, selectors.open); const selectedValue = useStore(store, selectors.selectedValue); const inputValue = useComboboxInputValueContext(); let visible = false; if (selectionMode === 'none') { visible = inputValue !== ''; } else if (selectionMode === 'single') { visible = selectedValue != null; } else { visible = Array.isArray(selectedValue) && selectedValue.length > 0; } const disabled = fieldDisabled || comboboxDisabled || disabledProp; const { buttonRef, getButtonProps } = useButton({ native: nativeButton, disabled }); const { mounted, transitionStatus, setMounted } = useTransitionStatus(visible); const state = React.useMemo(() => ({ disabled, open, transitionStatus }), [disabled, open, transitionStatus]); useOpenChangeComplete({ open: visible, ref: store.state.clearRef, onComplete() { if (!visible) { setMounted(false); } } }); const element = useRenderElement('button', componentProps, { state, ref: [forwardedRef, buttonRef, store.state.clearRef], props: [{ tabIndex: -1, children: 'x', disabled, 'aria-readonly': readOnly || undefined, // Avoid stealing focus from the input. onMouseDown(event) { event.preventDefault(); }, onClick(event) { if (disabled || readOnly) { return; } const keyboardActiveRef = store.state.keyboardActiveRef; store.state.setInputValue('', createChangeEventDetails(REASONS.clearPress, event.nativeEvent)); if (selectionMode !== 'none') { store.state.setSelectedValue(Array.isArray(selectedValue) ? [] : null, createChangeEventDetails(REASONS.clearPress, event.nativeEvent)); store.state.setIndices({ activeIndex: null, selectedIndex: null, type: keyboardActiveRef.current ? 'keyboard' : 'pointer' }); } else { store.state.setIndices({ activeIndex: null, type: keyboardActiveRef.current ? 'keyboard' : 'pointer' }); } store.state.inputRef.current?.focus(); } }, elementProps, getButtonProps], stateAttributesMapping }); const shouldRender = keepMounted || mounted; if (!shouldRender) { return null; } return element; }); if (process.env.NODE_ENV !== "production") ComboboxClear.displayName = "ComboboxClear";