Skip to content

Commit

Permalink
feat: [breadcrumb] 面包屑组件代码优化 fix kingdee#993
Browse files Browse the repository at this point in the history
  • Loading branch information
邵祺 authored and 邵祺 committed Dec 9, 2024
1 parent 1e51e8d commit 391acfe
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 93 deletions.
168 changes: 81 additions & 87 deletions components/breadcrumb/breadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FunctionComponentElement, useContext, useEffect, useRef } from 'react'
import React, { FunctionComponentElement, useCallback, useContext, useEffect, useRef } from 'react'
import classNames from 'classnames'
import ConfigContext from '../config-provider/ConfigContext'
import { getCompProps } from '../_utils'
Expand All @@ -24,7 +24,7 @@ const Breadcrumb = (props: IBreadcrumbProps): FunctionComponentElement<IBreadcru
const breadcrumbHideIconRef = useRef<HTMLDivElement>(null)
const [itemsConfig, setItemsConfig] = React.useState<IBreadcrumbItem[]>(items)
const [itemsArray, setItemsArray] = React.useState<any>()
const [breadcrumbWidth, setBreadcrumbWidth] = React.useState<number>()
const [breadcrumbWidth, setBreadcrumbWidth] = React.useState<number>(0)
const [openEllipsis, setOpenEllipsis] = React.useState<boolean>(false)

const breadcrumbPrefixCls = getPrefixCls!(prefixCls, 'breadcrumb', customPrefixcls)
Expand All @@ -33,56 +33,51 @@ const Breadcrumb = (props: IBreadcrumbProps): FunctionComponentElement<IBreadcru
const breadcrumbMorePanelClass = classNames(`${breadcrumbPrefixCls}-more-panel`)
const breadcrumbSeparatorClass = classNames(`${breadcrumbPrefixCls}-item-separator`)
const breadcrumbHideIconClass = classNames(`${breadcrumbPrefixCls}-hide-icon`)
const getBreadcrumbItemClass = (item: IBreadcrumbItem, isLast: boolean) => {
return classNames(item.className, `${breadcrumbPrefixCls}-item`, {
[`${breadcrumbPrefixCls}-item-link`]: item?.path || item?.href,
[`${breadcrumbPrefixCls}-item-${colorModel || 'emphasize'}-model-current`]: isLast,
[`${breadcrumbPrefixCls}-item-${colorModel || 'emphasize'}-model`]: !isLast,
})
}

const MIN_ITEM = 3 // 加上more只显示3个元素的时候,末尾元素开启省略号

const isLastItem = (index: number, items: IBreadcrumbItem[]) => {
return index === items?.length - 1
}
const getSeparator = (index: number, items: IBreadcrumbItem[]) => {
if (isLastItem(index, items)) {
return null
} else {
return <span className={breadcrumbSeparatorClass}>{separator}</span>
}
}

const getMoreIconContent = (items: IBreadcrumbItem[]) => {
const MoreItems = () => {
const getMoreIconContent = useCallback(
(items: IBreadcrumbItem[]) => {
const MoreItems = () => {
return (
<div className={breadcrumbMorePanelClass}>
{items.map((item, index) => {
return (
<BreadcrumbItem
key={`breadcrumb--more-item-${index}`}
index={index}
item={item}
colorModel={colorModel}
separator={separator}
onItemClick={onItemClick}
isLast={isLastItem(index, items)}
isTooltip={true}
/>
)
})}
</div>
)
}
return (
<div className={breadcrumbMorePanelClass}>
{items.map((item, index) => {
return (
<BreadcrumbItem
key={`breadcrumb--more-item-${index}`}
index={index}
item={{ ...item, className: getBreadcrumbItemClass(item, false) }}
separator={getSeparator(index, items)}
/>
)
})}
</div>
<>
<Tooltip
popperClassName={breadcrumbPopperClass}
arrow={false}
tip={<MoreItems />}
trigger="hover"
placement="bottomLeft"
>
<Icon type="more" />
</Tooltip>
</>
)
}
return (
<>
<Tooltip
popperClassName={breadcrumbPopperClass}
arrow={false}
tip={<MoreItems />}
trigger="hover"
placement="bottomLeft"
>
<Icon type="more" />
</Tooltip>
</>
)
}
},
[breadcrumbMorePanelClass, breadcrumbPopperClass, colorModel, onItemClick, separator],
)

const getElementWidth = (ref: React.RefObject<HTMLDivElement>) => {
if (ref.current) {
Expand All @@ -92,37 +87,40 @@ const Breadcrumb = (props: IBreadcrumbProps): FunctionComponentElement<IBreadcru
}
}

