import { forwardRef, useRef, useEffect, useState, useId } from 'react' import type { CSSObject } from 'styled-components' import React from 'react' import { KeyValuePairPropTypes, KeyValuePairSize, KeyValuePairStateType, TextOverflowMode, } from './types' import Block from '../Primitives/Block/Block' import { useResponsiveTokens } from '../../hooks/useResponsiveTokens' import { KeyValuePairTokensType } from './KeyValuePair.tokens' import PrimitiveText from '../Primitives/PrimitiveText/PrimitiveText' import Tooltip from '../Tooltip/Tooltip' import { TooltipSide } from '../Tooltip/types' import { getTextStyles, getPrimitiveTextStyles, getContainerStyles, getLayoutStyles, getSlotStyles, } from './utils' const ResponsiveText = ({ children, fontSize, color, className, fontWeight, textOverflow = 'truncate', maxLines = 2, showTooltipOnTruncate = true, as: Component = 'div', id, role, slotPresent, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, }: { children: string fontSize: CSSObject['fontSize'] color: CSSObject['color'] className?: string fontWeight?: CSSObject['fontWeight'] textOverflow?: TextOverflowMode maxLines?: number showTooltipOnTruncate?: boolean as?: React.ElementType id?: string role?: string slotPresent: boolean 'aria-label'?: string 'aria-labelledby'?: string }) => { const textRef = useRef(null) const [isTruncated, setIsTruncated] = useState(false) useEffect(() => { const element = textRef.current if (!element || textOverflow === 'wrap' || !showTooltipOnTruncate) { setIsTruncated(false) return } const checkTruncation = () => { if (textOverflow === 'truncate') { const textElement = element.firstElementChild as HTMLElement if (textElement) { const isOverflowing = textElement.scrollWidth > textElement.clientWidth setIsTruncated(isOverflowing) } } else if (textOverflow === 'wrap-clamp') { setIsTruncated(element.scrollHeight > element.clientHeight) } } const timeoutId = setTimeout(checkTruncation, 0) window.addEventListener('resize', checkTruncation) return () => { clearTimeout(timeoutId) window.removeEventListener('resize', checkTruncation) } }, [children, textOverflow, maxLines, showTooltipOnTruncate]) const textElement = ( {children} ) if (showTooltipOnTruncate && isTruncated) { return ( {textElement} ) } return textElement } const KeyValuePair = forwardRef( ( { keyString, size = KeyValuePairSize.SMALL, value, keySlot, valueLeftSlot, valueRightSlot, keyValuePairState = KeyValuePairStateType.vertical, maxWidth = '220px', textOverflow = 'truncate', maxLines = 2, showTooltipOnTruncate = true, }, ref ) => { const keyValuePairTokens = useResponsiveTokens('KEYVALUEPAIR') const containerStyles = { ...getLayoutStyles(keyValuePairState, keyValuePairTokens), ...getContainerStyles(textOverflow, maxWidth), } const keyContainerStyles = { display: 'flex', gap: keyValuePairTokens.key.gap, alignItems: textOverflow !== 'truncate' ? 'self-start' : 'center', } const valueContainerStyles = { display: 'flex', gap: keyValuePairTokens.value.gap, alignItems: textOverflow !== 'truncate' ? 'self-start' : 'center', } const baseId = useId() const keyId = `${baseId}-key` const valueId = `${baseId}-value` const keyFlexClass = keySlot ? '' : 'flex-1' const valueFlexClass = valueRightSlot ? '' : 'flex-1' return ( {keyString} {keySlot && ( {keySlot} )} {valueLeftSlot && ( {valueLeftSlot} )} {value || ''} {valueRightSlot && ( {valueRightSlot} )} ) } ) KeyValuePair.displayName = 'KeyValuePair' export default KeyValuePair