import React from 'react'; import type { BreadcrumbItem, BreadcrumbPressEvent, BreadcrumbsProps, } from '../../../../types/breadcrumbs'; import { Button } from '../../../button/public'; import { Icon } from '../../../icon/public'; import { resolveIconSize } from '../../../icon/utils/resolveIconSize'; import { View } from '../../../layout/public'; import { withZoraThemeScope } from '../../../theme/adapters/inbound/withZoraThemeScope'; import { useZoraTheme } from '../../../theme/composition/useZoraTheme'; import { Text } from '../../../typography/public'; /*** Renders a breadcrumb trail whose final item represents the current location. */ export const Breadcrumbs = withZoraThemeScope(BreadcrumbsInner); type BreadcrumbRenderItem = | { item: BreadcrumbItem; type: 'item'; } | { type: 'ellipsis'; }; /*** Connects serializable breadcrumb items to the component-level item press event. */ function BreadcrumbsInner({ themeId: _themeId, mode: _mode, interactionPolicy, items, separator = '/', maxItems, compact = false, disabled = false, onItemPress, testID, }: BreadcrumbsProps) { const normalizedMaxItems = normalizeMaxItems(maxItems); const renderItems = createRenderItems(items, normalizedMaxItems); const currentItemId = items.at(-1)?.id; if (items.length === 0) return null; return ( {renderItems.map((renderItem, index) => { const showSeparator = index !== renderItems.length - 1; if (renderItem.type === 'ellipsis') { return ( … {showSeparator ? renderSeparator(separator) : null} ); } return ( ); })} ); } /*** Renders one breadcrumb item and its optional following separator. */ function BreadcrumbItemView({ compact, current, disabled, interactionPolicy, item, onItemPress, separator, testID, }: { compact: boolean; current: boolean; disabled: boolean; interactionPolicy: BreadcrumbsProps['interactionPolicy']; item: BreadcrumbItem; onItemPress: ((event: BreadcrumbPressEvent) => void) | undefined; separator: string | undefined; testID: string | undefined; }) { const interactive = !current && !disabled && !item.disabled && onItemPress !== undefined; return ( <> {interactive ? ( ) : ( )} {separator === undefined ? null : renderSeparator(separator)} ); } /*** Renders one non-interactive breadcrumb label. */ function BreadcrumbLabel({ item, compact }: { item: BreadcrumbItem; compact: boolean }) { const { theme } = useZoraTheme(); return ( {item.icon ? ( ) : null} {item.label} ); } /*** Renders the configured separator with muted breadcrumb styling. */ function renderSeparator(separator: string): React.ReactNode { return ( {separator} ); } /*** Normalizes an optional maximum breadcrumb item count. */ function normalizeMaxItems(value: number | undefined): number | undefined { if (value === undefined || !Number.isFinite(value)) return undefined; return Math.max(0, Math.trunc(value)); } /*** Collapses middle breadcrumb items into one ellipsis when required. */ function createRenderItems( items: readonly BreadcrumbItem[], maxItems: number | undefined, ): readonly BreadcrumbRenderItem[] { if (maxItems === undefined || maxItems === 0 || items.length <= maxItems || maxItems < 3) { return items.map((item) => ({ item, type: 'item' })); } const firstItem = items.at(0); if (firstItem === undefined) return []; const trailingCount = maxItems - 2; const trailingItems = items.slice(items.length - trailingCount); return [ { item: firstItem, type: 'item' }, { type: 'ellipsis' }, ...trailingItems.map((item) => ({ item, type: 'item' }) satisfies BreadcrumbRenderItem), ]; }