const getItemsConfig = (widthArr: IItemsWidth[], breadcrumbWidth: number) => {
const difference = getElementWidth(breadcrumbRef) - breadcrumbWidth!
if (difference < 0) {
const number = -difference
const removeItem = widthArr?.reduce(
(acc, cur, ind) => {
if (ind > 0 && ind < widthArr.length - 1) {
if (acc.width < number + getElementWidth(breadcrumbHideIconRef)) {
acc.width += cur.width
acc.index = ind
return acc
const getItemsConfig = useCallback(
(widthArr: IItemsWidth[], breadcrumbWidth: number) => {
const difference = getElementWidth(breadcrumbRef) - breadcrumbWidth!
if (difference < 0) {
const number = -difference
const removeItem = widthArr?.reduce(
(acc, cur, ind) => {
if (ind > 0 && ind < widthArr.length - 1) {
if (acc.width < number + getElementWidth(breadcrumbHideIconRef)) {
acc.width += cur.width
acc.index = ind
return acc
} else {
return acc
}
} else {
return acc
}
} else {
return acc
}
},
{ width: 0, index: 0 },
)
if (removeItem.index > 0 && removeItem.index < items.length - 1) {
const newItemsConfig = cloneDeep(items)
newItemsConfig.splice(1, removeItem.index, {
title: getMoreIconContent(cloneDeep(items).splice(1, removeItem.index)),
})
setItemsConfig(newItemsConfig)
},
{ width: 0, index: 0 },
)
if (removeItem.index > 0 && removeItem.index < items.length - 1) {
const newItemsConfig = cloneDeep(items)
newItemsConfig.splice(1, removeItem.index, {
title: getMoreIconContent(cloneDeep(items).splice(1, removeItem.index)),
})
setItemsConfig(newItemsConfig)
}
} else {
setItemsConfig(items)
}
} else {
setItemsConfig(items)
}
}
},
[getMoreIconContent, items],
)

useEffect(() => {
const isMore = itemsConfig?.some((item: any) => {
Expand All @@ -146,20 +144,17 @@ const Breadcrumb = (props: IBreadcrumbProps): FunctionComponentElement<IBreadcru
getItemsConfig(itemsArray, breadcrumbWidth)
}
}
}, [])
}, [getItemsConfig])

useEffect(() => {
const fn = () => {
getItemsConfig(itemsArray, breadcrumbWidth)
}
if (itemsArray && breadcrumbWidth) {
window.addEventListener('resize', () => {
getItemsConfig(itemsArray, breadcrumbWidth)
})
window.addEventListener('resize', fn)
}

return () =>
window.removeEventListener('resize', () => {
getItemsConfig(itemsArray, breadcrumbWidth as any)
})
}, [itemsArray, breadcrumbWidth])
return () => window.removeEventListener('resize', fn)
}, [itemsArray, breadcrumbWidth, getItemsConfig])

return (
<>
Expand All @@ -169,13 +164,12 @@ const Breadcrumb = (props: IBreadcrumbProps): FunctionComponentElement<IBreadcru
return (
<BreadcrumbItem
key={`breadcrumb-item-${index}`}
item={{
...item,
className: getBreadcrumbItemClass(item, isLastItem(index, itemsConfig)),
}}
item={item}
onItemClick={onItemClick}
index={index}
separator={getSeparator(index, itemsConfig)}
separator={separator}
colorModel={colorModel}
isLast={isLastItem(index, itemsConfig)}
openEllipsis={isLastItem(index, itemsConfig) ? openEllipsis : false}
/>
)
Expand Down
31 changes: 25 additions & 6 deletions components/breadcrumb/breadcrumbItem.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
import React, { useContext, FC } from 'react'
import React, { useContext, FC, memo } from 'react'
import { IBreadcrumbItems } from './interface'
import Dropdown from '../dropdown'
import Icon from '../icon'
import devWarning from '../_utils/devwarning'
import ConfigContext from '../config-provider/ConfigContext'
import classNames from 'classnames'

const BreadcrumbItem: FC<IBreadcrumbItems> = (props) => {
const { getPrefixCls, prefixCls } = useContext(ConfigContext)
const { item, index, separator, openEllipsis, onItemClick } = props
const { title, className, dropdownProps, href, path, icon } = item
const { item, index, separator, openEllipsis, isLast, isTooltip, colorModel, onItemClick } = props
const { title, dropdownProps, href, path, icon, className } = item
const breadcrumbPrefixCls = getPrefixCls!(prefixCls, 'breadcrumb')
const breadcrumbSeparatorClass = classNames(`${breadcrumbPrefixCls}-item-separator`)
const itemTextCls = `${breadcrumbPrefixCls}-item-text`
const itemIconCls = `${breadcrumbPrefixCls}-item-icon`

const getBreadcrumbItemClass = () => {
return classNames(className, `${breadcrumbPrefixCls}-item`, {
[`${breadcrumbPrefixCls}-item-link`]: path || href,
[`${breadcrumbPrefixCls}-item-${colorModel || 'emphasize'}-model-current`]: isTooltip ? false : isLast,
[`${breadcrumbPrefixCls}-item-${colorModel || 'emphasize'}-model`]: isTooltip ? true : !isLast,
})
}

const handleItemClick = () => {
if (href && !path) {
window.open(href, '_self')
Expand All @@ -22,9 +33,17 @@ const BreadcrumbItem: FC<IBreadcrumbItems> = (props) => {
}
onItemClick && onItemClick(item, index)
}

const getSeparator = () => {
if (isLast) {
return null
} else {
return <span className={breadcrumbSeparatorClass}>{separator}</span>
}
}
return (
<>
<div className={className} onClick={handleItemClick}>
<div className={getBreadcrumbItemClass()} onClick={handleItemClick}>
{icon && <div className={itemIconCls}>{icon}</div>}
<div
className={itemTextCls}
Expand All @@ -38,9 +57,9 @@ const BreadcrumbItem: FC<IBreadcrumbItems> = (props) => {
<Icon type="arrow-down" />
</Dropdown>
)}
{separator}
{getSeparator()}
</div>
</>
)
}
export default BreadcrumbItem
export default memo(BreadcrumbItem)
3 changes: 3 additions & 0 deletions components/breadcrumb/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export interface IBreadcrumbItems {
index: number
separator?: ReactNode
openEllipsis?: boolean
colorModel?: ColorModelType // 颜色模式,默认是 'emphasize'
isLast?: boolean
isTooltip?: boolean
onItemClick?: (item: IBreadcrumbItem, index: number) => void // 点击面包屑导航的回调函数
}

Expand Down

0 comments on commit 391acfe

Please sign in to comment.