import { ReactElement, ReactNode, useEffect, useState } from 'react'; import { TOKEN_COLOR_BACKGROUND_SELECTED_BASE, TOKEN_SPACE_400, TOKEN_SPACE_1200, } from '@swipebox/morphe-design-tokens'; import PrimaryActionIconButton from './PrimaryActionIconButton'; import Badge from '../Badge'; import Box from '../Box'; import { useDeviceType } from '../contexts/DeviceTypeProvider'; import { useNesting } from '../contexts/NestingProvider'; import { useSideNavigation } from '../contexts/SideNavigationProvider'; import Flex from '../Flex'; import Icon from '../Icon'; import icons from '../icons/index'; import Indicator from '../Indicator'; import Text from '../Text'; import { Indexable } from '../zIndex'; function getTextColor({ active, disabled, subtext, }: { active?: string; disabled?: boolean; subtext?: boolean; }) { if (active) { return 'inverse'; } if (disabled) { return 'disabled'; } if (subtext) { return 'subtle'; } return 'default'; } export const NESTING_MARGIN_START_MAP = { '0': TOKEN_SPACE_400, '1': TOKEN_SPACE_1200, '2': '68px', } as const; type IconType = keyof typeof icons | { __path: string }; type BadgeType = { text: string; type?: 'info' | 'error' | 'warning' | 'success' | 'neutral'; }; type Counter = { number: string; accessibilityLabel: string; }; type PrimaryAction = { icon?: 'ellipsis' | 'edit' | 'trash-can'; onClick?: (arg1: { event: React.MouseEvent | React.KeyboardEvent; }) => void; tooltip: { accessibilityLabel?: string; text: string; zIndex?: Indexable; }; dropdownItems?: ReadonlyArray; }; type Props = { active?: 'page' | 'section'; hovered: boolean; focused: boolean; badge?: BadgeType; counter?: Counter; icon?: IconType; label: string; subtext?: string; disabled?: boolean; notificationAccessibilityLabel?: string; primaryAction?: PrimaryAction; setCompression: (arg1: 'compress' | 'none') => void; hasBorder?: boolean; isGroup?: boolean; children?: ReactNode; }; export default function ItemContent({ active, badge, counter, icon, label, subtext, disabled, primaryAction, notificationAccessibilityLabel, setCompression, hovered, focused, hasBorder, isGroup, children, }: Props) { const { nestedLevel: level } = useNesting(); const nestedLevel = isGroup ? level - 1 // compensate for NestingLevelProvider always wrapping ItemContent in SideNavigationGroup : level; const isTopLevel = nestedLevel === 0; const deviceType = useDeviceType(); const isMobile = deviceType === 'mobile'; const { collapsed: sideNavigationCollapsed, overlayPreview } = useSideNavigation(); const [forceIconButton, setForceIconButton] = useState<'force' | 'default'>('default'); const [showIconButton, setShowIconButton] = useState<'hide' | 'show'>('hide'); useEffect(() => { if (!isMobile && primaryAction && showIconButton === 'hide' && (hovered || focused)) { setShowIconButton('show'); } if ( !isMobile && primaryAction && showIconButton === 'show' && !hovered && !focused && forceIconButton === 'default' ) { setShowIconButton('hide'); } }, [hovered, focused, primaryAction, forceIconButton, isMobile, showIconButton]); const collapsed = sideNavigationCollapsed && !overlayPreview; const inactiveItemColor = hovered ? 'secondary' : undefined; const itemColor = active ? 'selected' : inactiveItemColor; const mainTextColor = getTextColor({ active, disabled }); const subTextColor = getTextColor({ active, disabled, subtext: true }); const counterColor = active ? 'inverse' : 'subtle'; const nestingMargin = isMobile ? // @ts-expect-error - TS7053 - Element implicitly has an 'any' type because expression of type 'number' can't be used to index type '{ readonly '0': "var(--space-400)"; readonly '1': "var(--space-1200)"; readonly '2': "68px"; }'. NESTING_MARGIN_START_MAP[isTopLevel ? 0 : nestedLevel - 1] : // @ts-expect-error - TS7053 - Element implicitly has an 'any' type because expression of type 'number' can't be used to index type '{ readonly '0': "var(--space-400)"; readonly '1': "var(--space-1200)"; readonly '2': "68px"; }'. NESTING_MARGIN_START_MAP[nestedLevel]; return ( {collapsed && icon && notificationAccessibilityLabel ? ( ) : null} {icon ? ( {typeof icon === 'string' ? ( ) : ( )} ) : null} {!collapsed && ( {label} {subtext && ( {subtext} )} {(badge || notificationAccessibilityLabel) && !disabled && ( {/* Adds a pause for screen reader users between the text content */} {`, `} {!notificationAccessibilityLabel && badge ? ( ) : null} {notificationAccessibilityLabel ? ( ) : null} )} )} {!collapsed && counter && (showIconButton === 'hide' || isMobile) ? ( {`, `} {counter.number} ) : null} {!collapsed && (showIconButton === 'show' || isMobile) && primaryAction ? ( {/* This is a workaround to announce the counter as it's replaced on focus */} {counter ? ( {`, `} ) : null} ) : null} {children} ); }