feat: anchor add customTitle slot #6447

pull/6514/head
tangjinzhou 2023-04-21 21:29:20 +08:00
parent 8932aff13f
commit bccd9204e8
11 changed files with 238 additions and 83 deletions

View File

@ -1,6 +1,5 @@
import type { CSSProperties, ExtractPropTypes, PropType } from 'vue';
import {
watch,
defineComponent,
nextTick,
onBeforeUnmount,
@ -19,18 +18,11 @@ import getScroll from '../_util/getScroll';
import useConfigInject from '../config-provider/hooks/useConfigInject';
import useProvideAnchor from './context';
import useStyle from './style';
import type { AnchorLinkProps } from './AnchorLink';
import type { AnchorLinkItemProps } from './AnchorLink';
import AnchorLink from './AnchorLink';
import type { Key } from '../_util/type';
import PropTypes from '../_util/vue-types';
import devWarning from '../vc-util/devWarning';
export interface AnchorLinkItemProps extends AnchorLinkProps {
key: Key;
class?: String;
style?: CSSProperties;
children?: AnchorLinkItemProps[];
}
import { arrayType } from '../_util/type';
export type AnchorDirection = 'vertical' | 'horizontal';
@ -76,10 +68,7 @@ export const anchorProps = () => ({
wrapperStyle: { type: Object as PropType<CSSProperties>, default: undefined as CSSProperties },
getCurrentAnchor: Function as PropType<(activeLink: string) => string>,
targetOffset: Number,
items: {
type: Array as PropType<AnchorLinkItemProps[]>,
default: undefined as AnchorLinkItemProps[],
},
items: arrayType<AnchorLinkItemProps[]>(),
direction: PropTypes.oneOf(['vertical', 'horizontal'] as AnchorDirection[]).def('vertical'),
onChange: Function as PropType<(currentActiveLink: string) => void>,
onClick: Function as PropType<(e: MouseEvent, link: { title: any; href: string }) => void>,
@ -105,7 +94,7 @@ export default defineComponent({
if (process.env.NODE_ENV !== 'production') {
devWarning(
typeof slots.default !== 'function',
props.items && typeof slots.default !== 'function',
'Anchor',
'`Anchor children` is deprecated. Please use `items` instead.',
);
@ -274,17 +263,25 @@ export default defineComponent({
updateInk();
});
watch([anchorDirection, getCurrentAnchor, state.links, activeLink], () => {
updateInk();
});
const createNestedLink = (options?: AnchorLinkItemProps[]) =>
Array.isArray(options)
? options.map(item => (
<AnchorLink {...item} key={item.key}>
{anchorDirection.value === 'vertical' ? createNestedLink(item.children) : null}
? options.map(option => {
const { children, key, href, target, class: cls, style, title } = option;
return (
<AnchorLink
key={key}
href={href}
target={target}
class={cls}
style={style}
title={title}
customTitleProps={option}
v-slots={{ customTitle: slots.customTitle }}
>
{anchorDirection.value === 'vertical' ? createNestedLink(children) : null}
</AnchorLink>
))
);
})
: null;
const [wrapSSR, hashId] = useStyle(prefixCls);

View File

@ -1,26 +1,40 @@
import type { ExtractPropTypes } from 'vue';
import { defineComponent, nextTick, onBeforeUnmount, onMounted, watch } from 'vue';
import PropTypes from '../_util/vue-types';
import { getPropsSlot, initDefaultProps } from '../_util/props-util';
import { initDefaultProps } from '../_util/props-util';
import classNames from '../_util/classNames';
import useConfigInject from '../config-provider/hooks/useConfigInject';
import { useInjectAnchor } from './context';
import type { Key, VueNode } from '../_util/type';
import { objectType, anyType } from '../_util/type';
import type { CSSProperties } from '../_util/cssinjs/hooks/useStyleRegister';
export const anchorLinkProps = () => ({
prefixCls: String,
href: String,
title: PropTypes.any,
title: anyType<VueNode | ((item: any) => VueNode)>(),
target: String,
/* private use */
customTitleProps: objectType<AnchorLinkItemProps>(),
});
export interface AnchorLinkItemProps {
key: Key;
class?: string;
style?: CSSProperties;
href?: string;
target?: string;
children?: AnchorLinkItemProps[];
title?: VueNode | ((item: AnchorLinkItemProps) => VueNode);
}
export type AnchorLinkProps = Partial<ExtractPropTypes<ReturnType<typeof anchorLinkProps>>>;
export default defineComponent({
compatConfig: { MODE: 3 },
name: 'AAnchorLink',
inheritAttrs: false,
props: initDefaultProps(anchorLinkProps(), { href: '#' }),
slots: ['title'],
setup(props, { slots }) {
slots: ['title', 'customTitle'],
setup(props, { slots, attrs }) {
let mergedTitle = null;
const {
handleClick: contextHandleClick,
@ -56,27 +70,30 @@ export default defineComponent({
});
return () => {
const { href, target } = props;
const { href, target, title = slots.title, customTitleProps = {} } = props;
const pre = prefixCls.value;
const title = getPropsSlot(slots, props, 'title');
mergedTitle = title;
mergedTitle = typeof title === 'function' ? title(customTitleProps) : title;
const active = activeLink.value === href;
const wrapperClassName = classNames(`${pre}-link`, {
const wrapperClassName = classNames(
`${pre}-link`,
{
[`${pre}-link-active`]: active,
});
},
attrs.class,
);
const titleClassName = classNames(`${pre}-link-title`, {
[`${pre}-link-title-active`]: active,
});
return (
<div class={wrapperClassName}>
<div {...attrs} class={wrapperClassName}>
<a
class={titleClassName}
href={href}
title={typeof title === 'string' ? title : ''}
title={typeof mergedTitle === 'string' ? mergedTitle : ''}
target={target}
onClick={handleClick}
>
{title}
{slots.customTitle ? slots.customTitle(customTitleProps) : mergedTitle}
</a>
{slots.default?.()}
</div>

View File

@ -21,7 +21,7 @@ The simplest usage.
{
key: 'part-1',
href: '#part-1',
title: 'Part 1',
title: () => h('span', { style: 'color: red' }, 'Part 1'),
},
{
key: 'part-2',
@ -36,3 +36,19 @@ The simplest usage.
]"
/>
</template>
<script lang="ts">
import { defineComponent, h } from 'vue';
export default defineComponent({
setup() {
const onChange = (link: string) => {
console.log('Anchor:OnChange', link);
};
return {
onChange,
h,
};
},
});
</script>

View File

@ -16,14 +16,39 @@ Customize the anchor highlight.
</docs>
<template>
<a-anchor :affix="false" :get-current-anchor="getCurrentAnchor">
<a-anchor-link href="#components-anchor-demo-basic" title="Basic demo" />
<a-anchor-link href="#components-anchor-demo-static" title="Static demo" />
<a-anchor-link href="#API" title="API">
<a-anchor-link href="#Anchor-Props" title="Anchor Props" />
<a-anchor-link href="#Link-Props" title="Link Props" />
</a-anchor-link>
</a-anchor>
<a-anchor
:affix="false"
:get-current-anchor="getCurrentAnchor"
:items="[
{
key: '1',
href: '#components-anchor-demo-basic',
title: 'Basic demo',
},
{
key: '2',
href: '#components-anchor-demo-static',
title: 'Static demo',
},
{
key: '3',
href: '#api',
title: 'API',
children: [
{
key: '4',
href: '#anchor-props',
title: 'Anchor Props',
},
{
key: '5',
href: '#link-props',
title: 'Link Props',
},
],
},
]"
></a-anchor>
</template>
<script lang="ts">

View File

@ -16,14 +16,39 @@ Listening for anchor link change.
</docs>
<template>
<a-anchor :affix="false" @change="onChange">
<a-anchor-link href="#components-anchor-demo-basic" title="Basic demo" />
<a-anchor-link href="#components-anchor-demo-static" title="Static demo" />
<a-anchor-link href="#API" title="API">
<a-anchor-link href="#Anchor-Props" title="Anchor Props" />
<a-anchor-link href="#Link-Props" title="Link Props" />
</a-anchor-link>
</a-anchor>
<a-anchor
:affix="false"
:items="[
{
key: '1',
href: '#components-anchor-demo-basic',
title: 'Basic demo',
},
{
key: '2',
href: '#components-anchor-demo-static',
title: 'Static demo',
},
{
key: '3',
href: '#api',
title: 'API',
children: [
{
key: '4',
href: '#anchor-props',
title: 'Anchor Props',
},
{
key: '5',
href: '#link-props',
title: 'Link Props',
},
],
},
]"
@change="onChange"
></a-anchor>
</template>
<script lang="ts">

View File

@ -16,14 +16,39 @@ Clicking on an anchor does not record history.
</docs>
<template>
<a-anchor :affix="false" @click="handleClick">
<a-anchor-link href="#components-anchor-demo-basic" title="Basic demo" />
<a-anchor-link href="#components-anchor-demo-static" title="Static demo" />
<a-anchor-link href="#API" title="API">
<a-anchor-link href="#Anchor-Props" title="Anchor Props" />
<a-anchor-link href="#Link-Props" title="Link Props" />
</a-anchor-link>
</a-anchor>
<a-anchor
:affix="false"
:items="[
{
key: '1',
href: '#components-anchor-demo-basic',
title: 'Basic demo',
},
{
key: '2',
href: '#components-anchor-demo-static',
title: 'Static demo',
},
{
key: '3',
href: '#api',
title: 'API',
children: [
{
key: '4',
href: '#anchor-props',
title: 'Anchor Props',
},
{
key: '5',
href: '#link-props',
title: 'Link Props',
},
],
},
]"
@click="handleClick"
></a-anchor>
</template>
<script lang="ts">

View File

@ -16,12 +16,36 @@ Do not change state when page is scrolling.
</docs>
<template>
<a-anchor :affix="false">
<a-anchor-link href="#components-anchor-demo-basic" title="Basic demo" />
<a-anchor-link href="#components-anchor-demo-static" title="Static demo" />
<a-anchor-link href="#API" title="API">
<a-anchor-link href="#Anchor-Props" title="Anchor Props" />
<a-anchor-link href="#Link-Props" title="Link Props" />
</a-anchor-link>
</a-anchor>
<a-anchor
:affix="false"
:items="[
{
key: '1',
href: '#components-anchor-demo-basic',
title: 'Basic demo',
},
{
key: '2',
href: '#components-anchor-demo-static',
title: 'Static demo',
},
{
key: '3',
href: '#api',
title: 'API',
children: [
{
key: '4',
href: '#anchor-props',
title: 'Anchor Props',
},
{
key: '5',
href: '#link-props',
title: 'Link Props',
},
],
},
]"
></a-anchor>
</template>

View File

@ -16,14 +16,38 @@ Anchor target scroll to screen center.
</docs>
<template>
<a-anchor :target-offset="targetOffset">
<a-anchor-link href="#components-anchor-demo-basic" title="Basic demo" />
<a-anchor-link href="#components-anchor-demo-static" title="Static demo" />
<a-anchor-link href="#API" title="API">
<a-anchor-link href="#Anchor-Props" title="Anchor Props" />
<a-anchor-link href="#Link-Props" title="Link Props" />
</a-anchor-link>
</a-anchor>
<a-anchor
:target-offset="targetOffset"
:items="[
{
key: '1',
href: '#components-anchor-demo-basic',
title: 'Basic demo',
},
{
key: '2',
href: '#components-anchor-demo-static',
title: 'Static demo',
},
{
key: '3',
href: '#api',
title: 'API',
children: [
{
key: '4',
href: '#anchor-props',
title: 'Anchor Props',
},
{
key: '5',
href: '#link-props',
title: 'Link Props',
},
],
},
]"
></a-anchor>
</template>
<script lang="ts">

View File

@ -1,7 +1,7 @@
---
category: Components
type: Navigation
cols: 2
cols: 1
title: Anchor
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*TBTSR4PyVmkAAAAAAAAAAAAADrJ8AQ/original
coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*JGb3RIzyOCkAAAAAAAAAAAAADrJ8AQ/original
@ -31,6 +31,7 @@ For displaying anchor hyperlinks on page and jumping between them.
| wrapperStyle | The style of the container | object | - | |
| items | Data configuration option content, support nesting through children | { key, href, title, target, children }\[] [see](#anchoritem) | - | 4.0 |
| direction | Set Anchor direction | `vertical` \| `horizontal` | `vertical` | 4.0 |
| customTitle | custom option title by slot | v-slot="AnchorItem" | - | 4.0 |
### AnchorItem

View File

@ -1,6 +1,6 @@
import type { App, Plugin } from 'vue';
import type { AnchorProps } from './Anchor';
import type { AnchorLinkProps } from './AnchorLink';
import type { AnchorLinkProps, AnchorLinkItemProps } from './AnchorLink';
import Anchor from './Anchor';
import AnchorLink from './AnchorLink';
@ -13,7 +13,7 @@ Anchor.install = function (app: App) {
return app;
};
export type { AnchorLinkProps, AnchorProps };
export type { AnchorLinkProps, AnchorProps, AnchorLinkItemProps };
export { AnchorLink, AnchorLink as Link };
export default Anchor as typeof Anchor &

View File

@ -1,7 +1,7 @@
---
category: Components
subtitle: 锚点
cols: 2
cols: 1
type: 导航
title: Anchor
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*TBTSR4PyVmkAAAAAAAAAAAAADrJ8AQ/original
@ -32,6 +32,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*JGb3RIzyOCkAAA
| wrapperStyle | 容器样式 | object | - | |
| items | 数据化配置选项内容,支持通过 children 嵌套 | { key, href, title, target, children }\[] [具体见](#anchoritem) | - | 4.0 |
| direction | 设置导航方向 | `vertical` \| `horizontal` | `vertical` | 4.0 |
| customTitle | 使用插槽自定义选项 title | v-slot="AnchorItem" | - | 4.0 |
### AnchorItem