import React, { useCallback, useContext, useMemo } from 'react'; import { useForwardedRef, useIdAllocator } from '@leafygreen-ui/hooks'; import { InputOption, InputOptionContent } from '@leafygreen-ui/input-option'; import { useDarkMode } from '@leafygreen-ui/leafygreen-provider'; import { getNodeTextContent } from '@leafygreen-ui/lib'; import { ComboboxContext } from '../ComboboxContext'; import { wrapJSX } from '../utils'; import { getInputOptionStyles } from './ComboboxOption.styles'; import { ComboboxOptionProps, InternalComboboxOptionProps, } from './ComboboxOption.types'; import { getGlyphs } from './getGlyphs'; /** * Internal ComboboxOption Component for use within Combobox only. * * Prefer using {@link ComboboxOption} * @internal */ export const InternalComboboxOption = React.forwardRef< HTMLLIElement, InternalComboboxOptionProps >( ( { glyph, isSelected, displayName, customContent, isFocused, setSelected, className, description, value, onClick, disabled = false, 'aria-label': ariaLabel, ...rest }: InternalComboboxOptionProps, forwardedRef, ) => { const { darkMode, theme } = useDarkMode(); const { multiselect, size, withIcons, inputValue } = useContext(ComboboxContext); const optionRef = useForwardedRef(forwardedRef, null); const optionTextId = useIdAllocator({ prefix: 'combobox-option-text' }); const handleOptionClick = useCallback( (e: React.SyntheticEvent) => { // stopPropagation will not stop the keyDown event (only click) // since the option is never `focused`, only `aria-selected` // the keyDown event does not actually fire on the option element e.stopPropagation(); if (!disabled) { setSelected(); onClick?.(e, value); } }, [disabled, onClick, setSelected, value], ); const { leftGlyph, rightGlyph } = useMemo( () => getGlyphs({ withIcons, isSelected, glyph, theme, darkMode, size, disabled, multiselect, optionTextId, isFocused, }), [ darkMode, disabled, glyph, isSelected, multiselect, optionTextId, size, theme, withIcons, isFocused, ], ); // When multiselect and withoutIcons the Checkbox is aligned to the top instead of centered. const multiSelectWithoutIcons = multiselect && !withIcons; // Convert displayName ReactNode to string for aria-label and wrapJSX const displayNameStr = getNodeTextContent(displayName); return ( {customContent ? ( customContent ) : ( {wrapJSX(displayNameStr, inputValue, 'strong')} )} ); }, ); InternalComboboxOption.displayName = 'ComboboxOption'; export function ComboboxOption(_: ComboboxOptionProps): JSX.Element { throw Error('`ComboboxOption` must be a child of a `Combobox` instance'); } ComboboxOption.displayName = 'ComboboxOption';