import React, { useContext, useState, MouseEvent, ReactNode, FC, useEffect } from 'react'; import classNames from 'classnames'; import { NavigationContext } from '../../context'; import NewBadge from '../../../new-badge'; import { Secondary } from './components'; import { ConfigContext } from '../../../config-provider'; interface ItemURLDetails { path: string; exact?: boolean; disabled?: boolean; excludePaths?: string[]; } export interface SubNavigationItem extends ItemURLDetails { path: string; name: string; badge?: ReactNode; onClick?(): void; } interface SecondaryAction { path: string; icon: React.ReactNode; } export interface ItemProps extends ItemURLDetails { icon?: React.ReactNode; badge?: ReactNode; name?: string; subNavigationItems?: SubNavigationItem[]; secondaryAction?: SecondaryAction; onClick?(): void; selected?: boolean; } enum MatchState { MatchUrl, NoMatch, Excluded, } function normalizePathname(pathname: string) { const barePathname = pathname.split('?')[0].split('#')[0]; return barePathname.endsWith('/') ? barePathname : `${barePathname}/`; } function safeEqual(location: string, path: string) { return normalizePathname(location) === normalizePathname(path); } function safeStartsWith(location: string, path: string) { return normalizePathname(location).startsWith(normalizePathname(path)); } function matchStateForItem({ path, exact, excludePaths }: ItemURLDetails, location: string) { if (path == null) { return MatchState.NoMatch; } if (excludePaths && excludePaths.some(excludePath => safeStartsWith(location, excludePath))) { return MatchState.Excluded; } const matchesUrl = exact ? safeEqual(location, path) : safeStartsWith(location, path); return matchesUrl ? MatchState.MatchUrl : MatchState.NoMatch; } export const Item: FC = props => { const { path, icon, name, subNavigationItems = [], excludePaths, secondaryAction, disabled, onClick, badge, exact, } = props; const { location, menuItemRender } = useContext(NavigationContext); const [expanded, setExpanded] = useState(false); const { getPrefixCls } = useContext(ConfigContext); const prefixCls = getPrefixCls('navigation'); useEffect(() => { if (expanded) { setExpanded(false); } }, [expanded]); // 动态小圆点 // const indicatorMarkup = ( // // // // ) : null; const iconMarkup = icon ?
{icon}
: null; let badgeMarkup: ReactNode = null; if (typeof badge === 'string') { badgeMarkup = {badge}; } else { badgeMarkup = badge; } const wrappedBadgeMarkup = badgeMarkup == null ? null :
{badgeMarkup}
; function getClickHandler(click: ItemProps['onClick']) { return (event: MouseEvent) => { const { currentTarget } = event; if (currentTarget.getAttribute('href') === location) { event.preventDefault(); } if (subNavigationItems && subNavigationItems.length > 0) { event.preventDefault(); setExpanded(!expanded); } if (click) { click(); } }; } const matchState = matchStateForItem({ path, exact, excludePaths }, location); const matchingSubNavigationItems = subNavigationItems.filter(item => { const subMatchState = matchStateForItem(item, location); return subMatchState === MatchState.MatchUrl; }); const childIsActive = matchingSubNavigationItems.length > 0; const selected = props.selected !== undefined ? props.selected : matchState === MatchState.MatchUrl; const showExpanded = selected || expanded || childIsActive; const itemClassName = classNames({ [`${prefixCls}-item`]: true, [`${prefixCls}-item-disabled`]: disabled, [`${prefixCls}-item-selected`]: selected && subNavigationItems.length === 0, [`${prefixCls}-sub-navigation-active`]: showExpanded, }); const itemContentMarkup = ( <> {iconMarkup} {name} {/* {indicatorMarkup} */} {subNavigationItems.length > 0 && ( )} {wrappedBadgeMarkup} ); if (path == null) { const className = classNames({ [`${prefixCls}-item`]: true, [`${prefixCls}-item-disabled`]: disabled, }); return (
  • ); } const secondaryActionMarkup = secondaryAction && (
    {secondaryAction.icon}
    ); let secondaryNavigationMarkup: ReactNode = null; if (subNavigationItems.length > 0) { const SecondaryNavigationClassName = classNames({ [`${prefixCls}-secondary-navigation`]: true, [`${prefixCls}-is-expanded`]: showExpanded, [`${prefixCls}-secondary-navigation-no-icon`]: !icon, }); secondaryNavigationMarkup = (
    {subNavigationItems.map(item => { const { name: itemName, ...rest } = item; return ; })}
    ); } const className = classNames({ [`${prefixCls}-list-item`]: true, [`${prefixCls}-list-item-has-action`]: secondaryAction, }); return (
  • {menuItemRender ? ( menuItemRender( { className: itemClassName, onClick: getClickHandler(onClick) }, props, itemContentMarkup, ) ) : ( {itemContentMarkup} )} {secondaryActionMarkup}
    {secondaryNavigationMarkup}
  • ); };