refactor: Breadcrumb
parent
1e4e3cb3b4
commit
287a8d0c4e
|
@ -46,3 +46,7 @@ export type MaybeRef<T> = T | Ref<T>;
|
||||||
export function eventType<T>() {
|
export function eventType<T>() {
|
||||||
return { type: [Function, Array] as PropType<T | 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 PropTypes from '../_util/vue-types';
|
||||||
import { flattenChildren, getPropsSlot } from '../_util/props-util';
|
import { flattenChildren, getPropsSlot } from '../_util/props-util';
|
||||||
import warning from '../_util/warning';
|
import warning from '../_util/warning';
|
||||||
|
import type { BreadcrumbItemProps } from './BreadcrumbItem';
|
||||||
import BreadcrumbItem from './BreadcrumbItem';
|
import BreadcrumbItem from './BreadcrumbItem';
|
||||||
import Menu from '../menu';
|
import Menu from '../menu';
|
||||||
import type { VueNode } from '../_util/type';
|
import type { VueNode } from '../_util/type';
|
||||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||||
|
import useStyle from './style';
|
||||||
export interface Route {
|
export interface Route {
|
||||||
path: string;
|
path: string;
|
||||||
breadcrumbName: string;
|
breadcrumbName: string;
|
||||||
|
@ -54,11 +55,12 @@ function defaultItemRender(opt: {
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
compatConfig: { MODE: 3 },
|
compatConfig: { MODE: 3 },
|
||||||
name: 'ABreadcrumb',
|
name: 'ABreadcrumb',
|
||||||
|
inheritAttrs: false,
|
||||||
props: breadcrumbProps(),
|
props: breadcrumbProps(),
|
||||||
slots: ['separator', 'itemRender'],
|
slots: ['separator', 'itemRender'],
|
||||||
setup(props, { slots }) {
|
setup(props, { slots, attrs }) {
|
||||||
const { prefixCls, direction } = useConfigInject('breadcrumb', props);
|
const { prefixCls, direction } = useConfigInject('breadcrumb', props);
|
||||||
|
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||||
const getPath = (path: string, params: unknown) => {
|
const getPath = (path: string, params: unknown) => {
|
||||||
path = (path || '').replace(/^\//, '');
|
path = (path || '').replace(/^\//, '');
|
||||||
Object.keys(params).forEach(key => {
|
Object.keys(params).forEach(key => {
|
||||||
|
@ -94,27 +96,25 @@ export default defineComponent({
|
||||||
let overlay = null;
|
let overlay = null;
|
||||||
if (route.children && route.children.length) {
|
if (route.children && route.children.length) {
|
||||||
overlay = (
|
overlay = (
|
||||||
<Menu>
|
<Menu
|
||||||
{route.children.map(child => (
|
items={route.children.map(child => ({
|
||||||
<Menu.Item key={child.path || child.breadcrumbName}>
|
key: child.path || child.breadcrumbName,
|
||||||
{itemRender({
|
label: itemRender({
|
||||||
route: child,
|
route: child,
|
||||||
params,
|
params,
|
||||||
routes,
|
routes,
|
||||||
paths: addChildPath(tempPaths, child.path, params),
|
paths: addChildPath(tempPaths, child.path, params),
|
||||||
})}
|
}),
|
||||||
</Menu.Item>
|
}))}
|
||||||
))}
|
></Menu>
|
||||||
</Menu>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
const itemProps: BreadcrumbItemProps = { separator };
|
||||||
|
if (overlay) {
|
||||||
|
itemProps.overlay = overlay;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<BreadcrumbItem
|
<BreadcrumbItem {...itemProps} key={path || route.breadcrumbName}>
|
||||||
overlay={overlay}
|
|
||||||
separator={separator}
|
|
||||||
key={path || route.breadcrumbName}
|
|
||||||
>
|
|
||||||
{itemRender({ route, params, routes, paths: tempPaths })}
|
{itemRender({ route, params, routes, paths: tempPaths })}
|
||||||
</BreadcrumbItem>
|
</BreadcrumbItem>
|
||||||
);
|
);
|
||||||
|
@ -152,11 +152,12 @@ export default defineComponent({
|
||||||
const breadcrumbClassName = {
|
const breadcrumbClassName = {
|
||||||
[prefixCls.value]: true,
|
[prefixCls.value]: true,
|
||||||
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
||||||
|
[hashId.value]: true,
|
||||||
};
|
};
|
||||||
return (
|
return wrapSSR(
|
||||||
<nav class={breadcrumbClassName}>
|
<nav {...attrs} class={breadcrumbClassName}>
|
||||||
<ol>{crumbs}</ol>
|
<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 { defineComponent } from 'vue';
|
||||||
import PropTypes from '../_util/vue-types';
|
import PropTypes from '../_util/vue-types';
|
||||||
import { getPropsSlot } from '../_util/props-util';
|
import { getPropsSlot } from '../_util/props-util';
|
||||||
|
import type { DropdownProps } from '../dropdown/dropdown';
|
||||||
import Dropdown from '../dropdown/dropdown';
|
import Dropdown from '../dropdown/dropdown';
|
||||||
import DownOutlined from '@ant-design/icons-vue/DownOutlined';
|
import DownOutlined from '@ant-design/icons-vue/DownOutlined';
|
||||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||||
import type { MouseEventHandler } from '../_util/EventInterface';
|
import type { MouseEventHandler } from '../_util/EventInterface';
|
||||||
|
import { eventType, objectType } from '../_util/type';
|
||||||
|
|
||||||
export const breadcrumbItemProps = () => ({
|
export const breadcrumbItemProps = () => ({
|
||||||
prefixCls: String,
|
prefixCls: String,
|
||||||
href: String,
|
href: String,
|
||||||
separator: PropTypes.any,
|
separator: PropTypes.any,
|
||||||
|
dropdownProps: objectType<DropdownProps>(),
|
||||||
overlay: PropTypes.any,
|
overlay: PropTypes.any,
|
||||||
onClick: Function as PropType<MouseEventHandler>,
|
onClick: eventType<MouseEventHandler>(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type BreadcrumbItemProps = Partial<ExtractPropTypes<ReturnType<typeof breadcrumbItemProps>>>;
|
export type BreadcrumbItemProps = Partial<ExtractPropTypes<ReturnType<typeof breadcrumbItemProps>>>;
|
||||||
|
@ -24,7 +27,7 @@ export default defineComponent({
|
||||||
props: breadcrumbItemProps(),
|
props: breadcrumbItemProps(),
|
||||||
// emits: ['click'],
|
// emits: ['click'],
|
||||||
slots: ['separator', 'overlay'],
|
slots: ['separator', 'overlay'],
|
||||||
setup(props, { slots, attrs }) {
|
setup(props, { slots, attrs, emit }) {
|
||||||
const { prefixCls } = useConfigInject('breadcrumb', props);
|
const { prefixCls } = useConfigInject('breadcrumb', props);
|
||||||
/**
|
/**
|
||||||
* if overlay is have
|
* if overlay is have
|
||||||
|
@ -34,7 +37,7 @@ export default defineComponent({
|
||||||
const overlay = getPropsSlot(slots, props, 'overlay');
|
const overlay = getPropsSlot(slots, props, 'overlay');
|
||||||
if (overlay) {
|
if (overlay) {
|
||||||
return (
|
return (
|
||||||
<Dropdown overlay={overlay} placement="bottom">
|
<Dropdown {...props.dropdownProps} overlay={overlay} placement="bottom">
|
||||||
<span class={`${prefixCls}-overlay-link`}>
|
<span class={`${prefixCls}-overlay-link`}>
|
||||||
{breadcrumbItem}
|
{breadcrumbItem}
|
||||||
<DownOutlined />
|
<DownOutlined />
|
||||||
|
@ -44,7 +47,9 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
return breadcrumbItem;
|
return breadcrumbItem;
|
||||||
};
|
};
|
||||||
|
const handleClick = (e: MouseEvent) => {
|
||||||
|
emit('click', e);
|
||||||
|
};
|
||||||
return () => {
|
return () => {
|
||||||
const separator = getPropsSlot(slots, props, 'separator') ?? '/';
|
const separator = getPropsSlot(slots, props, 'separator') ?? '/';
|
||||||
const children = getPropsSlot(slots, props);
|
const children = getPropsSlot(slots, props);
|
||||||
|
@ -52,20 +57,20 @@ export default defineComponent({
|
||||||
let link: JSX.Element;
|
let link: JSX.Element;
|
||||||
if (props.href !== undefined) {
|
if (props.href !== undefined) {
|
||||||
link = (
|
link = (
|
||||||
<a class={`${prefixCls.value}-link`} onClick={props.onClick} {...restAttrs}>
|
<a class={`${prefixCls.value}-link`} onClick={handleClick} {...restAttrs}>
|
||||||
{children}
|
{children}
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
link = (
|
link = (
|
||||||
<span class={`${prefixCls.value}-link`} onClick={props.onClick} {...restAttrs}>
|
<span class={`${prefixCls.value}-link`} onClick={handleClick} {...restAttrs}>
|
||||||
{children}
|
{children}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// wrap to dropDown
|
// wrap to dropDown
|
||||||
link = renderBreadcrumbNode(link, prefixCls.value);
|
link = renderBreadcrumbNode(link, prefixCls.value);
|
||||||
if (children) {
|
if (children !== undefined && children !== null) {
|
||||||
return (
|
return (
|
||||||
<li class={cls} style={style as CSSProperties}>
|
<li class={cls} style={style as CSSProperties}>
|
||||||
{link}
|
{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 buttonTypes from '../button/buttonTypes';
|
||||||
import type { MouseEventHandler } from '../_util/EventInterface';
|
import type { MouseEventHandler } from '../_util/EventInterface';
|
||||||
|
import type { MenuProps } from '../menu';
|
||||||
|
import { objectType } from '../_util/type';
|
||||||
|
|
||||||
export type Align = {
|
export type Align = {
|
||||||
points?: [string, string];
|
points?: [string, string];
|
||||||
|
@ -30,6 +32,7 @@ const dropdownProps = () => ({
|
||||||
trigger: {
|
trigger: {
|
||||||
type: [Array, String] as PropType<Trigger[] | Trigger>,
|
type: [Array, String] as PropType<Trigger[] | Trigger>,
|
||||||
},
|
},
|
||||||
|
menu: objectType<MenuProps>(),
|
||||||
overlay: PropTypes.any,
|
overlay: PropTypes.any,
|
||||||
visible: { type: Boolean, default: undefined },
|
visible: { type: Boolean, default: undefined },
|
||||||
disabled: { type: Boolean, default: undefined },
|
disabled: { type: Boolean, default: undefined },
|
||||||
|
|
|
@ -33,7 +33,7 @@ import './modal/style';
|
||||||
// import './alert/style';
|
// import './alert/style';
|
||||||
import './time-picker/style';
|
import './time-picker/style';
|
||||||
import './steps/style';
|
import './steps/style';
|
||||||
import './breadcrumb/style';
|
// import './breadcrumb/style';
|
||||||
import './calendar/style';
|
import './calendar/style';
|
||||||
import './date-picker/style';
|
import './date-picker/style';
|
||||||
import './slider/style';
|
import './slider/style';
|
||||||
|
|
Loading…
Reference in New Issue