2016-12-30 21:41:28 +08:00
|
|
|
import classNames from 'classnames';
|
2019-08-20 15:58:37 +08:00
|
|
|
import toArray from 'rc-util/lib/Children/toArray';
|
2023-03-05 20:57:49 +08:00
|
|
|
import pickAttrs from 'rc-util/lib/pickAttrs';
|
2023-05-06 15:49:37 +08:00
|
|
|
import * as React from 'react';
|
2020-05-14 20:54:49 +08:00
|
|
|
import { cloneElement } from '../_util/reactNode';
|
2022-06-22 14:57:09 +08:00
|
|
|
import warning from '../_util/warning';
|
2023-04-27 22:43:42 +08:00
|
|
|
import { ConfigContext } from '../config-provider';
|
2022-11-15 15:10:47 +08:00
|
|
|
import type { BreadcrumbItemProps } from './BreadcrumbItem';
|
2023-04-27 22:43:42 +08:00
|
|
|
import BreadcrumbItem, { InternalBreadcrumbItem } from './BreadcrumbItem';
|
2022-06-22 14:57:09 +08:00
|
|
|
import BreadcrumbSeparator from './BreadcrumbSeparator';
|
2016-03-31 17:46:35 +08:00
|
|
|
|
2023-06-23 00:45:47 +08:00
|
|
|
import type { DropdownProps } from '../dropdown';
|
2022-05-24 01:13:36 +08:00
|
|
|
import useStyle from './style';
|
2023-04-27 22:43:42 +08:00
|
|
|
import useItemRender from './useItemRender';
|
2023-03-05 20:57:49 +08:00
|
|
|
import useItems from './useItems';
|
2016-03-31 17:46:35 +08:00
|
|
|
|
2023-03-05 20:57:49 +08:00
|
|
|
export interface BreadcrumbItemType {
|
|
|
|
key?: React.Key;
|
|
|
|
/**
|
|
|
|
* Different with `path`. Directly set the link of this item.
|
|
|
|
*/
|
|
|
|
href?: string;
|
|
|
|
/**
|
|
|
|
* Different with `href`. It will concat all prev `path` to the current one.
|
|
|
|
*/
|
|
|
|
path?: string;
|
2023-04-27 22:43:42 +08:00
|
|
|
title?: React.ReactNode;
|
|
|
|
/* @deprecated Please use `title` instead */
|
|
|
|
breadcrumbName?: string;
|
2023-03-05 20:57:49 +08:00
|
|
|
menu?: BreadcrumbItemProps['menu'];
|
|
|
|
/** @deprecated Please use `menu` instead */
|
|
|
|
overlay?: React.ReactNode;
|
2023-03-17 11:26:36 +08:00
|
|
|
className?: string;
|
2023-06-23 00:45:47 +08:00
|
|
|
dropdownProps?: DropdownProps;
|
2023-03-17 11:26:36 +08:00
|
|
|
onClick?: React.MouseEventHandler<HTMLAnchorElement | HTMLSpanElement>;
|
2023-04-27 22:43:42 +08:00
|
|
|
|
|
|
|
/** @deprecated Please use `menu` instead */
|
|
|
|
children?: Omit<BreadcrumbItemType, 'children'>[];
|
2023-03-05 20:57:49 +08:00
|
|
|
}
|
|
|
|
export interface BreadcrumbSeparatorType {
|
|
|
|
type: 'separator';
|
|
|
|
separator?: React.ReactNode;
|
|
|
|
}
|
|
|
|
|
2023-04-27 22:43:42 +08:00
|
|
|
export type ItemType = Partial<BreadcrumbItemType & BreadcrumbSeparatorType>;
|
2023-03-05 20:57:49 +08:00
|
|
|
|
2023-04-27 22:43:42 +08:00
|
|
|
export type InternalRouteType = Partial<BreadcrumbItemType & BreadcrumbSeparatorType>;
|
2023-03-05 20:57:49 +08:00
|
|
|
|
2023-06-29 13:03:52 +08:00
|
|
|
export interface BreadcrumbProps<T extends Record<PropertyKey, any> = any> {
|
2016-08-29 16:52:35 +08:00
|
|
|
prefixCls?: string;
|
2023-06-29 13:03:52 +08:00
|
|
|
params?: T;
|
2017-01-20 20:10:50 +08:00
|
|
|
separator?: React.ReactNode;
|
2016-08-29 16:52:35 +08:00
|
|
|
style?: React.CSSProperties;
|
2016-12-30 21:41:28 +08:00
|
|
|
className?: string;
|
2023-01-20 11:03:50 +08:00
|
|
|
rootClassName?: string;
|
2022-04-08 22:55:42 +08:00
|
|
|
children?: React.ReactNode;
|
2016-08-29 16:52:35 +08:00
|
|
|
|
2023-03-05 20:57:49 +08:00
|
|
|
/** @deprecated Please use `items` instead */
|
2023-04-27 22:43:42 +08:00
|
|
|
routes?: ItemType[];
|
2023-03-05 20:57:49 +08:00
|
|
|
|
2023-04-27 22:43:42 +08:00
|
|
|
items?: ItemType[];
|
2023-03-05 20:57:49 +08:00
|
|
|
|
2023-06-29 13:03:52 +08:00
|
|
|
itemRender?: (route: ItemType, params: T, routes: ItemType[], paths: string[]) => React.ReactNode;
|
2023-03-05 20:57:49 +08:00
|
|
|
}
|
|
|
|
|
2023-06-29 13:03:52 +08:00
|
|
|
const getPath = <T extends Record<PropertyKey, any> = any>(params: T, path?: string) => {
|
2023-03-05 20:57:49 +08:00
|
|
|
if (path === undefined) {
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
let mergedPath = (path || '').replace(/^\//, '');
|
2022-11-15 15:10:47 +08:00
|
|
|
Object.keys(params).forEach((key) => {
|
2023-03-05 20:57:49 +08:00
|
|
|
mergedPath = mergedPath.replace(`:${key}`, params[key]!);
|
2020-05-28 15:22:00 +08:00
|
|
|
});
|
2023-03-05 20:57:49 +08:00
|
|
|
return mergedPath;
|
2020-05-28 15:22:00 +08:00
|
|
|
};
|
2016-07-14 13:29:50 +08:00
|
|
|
|
2023-06-29 13:03:52 +08:00
|
|
|
const Breadcrumb = <T extends Record<PropertyKey, any> = any>(props: BreadcrumbProps<T>) => {
|
2023-03-05 20:57:49 +08:00
|
|
|
const {
|
|
|
|
prefixCls: customizePrefixCls,
|
|
|
|
separator = '/',
|
|
|
|
style,
|
|
|
|
className,
|
|
|
|
rootClassName,
|
|
|
|
routes: legacyRoutes,
|
|
|
|
items,
|
|
|
|
children,
|
|
|
|
itemRender,
|
|
|
|
params = {},
|
|
|
|
...restProps
|
2023-04-27 22:43:42 +08:00
|
|
|
} = props;
|
2023-03-05 20:57:49 +08:00
|
|
|
|
2020-05-28 15:22:00 +08:00
|
|
|
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
|
|
|
|
2022-10-14 11:37:48 +08:00
|
|
|
let crumbs: React.ReactNode;
|
2020-05-28 15:22:00 +08:00
|
|
|
const prefixCls = getPrefixCls('breadcrumb', customizePrefixCls);
|
2022-05-24 01:13:36 +08:00
|
|
|
const [wrapSSR, hashId] = useStyle(prefixCls);
|
|
|
|
|
2023-03-05 20:57:49 +08:00
|
|
|
const mergedItems = useItems(items, legacyRoutes);
|
|
|
|
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
|
|
warning(!legacyRoutes, 'Breadcrumb', '`routes` is deprecated. Please use `items` instead.');
|
|
|
|
}
|
|
|
|
|
2023-04-27 22:43:42 +08:00
|
|
|
const mergedItemRender = useItemRender(prefixCls, itemRender);
|
2023-03-05 20:57:49 +08:00
|
|
|
|
|
|
|
if (mergedItems && mergedItems.length > 0) {
|
2020-05-28 15:22:00 +08:00
|
|
|
// generated by route
|
2019-05-06 12:04:39 +08:00
|
|
|
const paths: string[] = [];
|
|
|
|
|
2023-06-29 13:03:52 +08:00
|
|
|
const itemRenderRoutes = items || legacyRoutes;
|
2023-03-05 20:57:49 +08:00
|
|
|
|
|
|
|
crumbs = mergedItems.map((item, index) => {
|
2023-03-17 11:26:36 +08:00
|
|
|
const {
|
|
|
|
path,
|
|
|
|
key,
|
|
|
|
type,
|
|
|
|
menu,
|
|
|
|
overlay,
|
|
|
|
onClick,
|
|
|
|
className: itemClassName,
|
|
|
|
separator: itemSeparator,
|
2023-06-23 00:45:47 +08:00
|
|
|
dropdownProps,
|
2023-03-17 11:26:36 +08:00
|
|
|
} = item;
|
2023-03-05 20:57:49 +08:00
|
|
|
const mergedPath = getPath(params, path);
|
|
|
|
|
|
|
|
if (mergedPath !== undefined) {
|
|
|
|
paths.push(mergedPath);
|
2019-05-06 12:04:39 +08:00
|
|
|
}
|
2023-03-05 20:57:49 +08:00
|
|
|
|
|
|
|
const mergedKey = key ?? index;
|
|
|
|
|
|
|
|
if (type === 'separator') {
|
|
|
|
return <BreadcrumbSeparator key={mergedKey}>{itemSeparator}</BreadcrumbSeparator>;
|
2019-05-06 12:04:39 +08:00
|
|
|
}
|
2016-09-09 13:55:46 +08:00
|
|
|
|
2023-03-05 20:57:49 +08:00
|
|
|
const itemProps: BreadcrumbItemProps = {};
|
|
|
|
const isLastItem = index === mergedItems.length - 1;
|
2022-11-15 15:10:47 +08:00
|
|
|
|
2023-03-05 20:57:49 +08:00
|
|
|
if (menu) {
|
|
|
|
itemProps.menu = menu;
|
|
|
|
} else if (overlay) {
|
|
|
|
itemProps.overlay = overlay as any;
|
|
|
|
}
|
|
|
|
|
|
|
|
let { href } = item;
|
|
|
|
if (paths.length && mergedPath !== undefined) {
|
|
|
|
href = `#/${paths.join('/')}`;
|
2022-11-15 15:10:47 +08:00
|
|
|
}
|
|
|
|
|
2019-05-06 12:04:39 +08:00
|
|
|
return (
|
2023-04-27 22:43:42 +08:00
|
|
|
<InternalBreadcrumbItem
|
2023-03-05 20:57:49 +08:00
|
|
|
key={mergedKey}
|
|
|
|
{...itemProps}
|
|
|
|
{...pickAttrs(item, {
|
|
|
|
data: true,
|
|
|
|
aria: true,
|
|
|
|
})}
|
2023-06-23 00:45:47 +08:00
|
|
|
className={itemClassName}
|
|
|
|
dropdownProps={dropdownProps}
|
2023-03-05 20:57:49 +08:00
|
|
|
href={href}
|
|
|
|
separator={isLastItem ? '' : separator}
|
2023-03-17 11:26:36 +08:00
|
|
|
onClick={onClick}
|
2023-04-27 22:43:42 +08:00
|
|
|
prefixCls={prefixCls}
|
2023-03-05 20:57:49 +08:00
|
|
|
>
|
2023-06-29 13:03:52 +08:00
|
|
|
{mergedItemRender(item, params, itemRenderRoutes!, paths, href)}
|
2023-04-27 22:43:42 +08:00
|
|
|
</InternalBreadcrumbItem>
|
2019-05-06 12:04:39 +08:00
|
|
|
);
|
|
|
|
});
|
2020-05-28 15:22:00 +08:00
|
|
|
} else if (children) {
|
2023-02-24 18:20:28 +08:00
|
|
|
const childrenLength = toArray(children).length;
|
2020-05-28 15:22:00 +08:00
|
|
|
crumbs = toArray(children).map((element: any, index) => {
|
|
|
|
if (!element) {
|
|
|
|
return element;
|
|
|
|
}
|
2023-03-05 20:57:49 +08:00
|
|
|
// =================== Warning =====================
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
|
|
warning(
|
|
|
|
!element,
|
|
|
|
'Breadcrumb',
|
|
|
|
'`Breadcrumb.Item and Breadcrumb.Separator` is deprecated. Please use `items` instead.',
|
|
|
|
);
|
|
|
|
}
|
2022-05-10 15:43:29 +08:00
|
|
|
warning(
|
2020-05-28 15:22:00 +08:00
|
|
|
element.type &&
|
|
|
|
(element.type.__ANT_BREADCRUMB_ITEM === true ||
|
|
|
|
element.type.__ANT_BREADCRUMB_SEPARATOR === true),
|
|
|
|
'Breadcrumb',
|
|
|
|
"Only accepts Breadcrumb.Item and Breadcrumb.Separator as it's children",
|
|
|
|
);
|
2023-02-24 18:20:28 +08:00
|
|
|
const isLastItem = index === childrenLength - 1;
|
2020-05-28 15:22:00 +08:00
|
|
|
return cloneElement(element, {
|
2023-02-24 18:20:28 +08:00
|
|
|
separator: isLastItem ? '' : separator,
|
2020-05-28 15:22:00 +08:00
|
|
|
key: index,
|
2016-03-31 17:46:35 +08:00
|
|
|
});
|
2020-01-21 22:01:53 +08:00
|
|
|
});
|
2018-12-05 19:12:18 +08:00
|
|
|
}
|
2020-05-28 15:22:00 +08:00
|
|
|
|
2020-09-06 13:07:39 +08:00
|
|
|
const breadcrumbClassName = classNames(
|
|
|
|
prefixCls,
|
|
|
|
{
|
|
|
|
[`${prefixCls}-rtl`]: direction === 'rtl',
|
|
|
|
},
|
|
|
|
className,
|
2023-01-20 11:03:50 +08:00
|
|
|
rootClassName,
|
2022-05-24 01:13:36 +08:00
|
|
|
hashId,
|
2020-09-06 13:07:39 +08:00
|
|
|
);
|
2020-05-28 15:22:00 +08:00
|
|
|
|
2022-05-24 01:13:36 +08:00
|
|
|
return wrapSSR(
|
2022-03-10 11:46:42 +08:00
|
|
|
<nav className={breadcrumbClassName} style={style} {...restProps}>
|
2022-03-18 15:20:35 +08:00
|
|
|
<ol>{crumbs}</ol>
|
2022-05-24 01:13:36 +08:00
|
|
|
</nav>,
|
2020-05-28 15:22:00 +08:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
Breadcrumb.Item = BreadcrumbItem;
|
|
|
|
Breadcrumb.Separator = BreadcrumbSeparator;
|
|
|
|
|
2023-01-08 21:30:41 +08:00
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
|
|
Breadcrumb.displayName = 'Breadcrumb';
|
|
|
|
}
|
|
|
|
|
2020-05-28 15:22:00 +08:00
|
|
|
export default Breadcrumb;
|