Skip to content

Commit bccd920

Browse files
committed
feat: anchor add customTitle slot #6447
1 parent 8932aff commit bccd920

11 files changed

+238
-83
lines changed

components/anchor/Anchor.tsx

+21-24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { CSSProperties, ExtractPropTypes, PropType } from 'vue';
22
import {
3-
watch,
43
defineComponent,
54
nextTick,
65
onBeforeUnmount,
@@ -19,18 +18,11 @@ import getScroll from '../_util/getScroll';
1918
import useConfigInject from '../config-provider/hooks/useConfigInject';
2019
import useProvideAnchor from './context';
2120
import useStyle from './style';
22-
import type { AnchorLinkProps } from './AnchorLink';
21+
import type { AnchorLinkItemProps } from './AnchorLink';
2322
import AnchorLink from './AnchorLink';
24-
import type { Key } from '../_util/type';
2523
import PropTypes from '../_util/vue-types';
2624
import devWarning from '../vc-util/devWarning';
27-
28-
export interface AnchorLinkItemProps extends AnchorLinkProps {
29-
key: Key;
30-
class?: String;
31-
style?: CSSProperties;
32-
children?: AnchorLinkItemProps[];
33-
}
25+
import { arrayType } from '../_util/type';
3426

3527
export type AnchorDirection = 'vertical' | 'horizontal';
3628

@@ -76,10 +68,7 @@ export const anchorProps = () => ({
7668
wrapperStyle: { type: Object as PropType<CSSProperties>, default: undefined as CSSProperties },
7769
getCurrentAnchor: Function as PropType<(activeLink: string) => string>,
7870
targetOffset: Number,
79-
items: {
80-
type: Array as PropType<AnchorLinkItemProps[]>,
81-
default: undefined as AnchorLinkItemProps[],
82-
},
71+
items: arrayType<AnchorLinkItemProps[]>(),
8372
direction: PropTypes.oneOf(['vertical', 'horizontal'] as AnchorDirection[]).def('vertical'),
8473
onChange: Function as PropType<(currentActiveLink: string) => void>,
8574
onClick: Function as PropType<(e: MouseEvent, link: { title: any; href: string }) => void>,
@@ -105,7 +94,7 @@ export default defineComponent({
10594

10695
if (process.env.NODE_ENV !== 'production') {
10796
devWarning(
108-
typeof slots.default !== 'function',
97+
props.items && typeof slots.default !== 'function',
10998
'Anchor',
11099
'`Anchor children` is deprecated. Please use `items` instead.',
111100
);
@@ -274,17 +263,25 @@ export default defineComponent({
274263
updateInk();
275264
});
276265

277-
watch([anchorDirection, getCurrentAnchor, state.links, activeLink], () => {
278-
updateInk();
279-
});
280-
281266
const createNestedLink = (options?: AnchorLinkItemProps[]) =>
282267
Array.isArray(options)
283-
? options.map(item => (
284-
<AnchorLink {...item} key={item.key}>
285-
{anchorDirection.value === 'vertical' ? createNestedLink(item.children) : null}
286-
</AnchorLink>
287-
))
268+
? options.map(option => {
269+
const { children, key, href, target, class: cls, style, title } = option;
270+
return (
271+
<AnchorLink
272+
key={key}
273+
href={href}
274+
target={target}
275+
class={cls}
276+
style={style}
277+
title={title}
278+
customTitleProps={option}
279+
v-slots={{ customTitle: slots.customTitle }}
280+
>
281+
{anchorDirection.value === 'vertical' ? createNestedLink(children) : null}
282+
</AnchorLink>
283+
);
284+
})
288285
: null;
289286

290287
const [wrapSSR, hashId] = useStyle(prefixCls);

components/anchor/AnchorLink.tsx

+31-14
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,40 @@
11
import type { ExtractPropTypes } from 'vue';
22
import { defineComponent, nextTick, onBeforeUnmount, onMounted, watch } from 'vue';
3-
import PropTypes from '../_util/vue-types';
4-
import { getPropsSlot, initDefaultProps } from '../_util/props-util';
3+
import { initDefaultProps } from '../_util/props-util';
54
import classNames from '../_util/classNames';
65
import useConfigInject from '../config-provider/hooks/useConfigInject';
76
import { useInjectAnchor } from './context';
7+
import type { Key, VueNode } from '../_util/type';
8+
import { objectType, anyType } from '../_util/type';
9+
import type { CSSProperties } from '../_util/cssinjs/hooks/useStyleRegister';
810

911
export const anchorLinkProps = () => ({
1012
prefixCls: String,
1113
href: String,
12-
title: PropTypes.any,
14+
title: anyType<VueNode | ((item: any) => VueNode)>(),
1315
target: String,
16+
/* private use */
17+
customTitleProps: objectType<AnchorLinkItemProps>(),
1418
});
19+
export interface AnchorLinkItemProps {
20+
key: Key;
21+
class?: string;
22+
style?: CSSProperties;
23+
href?: string;
24+
target?: string;
25+
children?: AnchorLinkItemProps[];
26+
title?: VueNode | ((item: AnchorLinkItemProps) => VueNode);
27+
}
1528

1629
export type AnchorLinkProps = Partial<ExtractPropTypes<ReturnType<typeof anchorLinkProps>>>;
1730

1831
export default defineComponent({
1932
compatConfig: { MODE: 3 },
2033
name: 'AAnchorLink',
34+
inheritAttrs: false,
2135
props: initDefaultProps(anchorLinkProps(), { href: '#' }),
22-
slots: ['title'],
23-
setup(props, { slots }) {
36+
slots: ['title', 'customTitle'],
37+
setup(props, { slots, attrs }) {
2438
let mergedTitle = null;
2539
const {
2640
handleClick: contextHandleClick,
@@ -56,27 +70,30 @@ export default defineComponent({
5670
});
5771

5872
return () => {
59-
const { href, target } = props;
73+
const { href, target, title = slots.title, customTitleProps = {} } = props;
6074
const pre = prefixCls.value;
61-
const title = getPropsSlot(slots, props, 'title');
62-
mergedTitle = title;
75+
mergedTitle = typeof title === 'function' ? title(customTitleProps) : title;
6376
const active = activeLink.value === href;
64-
const wrapperClassName = classNames(`${pre}-link`, {
65-
[`${pre}-link-active`]: active,
66-
});
77+
const wrapperClassName = classNames(
78+
`${pre}-link`,
79+
{
80+
[`${pre}-link-active`]: active,
81+
},
82+
attrs.class,
83+
);
6784
const titleClassName = classNames(`${pre}-link-title`, {
6885
[`${pre}-link-title-active`]: active,
6986
});
7087
return (
71-
<div class={wrapperClassName}>
88+
<div {...attrs} class={wrapperClassName}>
7289
<a
7390
class={titleClassName}
7491
href={href}
75-
title={typeof title === 'string' ? title : ''}
92+
title={typeof mergedTitle === 'string' ? mergedTitle : ''}
7693
target={target}
7794
onClick={handleClick}
7895
>
79-
{title}
96+
{slots.customTitle ? slots.customTitle(customTitleProps) : mergedTitle}
8097
</a>
8198
{slots.default?.()}
8299
</div>

components/anchor/demo/basic.vue

+17-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ The simplest usage.
2121
{
2222
key: 'part-1',
2323
href: '#part-1',
24-
title: 'Part 1',
24+
title: () => h('span', { style: 'color: red' }, 'Part 1'),
2525
},
2626
{
2727
key: 'part-2',
@@ -36,3 +36,19 @@ The simplest usage.
3636
]"
3737
/>
3838
</template>
39+
40+
<script lang="ts">
41+
import { defineComponent, h } from 'vue';
42+
43+
export default defineComponent({
44+
setup() {
45+
const onChange = (link: string) => {
46+
console.log('Anchor:OnChange', link);
47+
};
48+
return {
49+
onChange,
50+
h,
51+
};
52+
},
53+
});
54+
</script>

components/anchor/demo/customizeHighlight.vue

+33-8
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,39 @@ Customize the anchor highlight.
1616
</docs>
1717

1818
<template>
19-
<a-anchor :affix="false" :get-current-anchor="getCurrentAnchor">
20-
<a-anchor-link href="#components-anchor-demo-basic" title="Basic demo" />
21-
<a-anchor-link href="#components-anchor-demo-static" title="Static demo" />
22-
<a-anchor-link href="#API" title="API">
23-
<a-anchor-link href="#Anchor-Props" title="Anchor Props" />
24-
<a-anchor-link href="#Link-Props" title="Link Props" />
25-
</a-anchor-link>
26-
</a-anchor>
19+
<a-anchor
20+
:affix="false"
21+
:get-current-anchor="getCurrentAnchor"
22+
:items="[
23+
{
24+
key: '1',
25+
href: '#components-anchor-demo-basic',
26+
title: 'Basic demo',
27+
},
28+
{
29+
key: '2',
30+
href: '#components-anchor-demo-static',
31+
title: 'Static demo',
32+
},
33+
{
34+
key: '3',
35+
href: '#api',
36+
title: 'API',
37+
children: [
38+
{
39+
key: '4',
40+
href: '#anchor-props',
41+
title: 'Anchor Props',
42+
},
43+
{
44+
key: '5',
45+
href: '#link-props',
46+
title: 'Link Props',
47+
},
48+
],
49+
},
50+
]"
51+
></a-anchor>
2752
</template>
2853

2954
<script lang="ts">

components/anchor/demo/onChange.vue

+33-8
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,39 @@ Listening for anchor link change.
1616
</docs>
1717

1818
<template>
19-
<a-anchor :affix="false" @change="onChange">
20-
<a-anchor-link href="#components-anchor-demo-basic" title="Basic demo" />
21-
<a-anchor-link href="#components-anchor-demo-static" title="Static demo" />
22-
<a-anchor-link href="#API" title="API">
23-
<a-anchor-link href="#Anchor-Props" title="Anchor Props" />
24-
<a-anchor-link href="#Link-Props" title="Link Props" />
25-
</a-anchor-link>
26-
</a-anchor>
19+
<a-anchor
20+
:affix="false"
21+
:items="[
22+
{
23+
key: '1',
24+
href: '#components-anchor-demo-basic',
25+
title: 'Basic demo',
26+
},
27+
{
28+
key: '2',
29+
href: '#components-anchor-demo-static',
30+
title: 'Static demo',
31+
},
32+
{
33+
key: '3',
34+
href: '#api',
35+
title: 'API',
36+
children: [
37+
{
38+
key: '4',
39+
href: '#anchor-props',
40+
title: 'Anchor Props',
41+
},
42+
{
43+
key: '5',
44+
href: '#link-props',
45+
title: 'Link Props',
46+
},
47+
],
48+
},
49+
]"
50+
@change="onChange"
51+
></a-anchor>
2752
</template>
2853

2954
<script lang="ts">

components/anchor/demo/onClick.vue

+33-8
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,39 @@ Clicking on an anchor does not record history.
1616
</docs>
1717

1818
<template>
19-
<a-anchor :affix="false" @click="handleClick">
20-
<a-anchor-link href="#components-anchor-demo-basic" title="Basic demo" />
21-
<a-anchor-link href="#components-anchor-demo-static" title="Static demo" />
22-
<a-anchor-link href="#API" title="API">
23-
<a-anchor-link href="#Anchor-Props" title="Anchor Props" />
24-
<a-anchor-link href="#Link-Props" title="Link Props" />
25-
</a-anchor-link>
26-
</a-anchor>
19+
<a-anchor
20+
:affix="false"
21+
:items="[
22+
{
23+
key: '1',
24+
href: '#components-anchor-demo-basic',
25+
title: 'Basic demo',
26+
},
27+
{
28+
key: '2',
29+
href: '#components-anchor-demo-static',
30+
title: 'Static demo',
31+
},
32+
{
33+
key: '3',
34+
href: '#api',
35+
title: 'API',
36+
children: [
37+
{
38+
key: '4',
39+
href: '#anchor-props',
40+
title: 'Anchor Props',
41+
},
42+
{
43+
key: '5',
44+
href: '#link-props',
45+
title: 'Link Props',
46+
},
47+
],
48+
},
49+
]"
50+
@click="handleClick"
51+
></a-anchor>
2752
</template>
2853

2954
<script lang="ts">

components/anchor/demo/static.vue

+32-8
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,36 @@ Do not change state when page is scrolling.
1616
</docs>
1717

1818
<template>
19-
<a-anchor :affix="false">
20-
<a-anchor-link href="#components-anchor-demo-basic" title="Basic demo" />
21-
<a-anchor-link href="#components-anchor-demo-static" title="Static demo" />
22-
<a-anchor-link href="#API" title="API">
23-
<a-anchor-link href="#Anchor-Props" title="Anchor Props" />
24-
<a-anchor-link href="#Link-Props" title="Link Props" />
25-
</a-anchor-link>
26-
</a-anchor>
19+
<a-anchor
20+
:affix="false"
21+
:items="[
22+
{
23+
key: '1',
24+
href: '#components-anchor-demo-basic',
25+
title: 'Basic demo',
26+
},
27+
{
28+
key: '2',
29+
href: '#components-anchor-demo-static',
30+
title: 'Static demo',
31+
},
32+
{
33+
key: '3',
34+
href: '#api',
35+
title: 'API',
36+
children: [
37+
{
38+
key: '4',
39+
href: '#anchor-props',
40+
title: 'Anchor Props',
41+
},
42+
{
43+
key: '5',
44+
href: '#link-props',
45+
title: 'Link Props',
46+
},
47+
],
48+
},
49+
]"
50+
></a-anchor>
2751
</template>

0 commit comments

Comments
 (0)