refactor: Breadcrumb
parent
1e4e3cb3b4
commit
287a8d0c4e
|
@ -46,3 +46,7 @@ export type MaybeRef<T> = T | Ref<T>;
|
|||
export function eventType<T>() {
|
||||
return { type: [Function, Array] as PropType<T | T[]> };
|
||||
}
|
||||
|
||||
export function objectType<T>(defaultVal?: any) {
|
||||
return { type: Object as PropType<T>, default: defaultVal as T };
|
||||
}
|
||||
|
|
|
@ -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 = (
|
||||
<Menu>
|
||||
{route.children.map(child => (
|
||||
<Menu.Item key={child.path || child.breadcrumbName}>
|
||||
{itemRender({
|
||||
route: child,
|
||||
params,
|
||||
routes,
|
||||
paths: addChildPath(tempPaths, child.path, params),
|
||||
})}
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
<Menu
|
||||
items={route.children.map(child => ({
|
||||
key: child.path || child.breadcrumbName,
|
||||
label: itemRender({
|
||||
route: child,
|
||||
params,
|
||||
routes,
|
||||
paths: addChildPath(tempPaths, child.path, params),
|
||||
}),
|
||||
}))}
|
||||
></Menu>
|
||||
);
|
||||
}
|
||||
|
||||
const itemProps: BreadcrumbItemProps = { separator };
|
||||
if (overlay) {
|
||||
itemProps.overlay = overlay;
|
||||
}
|
||||
return (
|
||||
<BreadcrumbItem
|
||||
overlay={overlay}
|
||||
separator={separator}
|
||||
key={path || route.breadcrumbName}
|
||||
>
|
||||
<BreadcrumbItem {...itemProps} key={path || route.breadcrumbName}>
|
||||
{itemRender({ route, params, routes, paths: tempPaths })}
|
||||
</BreadcrumbItem>
|
||||
);
|
||||
|
@ -152,11 +152,12 @@ export default defineComponent({
|
|||
const breadcrumbClassName = {
|
||||
[prefixCls.value]: true,
|
||||
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
||||
[hashId.value]: true,
|
||||
};
|
||||
return (
|
||||
<nav class={breadcrumbClassName}>
|
||||
return wrapSSR(
|
||||
<nav {...attrs} class={breadcrumbClassName}>
|
||||
<ol>{crumbs}</ol>
|
||||
</nav>
|
||||
</nav>,
|
||||
);
|
||||
};
|
||||
},
|
||||
|
|
|
@ -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<DropdownProps>(),
|
||||
overlay: PropTypes.any,
|
||||
onClick: Function as PropType<MouseEventHandler>,
|
||||
onClick: eventType<MouseEventHandler>(),
|
||||
});
|
||||
|
||||
export type BreadcrumbItemProps = Partial<ExtractPropTypes<ReturnType<typeof breadcrumbItemProps>>>;
|
||||
|
@ -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 (
|
||||
<Dropdown overlay={overlay} placement="bottom">
|
||||
<Dropdown {...props.dropdownProps} overlay={overlay} placement="bottom">
|
||||
<span class={`${prefixCls}-overlay-link`}>
|
||||
{breadcrumbItem}
|
||||
<DownOutlined />
|
||||
|
@ -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 = (
|
||||
<a class={`${prefixCls.value}-link`} onClick={props.onClick} {...restAttrs}>
|
||||
<a class={`${prefixCls.value}-link`} onClick={handleClick} {...restAttrs}>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
} else {
|
||||
link = (
|
||||
<span class={`${prefixCls.value}-link`} onClick={props.onClick} {...restAttrs}>
|
||||
<span class={`${prefixCls.value}-link`} onClick={handleClick} {...restAttrs}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
// wrap to dropDown
|
||||
link = renderBreadcrumbNode(link, prefixCls.value);
|
||||
if (children) {
|
||||
if (children !== undefined && children !== null) {
|
||||
return (
|
||||
<li class={cls} style={style as CSSProperties}>
|
||||
{link}
|
||||
|
|
|
@ -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';
|
|
@ -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<BreadcrumbToken, CSSObject> = 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<BreadcrumbToken>(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)];
|
||||
});
|
|
@ -1,6 +0,0 @@
|
|||
import '../../style/index.less';
|
||||
import './index.less';
|
||||
|
||||
// style dependencies
|
||||
import '../../menu/style';
|
||||
import '../../dropdown/style';
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<Trigger[] | Trigger>,
|
||||
},
|
||||
menu: objectType<MenuProps>(),
|
||||
overlay: PropTypes.any,
|
||||
visible: { type: Boolean, default: undefined },
|
||||
disabled: { type: Boolean, default: undefined },
|
||||
|
|
|
@ -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';
|
||||
|
|
Loading…
Reference in New Issue