import { forwardRef, useRef, useState, useEffect, useCallback } from 'react'
import * as RadixMenu from '@radix-ui/react-dropdown-menu'
import { Check } from 'lucide-react'
import Block from '../Primitives/Block/Block'
import PrimitiveText from '../Primitives/PrimitiveText/PrimitiveText'
import { Checkbox } from '../Checkbox'
import { Tooltip, TooltipSide } from '../Tooltip'
import { checkIfTruncated } from '../Select/SelectItem/utils'
import SelectItemIndicator from './SelectItemIndicator'
import type { SelectItemV2Props } from './types'
const SlotWrapper = ({ slot }: { slot: React.ReactNode }) => (
{slot}
)
const SelectItemV2 = forwardRef(
(props, ref) => {
const {
item,
onSelect,
itemTokens,
index = 0,
selectedPosition = 'none',
className,
asMenuItem = true,
focusIdentityValue,
role,
tabIndex,
ariaSelected,
ariaSetSize,
ariaPosInSet,
ariaDescription,
decorativeIndicator = false,
} = props
const textRef = useRef(null)
const subLabelRef = useRef(null)
const [showTooltip, setShowTooltip] = useState(false)
const [showSubLabelTooltip, setShowSubLabelTooltip] = useState(false)
const resizeObserverRef = useRef(null)
const rafRef = useRef(null)
const isSelected =
props.mode === 'single'
? props.selected === item.value
: props.selectedValues.includes(item.value)
const isMulti = props.mode === 'multi'
const showCheckmark =
props.mode === 'single' ? (props.showCheckmark ?? true) : false
const resolvedRole = role ?? (isMulti ? 'option' : 'menuitem')
const isListRow = role !== undefined
const checkTruncation = useCallback(() => {
if (rafRef.current !== null) cancelAnimationFrame(rafRef.current)
resizeObserverRef.current?.disconnect()
rafRef.current = requestAnimationFrame(() => {
let nextShow = false
let nextSub = false
if (textRef.current)
nextShow = checkIfTruncated(textRef.current)
if (item.subLabel && subLabelRef.current)
nextSub = checkIfTruncated(subLabelRef.current)
setShowTooltip((p) => (p !== nextShow ? nextShow : p))
setShowSubLabelTooltip((p) => (p !== nextSub ? nextSub : p))
requestAnimationFrame(() => {
const ro = resizeObserverRef.current
if (!ro) return
if (textRef.current) ro.observe(textRef.current)
if (subLabelRef.current) ro.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 (isMulti) e.preventDefault()
onSelect(item.value)
}
const handleClick = () => {
if (item.disabled) return
onSelect(item.value)
}
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key !== 'Enter' && e.key !== ' ') return
e.preventDefault()
handleClick()
}
const hasTooltip =
(showTooltip && !!item.label) ||
(showSubLabelTooltip && !!item.subLabel) ||
!!item.tooltip
const tooltipContent = item.tooltip ?? (
<>
{showTooltip && item.label && {item.label}}
{showSubLabelTooltip && item.subLabel && (
{item.subLabel}
)}
>
)
const defaultRadius = String(itemTokens.borderRadius ?? '')
const getBorderRadius = (): string => {
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}`
default:
return defaultRadius
}
}
const colorState = item.disabled
? 'disabled'
: isSelected
? 'selected'
: 'default'
const itemBlock = (
{item.slot1 && }
{item.label}
{item.slot2 && }
{item.slot3 && }
{item.slot4 && }
{showCheckmark && isSelected && (
)}
{isMulti &&
(decorativeIndicator ? (
) : (
))}
{item.subLabel && (
{item.subLabel}
)}
)
const itemContent = asMenuItem ? (
{itemBlock}
) : (
itemBlock
)
if (hasTooltip) {
return (
{itemContent}
)
}
return itemContent
}
)
SelectItemV2.displayName = 'SelectItemV2'
export default SelectItemV2