From 287a8d0c4edb38c61a73695265d0ef7519469cf0 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Thu, 26 Jan 2023 16:52:06 +0800 Subject: [PATCH] refactor: Breadcrumb --- components/_util/type.ts | 4 + components/breadcrumb/Breadcrumb.tsx | 49 ++++----- components/breadcrumb/BreadcrumbItem.tsx | 21 ++-- components/breadcrumb/style/index.less | 64 ------------ components/breadcrumb/style/index.ts | 127 +++++++++++++++++++++++ components/breadcrumb/style/index.tsx | 6 -- components/breadcrumb/style/rtl.less | 29 ------ components/dropdown/props.ts | 3 + components/style.ts | 2 +- 9 files changed, 173 insertions(+), 132 deletions(-) delete mode 100644 components/breadcrumb/style/index.less create mode 100644 components/breadcrumb/style/index.ts delete mode 100644 components/breadcrumb/style/index.tsx delete mode 100644 components/breadcrumb/style/rtl.less diff --git a/components/_util/type.ts b/components/_util/type.ts index 1a18abf5a..51c2a03da 100644 --- a/components/_util/type.ts +++ b/components/_util/type.ts @@ -46,3 +46,7 @@ export type MaybeRef = T | Ref; export function eventType() { return { type: [Function, Array] as PropType }; } + +export function objectType(defaultVal?: any) { + return { type: Object as PropType, default: defaultVal as T }; +} diff --git a/components/breadcrumb/Breadcrumb.tsx b/components/breadcrumb/Breadcrumb.tsx index bd53f8e6d..e7fa68bbb 100644 --- a/components/breadcrumb/Breadcrumb.tsx +++ b/components/breadcrumb/Breadcrumb.tsx @@ -3,11 +3,12 @@ import { cloneVNode, defineComponent } from 'vue'; import PropTypes from '../_util/vue-types'; import { flattenChildren, getPropsSlot } from '../_util/props-util'; import warning from '../_util/warning'; +import type { BreadcrumbItemProps } from './BreadcrumbItem'; import BreadcrumbItem from './BreadcrumbItem'; import Menu from '../menu'; import type { VueNode } from '../_util/type'; import useConfigInject from '../_util/hooks/useConfigInject'; - +import useStyle from './style'; export interface Route { path: string; breadcrumbName: string; @@ -54,11 +55,12 @@ function defaultItemRender(opt: { export default defineComponent({ compatConfig: { MODE: 3 }, name: 'ABreadcrumb', + inheritAttrs: false, props: breadcrumbProps(), slots: ['separator', 'itemRender'], - setup(props, { slots }) { + setup(props, { slots, attrs }) { const { prefixCls, direction } = useConfigInject('breadcrumb', props); - + const [wrapSSR, hashId] = useStyle(prefixCls); const getPath = (path: string, params: unknown) => { path = (path || '').replace(/^\//, ''); Object.keys(params).forEach(key => { @@ -94,27 +96,25 @@ export default defineComponent({ let overlay = null; if (route.children && route.children.length) { overlay = ( - - {route.children.map(child => ( - - {itemRender({ - route: child, - params, - routes, - paths: addChildPath(tempPaths, child.path, params), - })} - - ))} - + ({ + key: child.path || child.breadcrumbName, + label: itemRender({ + route: child, + params, + routes, + paths: addChildPath(tempPaths, child.path, params), + }), + }))} + > ); } - + const itemProps: BreadcrumbItemProps = { separator }; + if (overlay) { + itemProps.overlay = overlay; + } return ( - + {itemRender({ route, params, routes, paths: tempPaths })} ); @@ -152,11 +152,12 @@ export default defineComponent({ const breadcrumbClassName = { [prefixCls.value]: true, [`${prefixCls.value}-rtl`]: direction.value === 'rtl', + [hashId.value]: true, }; - return ( - , ); }; }, diff --git a/components/breadcrumb/BreadcrumbItem.tsx b/components/breadcrumb/BreadcrumbItem.tsx index 01ac344c4..9570257e1 100644 --- a/components/breadcrumb/BreadcrumbItem.tsx +++ b/components/breadcrumb/BreadcrumbItem.tsx @@ -1,18 +1,21 @@ -import type { CSSProperties, ExtractPropTypes, PropType } from 'vue'; +import type { CSSProperties, ExtractPropTypes } from 'vue'; import { defineComponent } from 'vue'; import PropTypes from '../_util/vue-types'; import { getPropsSlot } from '../_util/props-util'; +import type { DropdownProps } from '../dropdown/dropdown'; import Dropdown from '../dropdown/dropdown'; import DownOutlined from '@ant-design/icons-vue/DownOutlined'; import useConfigInject from '../_util/hooks/useConfigInject'; import type { MouseEventHandler } from '../_util/EventInterface'; +import { eventType, objectType } from '../_util/type'; export const breadcrumbItemProps = () => ({ prefixCls: String, href: String, separator: PropTypes.any, + dropdownProps: objectType(), overlay: PropTypes.any, - onClick: Function as PropType, + onClick: eventType(), }); export type BreadcrumbItemProps = Partial>>; @@ -24,7 +27,7 @@ export default defineComponent({ props: breadcrumbItemProps(), // emits: ['click'], slots: ['separator', 'overlay'], - setup(props, { slots, attrs }) { + setup(props, { slots, attrs, emit }) { const { prefixCls } = useConfigInject('breadcrumb', props); /** * if overlay is have @@ -34,7 +37,7 @@ export default defineComponent({ const overlay = getPropsSlot(slots, props, 'overlay'); if (overlay) { return ( - + {breadcrumbItem} @@ -44,7 +47,9 @@ export default defineComponent({ } return breadcrumbItem; }; - + const handleClick = (e: MouseEvent) => { + emit('click', e); + }; return () => { const separator = getPropsSlot(slots, props, 'separator') ?? '/'; const children = getPropsSlot(slots, props); @@ -52,20 +57,20 @@ export default defineComponent({ let link: JSX.Element; if (props.href !== undefined) { link = ( - + {children} ); } else { link = ( - + {children} ); } // wrap to dropDown link = renderBreadcrumbNode(link, prefixCls.value); - if (children) { + if (children !== undefined && children !== null) { return (
  • {link} diff --git a/components/breadcrumb/style/index.less b/components/breadcrumb/style/index.less deleted file mode 100644 index 79dee8aec..000000000 --- a/components/breadcrumb/style/index.less +++ /dev/null @@ -1,64 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@breadcrumb-prefix-cls: ~'@{ant-prefix}-breadcrumb'; - -.@{breadcrumb-prefix-cls} { - .reset-component(); - - color: @breadcrumb-base-color; - font-size: @breadcrumb-font-size; - - .@{iconfont-css-prefix} { - font-size: @breadcrumb-icon-font-size; - } - - ol { - display: flex; - flex-wrap: wrap; - margin: 0; - padding: 0; - list-style: none; - } - - a { - color: @breadcrumb-link-color; - transition: color 0.3s; - - &:hover { - color: @breadcrumb-link-color-hover; - } - } - - li:last-child { - color: @breadcrumb-last-item-color; - - a { - color: @breadcrumb-last-item-color; - } - } - - li:last-child &-separator { - display: none; - } - - &-separator { - margin: @breadcrumb-separator-margin; - color: @breadcrumb-separator-color; - } - - &-link { - > .@{iconfont-css-prefix} + span, - > .@{iconfont-css-prefix} + a { - margin-left: 4px; - } - } - - &-overlay-link { - > .@{iconfont-css-prefix} { - margin-left: 4px; - } - } -} - -@import './rtl'; diff --git a/components/breadcrumb/style/index.ts b/components/breadcrumb/style/index.ts new file mode 100644 index 000000000..1fe152f21 --- /dev/null +++ b/components/breadcrumb/style/index.ts @@ -0,0 +1,127 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import type { FullToken, GenerateStyle } from '../../theme/internal'; +import { genComponentStyleHook, mergeToken } from '../../theme/internal'; +import { genFocusStyle, resetComponent } from '../../_style'; + +interface BreadcrumbToken extends FullToken<'Breadcrumb'> { + breadcrumbBaseColor: string; + breadcrumbFontSize: number; + breadcrumbIconFontSize: number; + breadcrumbLinkColor: string; + breadcrumbLinkColorHover: string; + breadcrumbLastItemColor: string; + breadcrumbSeparatorMargin: number; + breadcrumbSeparatorColor: string; +} + +const genBreadcrumbStyle: GenerateStyle = token => { + const { componentCls, iconCls } = token; + + return { + [componentCls]: { + ...resetComponent(token), + color: token.breadcrumbBaseColor, + fontSize: token.breadcrumbFontSize, + + [iconCls]: { + fontSize: token.breadcrumbIconFontSize, + }, + + ol: { + display: 'flex', + flexWrap: 'wrap', + margin: 0, + padding: 0, + listStyle: 'none', + }, + + a: { + color: token.breadcrumbLinkColor, + transition: `color ${token.motionDurationMid}`, + padding: `0 ${token.paddingXXS}px`, + borderRadius: token.borderRadiusSM, + height: token.lineHeight * token.fontSize, + display: 'inline-block', + marginInline: -token.marginXXS, + + '&:hover': { + color: token.breadcrumbLinkColorHover, + backgroundColor: token.colorBgTextHover, + }, + + ...genFocusStyle(token), + }, + + [`li:last-child`]: { + color: token.breadcrumbLastItemColor, + + [`& > ${componentCls}-separator`]: { + display: 'none', + }, + }, + + [`${componentCls}-separator`]: { + marginInline: token.breadcrumbSeparatorMargin, + color: token.breadcrumbSeparatorColor, + }, + + [`${componentCls}-link`]: { + [` + > ${iconCls} + span, + > ${iconCls} + a + `]: { + marginInlineStart: token.marginXXS, + }, + }, + + [`${componentCls}-overlay-link`]: { + borderRadius: token.borderRadiusSM, + height: token.lineHeight * token.fontSize, + display: 'inline-block', + padding: `0 ${token.paddingXXS}px`, + marginInline: -token.marginXXS, + + [`> ${iconCls}`]: { + marginInlineStart: token.marginXXS, + fontSize: token.fontSizeIcon, + }, + + '&:hover': { + color: token.breadcrumbLinkColorHover, + backgroundColor: token.colorBgTextHover, + + a: { + color: token.breadcrumbLinkColorHover, + }, + }, + + a: { + '&:hover': { + backgroundColor: 'transparent', + }, + }, + }, + + // rtl style + [`&${token.componentCls}-rtl`]: { + direction: 'rtl', + }, + }, + }; +}; + +// ============================== Export ============================== +export default genComponentStyleHook('Breadcrumb', token => { + const BreadcrumbToken = mergeToken(token, { + breadcrumbBaseColor: token.colorTextDescription, + breadcrumbFontSize: token.fontSize, + breadcrumbIconFontSize: token.fontSize, + breadcrumbLinkColor: token.colorTextDescription, + breadcrumbLinkColorHover: token.colorText, + breadcrumbLastItemColor: token.colorText, + breadcrumbSeparatorMargin: token.marginXS, + breadcrumbSeparatorColor: token.colorTextDescription, + }); + + return [genBreadcrumbStyle(BreadcrumbToken)]; +}); diff --git a/components/breadcrumb/style/index.tsx b/components/breadcrumb/style/index.tsx deleted file mode 100644 index 3d7084daa..000000000 --- a/components/breadcrumb/style/index.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import '../../style/index.less'; -import './index.less'; - -// style dependencies -import '../../menu/style'; -import '../../dropdown/style'; diff --git a/components/breadcrumb/style/rtl.less b/components/breadcrumb/style/rtl.less deleted file mode 100644 index c1141c993..000000000 --- a/components/breadcrumb/style/rtl.less +++ /dev/null @@ -1,29 +0,0 @@ -.@{breadcrumb-prefix-cls} { - &-rtl { - .clearfix(); - direction: rtl; - - > span { - float: right; - } - } - - &-link { - > .@{iconfont-css-prefix} + span, - > .@{iconfont-css-prefix} + a { - .@{breadcrumb-prefix-cls}-rtl & { - margin-right: 4px; - margin-left: 0; - } - } - } - - &-overlay-link { - > .@{iconfont-css-prefix} { - .@{breadcrumb-prefix-cls}-rtl & { - margin-right: 4px; - margin-left: 0; - } - } - } -} diff --git a/components/dropdown/props.ts b/components/dropdown/props.ts index f983f2a42..7cccba769 100644 --- a/components/dropdown/props.ts +++ b/components/dropdown/props.ts @@ -3,6 +3,8 @@ import PropTypes from '../_util/vue-types'; import buttonTypes from '../button/buttonTypes'; import type { MouseEventHandler } from '../_util/EventInterface'; +import type { MenuProps } from '../menu'; +import { objectType } from '../_util/type'; export type Align = { points?: [string, string]; @@ -30,6 +32,7 @@ const dropdownProps = () => ({ trigger: { type: [Array, String] as PropType, }, + menu: objectType(), overlay: PropTypes.any, visible: { type: Boolean, default: undefined }, disabled: { type: Boolean, default: undefined }, diff --git a/components/style.ts b/components/style.ts index 611c5554f..045959d81 100644 --- a/components/style.ts +++ b/components/style.ts @@ -33,7 +33,7 @@ import './modal/style'; // import './alert/style'; import './time-picker/style'; import './steps/style'; -import './breadcrumb/style'; +// import './breadcrumb/style'; import './calendar/style'; import './date-picker/style'; import './slider/style';