import { forwardRef, useRef, useState, useEffect, useCallback } from 'react' import * as RadixMenu from '@radix-ui/react-dropdown-menu' import Block from '../../Primitives/Block/Block' import PrimitiveText from '../../Primitives/PrimitiveText/PrimitiveText' import { Tooltip, TooltipSide } from '../../Tooltip' import { Checkbox } from '../../Checkbox' import { Check } from 'lucide-react' import { type SelectItemProps, SelectItemType } from './types' import { checkIfTruncated, isItemSelected, getRightSlotConfig } from './utils' import { useResponsiveTokens } from '../../../hooks/useResponsiveTokens' import type { MultiSelectTokensType } from '../../MultiSelect/multiSelect.tokens' import type { SingleSelectTokensType } from '../../SingleSelect/singleSelect.tokens' const MenuItemSlot = ({ slot }: { slot: React.ReactNode }) => { return ( {slot} ) } const SelectItem = forwardRef( ( { item, onSelect, selected, type, showCheckmark = true, className, selectedPosition = 'none', index = 0, focusIdentityValue, }, ref ) => { const textRef = useRef(null) const subLabelRef = useRef(null) const [showTooltip, setShowTooltip] = useState(false) const [showSubLabelTooltip, setShowSubLabelTooltip] = useState(false) const multiSelectTokens = useResponsiveTokens('MULTI_SELECT') const singleSelectTokens = useResponsiveTokens('SINGLE_SELECT') const tokens: MultiSelectTokensType | SingleSelectTokensType = type === SelectItemType.MULTI ? multiSelectTokens : singleSelectTokens const isSelected = isItemSelected(item.value, selected, type) const resizeObserverRef = useRef(null) const rafRef = useRef(null) const checkTruncation = useCallback(() => { if (rafRef.current !== null) { cancelAnimationFrame(rafRef.current) } resizeObserverRef.current?.disconnect() rafRef.current = requestAnimationFrame(() => { let nextShowTooltip = false let nextShowSubLabelTooltip = false if (textRef.current) { nextShowTooltip = checkIfTruncated(textRef.current) } if (item.subLabel && subLabelRef.current) { nextShowSubLabelTooltip = checkIfTruncated( subLabelRef.current ) } setShowTooltip((prev) => prev !== nextShowTooltip ? nextShowTooltip : prev ) setShowSubLabelTooltip((prev) => prev !== nextShowSubLabelTooltip ? nextShowSubLabelTooltip : prev ) requestAnimationFrame(() => { if (!resizeObserverRef.current) return if (textRef.current) { resizeObserverRef.current.observe(textRef.current) } if (subLabelRef.current) { resizeObserverRef.current.observe(subLabelRef.current) } }) rafRef.current = null }) }, [item.subLabel]) useEffect(() => { if (item.disableTruncation) return resizeObserverRef.current = new ResizeObserver(() => { checkTruncation() }) checkTruncation() return () => { resizeObserverRef.current?.disconnect() resizeObserverRef.current = null if (rafRef.current !== null) { cancelAnimationFrame(rafRef.current) } } }, [item.label, item.subLabel, item.disableTruncation, checkTruncation]) const handleSelect = (e: Event) => { if (item.disabled) { e.preventDefault() return } if (type === SelectItemType.MULTI) { e.preventDefault() } onSelect(item.value) } const shouldShowAutoTooltip = (showTooltip && item.label) || (showSubLabelTooltip && item.subLabel) const customTooltip = item.tooltip const hasTooltip = shouldShowAutoTooltip || customTooltip const tooltipContent = customTooltip || ( <> {showTooltip && item.label && {item.label}} {showSubLabelTooltip && item.subLabel && ( {item.subLabel} )} ) const rightSlotConfig = getRightSlotConfig(isSelected, type, item) const rightSlotContent = rightSlotConfig ? ( rightSlotConfig.type === 'checkbox' ? ( ) : ( ) ) : null const getBorderRadius = () => { const defaultRadius = tokens?.menu?.item?.borderRadius if (!isSelected || selectedPosition === 'none') { return defaultRadius } switch (selectedPosition) { case 'first': return `${defaultRadius} ${defaultRadius} 0 0` case 'middle': return '0' case 'last': return `0 0 ${defaultRadius} ${defaultRadius}` case 'only': return defaultRadius default: return defaultRadius } } const itemContent = ( {item.slot1 && } {item.label} {item.slot2 && } {item.slot3 && } {item.slot4 && } {showCheckmark && rightSlotContent && ( {rightSlotContent} )} {item.subLabel && ( {item.subLabel} )} ) if (hasTooltip) { return ( {itemContent} ) } return itemContent } ) SelectItem.displayName = 'SelectItem' export default SelectItem export { SelectItemType } from './types' export type { SelectItemProps, BaseSelectItemType } from './types'