Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(anchor): child level #2816

Merged
merged 3 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/anchor/__tests__/anchor-link.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ describe('AnchorItem', () => {
it('should render props correctly', async () => {
const provide = {
active: '',
handleScrollTo: vi.fn(),
registerLink: vi.fn(),
unregisterLink: vi.fn(),
handleLinkClick: vi.fn(),
Expand Down Expand Up @@ -56,7 +55,6 @@ describe('AnchorItem', () => {
expect(a.element.textContent).toEqual(props.title);
expect(provide.registerLink).toBeCalledWith(props.href);
a.trigger('click');
expect(provide.handleScrollTo).toBeCalledWith(props.href);
expect(provide.handleLinkClick).toBeCalledWith({ ...omit(props, 'target'), e: expect.any(MouseEvent) });
wrapper.setData({
href: '#test-b',
Expand Down Expand Up @@ -107,7 +105,7 @@ describe('AnchorItem', () => {
});
expect(wrapper.find('a').attributes('title')).toEqual('');
expect(wrapper.find('#title').element.outerHTML).toMatchSnapshot();
expect(wrapper.find('#default').element.outerHTML).toMatchSnapshot();
expect(wrapper.find('#default').element?.outerHTML).toMatchSnapshot();
});
});
});
6 changes: 3 additions & 3 deletions src/anchor/__tests__/index.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,10 @@ describe('Anchor', () => {
}).findComponent(Anchor);
const links = wrapper.findAllComponents(AnchorItem);
links.at(0).find('a').trigger('click');
expect(onChange).toBeCalledTimes(1);
expect(onChange).toBeCalledWith('#test-a', '');
links.at(1).find('a').trigger('click');
expect(onChange).toBeCalledTimes(2);
expect(onChange).toBeCalledWith('#test-b', '');
links.at(1).find('a').trigger('click');
expect(onChange).toBeCalledTimes(3);
expect(onChange).toBeCalledWith('#test-b', '#test-a');
});
});
Expand Down
32 changes: 32 additions & 0 deletions src/anchor/_example/customize-highlight.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<template>
<t-space>
<div class="flex: 1"></div>
<t-anchor :bounds="150" :getCurrentAnchor="getCurrentAnchor">
<t-anchor-item href="#基础锚点" title="基础锚点"></t-anchor-item>
<t-anchor-item href="#多级锚点" title="多级锚点"></t-anchor-item>
<t-anchor-item href="#指定容器锚点" title="指定容器锚点"></t-anchor-item>
<t-anchor-item href="#特定交互锚点" title="特定交互锚点"></t-anchor-item>
<t-anchor-item href="#尺寸" title="尺寸">
<t-anchor-item href="#size-1" title="尺寸1">
<div id="default">this is children content</div>
<div>sdadasas</div>
</t-anchor-item>
<t-anchor-item href="#size-2" title="尺寸2">
<t-anchor-item href="#size-3" title="尺寸1"></t-anchor-item>
</t-anchor-item>
</t-anchor-item>
</t-anchor>
</t-space>
</template>
<script>
export default {
data() {
return {
getCurrentAnchor(value) {
console.log('value1', value);
return value || '#多级锚点';
},
};
},
};
</script>
5 changes: 4 additions & 1 deletion src/anchor/_example/multiple.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
<t-anchor-item href="#多级锚点" title="多级锚点"></t-anchor-item>
<t-anchor-item href="#指定容器锚点" title="指定容器锚点"></t-anchor-item>
<t-anchor-item href="#特定交互锚点" title="特定交互锚点"></t-anchor-item>
<t-anchor-item href="#尺寸" title="尺寸"></t-anchor-item>
<t-anchor-item href="#尺寸" title="尺寸">
<t-anchor-item href="#size-1" title="尺寸1"></t-anchor-item>
<t-anchor-item href="#size-2" title="尺寸2"></t-anchor-item>
</t-anchor-item>
</t-anchor>
</t-space>
</template>
Expand Down
2 changes: 1 addition & 1 deletion src/anchor/anchor-item-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

/**
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
* updated at 2021-11-19 10:44:26
* */

import { TdAnchorItemProps } from '../anchor/type';
Expand All @@ -20,6 +19,7 @@ export default {
type: String as PropType<TdAnchorItemProps['target']>,
default: '_self' as TdAnchorItemProps['target'],
validator(val: TdAnchorItemProps['target']): boolean {
if (!val) return true;
return ['_self', '_blank', '_parent', '_top'].includes(val);
},
},
Expand Down
52 changes: 44 additions & 8 deletions src/anchor/anchor-item.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import Vue, { VueConstructor } from 'vue';
import { ScopedSlotReturnValue } from 'vue/types/vnode';
import { isEmpty } from 'lodash';
import { VNode } from 'vue/types/umd';
import { ANCHOR_SHARP_REGEXP } from './utils';
import props from './anchor-item-props';
import { getClassPrefixMixins } from '../config-provider/config-receiver';
Expand All @@ -10,7 +12,6 @@ const classPrefixMixins = getClassPrefixMixins('anchor');
export interface Anchor extends Vue {
tAnchor: {
active: string;
handleScrollTo(target: string): void;
registerLink(href: string): void;
unregisterLink(href: string): void;
handleLinkClick(link: { href: string; title: string; e: MouseEvent }): void;
Expand All @@ -21,6 +22,10 @@ export default mixins(Vue as VueConstructor<Anchor>, classPrefixMixins).extend({
name: 'TAnchorItem',
props: {
...props,
_level: {
type: Number,
default: 0,
},
href: {
type: String,
required: true,
Expand Down Expand Up @@ -57,7 +62,6 @@ export default mixins(Vue as VueConstructor<Anchor>, classPrefixMixins).extend({
},
handleClick(e: MouseEvent): void {
const { href, tAnchor, title } = this;
tAnchor.handleScrollTo(href);
tAnchor.handleLinkClick({
href,
title: typeof title === 'string' ? title : undefined,
Expand All @@ -83,27 +87,59 @@ export default mixins(Vue as VueConstructor<Anchor>, classPrefixMixins).extend({
return titleVal;
},
},
render() {
render(h) {
const {
href, target, $scopedSlots, tAnchor,
} = this;
const { default: children, title: titleSlot } = $scopedSlots;

const title = this.renderTitle();
const titleAttr = typeof title === 'string' ? title : null;
const isActive = tAnchor.active === href;

const wrapperClass = {
[`${this.componentName}__item`]: true,
[this.commonStatusClassName.active]: isActive,
};
const titleClass = {
[`${this.componentName}__item-link`]: true,
};

const newLevel = this.$props._level + 1;

// 这样处理是为了兼容普通节点和组件
const h5Tags: VNode[] = [];
const anchorItems: VNode[] = [];
if (children) {
children(null).forEach((child) => {
if (isEmpty(child.componentOptions)) {
h5Tags.push(h(child.tag, child.data, child.children));
} else {
anchorItems.push(
h(
child.componentOptions?.Ctor,
{
props: {
...child.componentOptions?.propsData,
_level: newLevel,
},
},
child?.componentOptions?.children,
),
);
}
});
}

return (
<div class={wrapperClass}>
<a href={href} title={titleAttr} class={titleClass} target={target} onClick={this.handleClick}>
{titleSlot ? titleSlot(null) : title}
</a>
{children && children(null)}
<div style={{ display: 'contents' }}>
<div class={wrapperClass} style={{ '--level': newLevel }}>
<a href={href} title={titleAttr} class={titleClass} target={target} onClick={this.handleClick}>
{titleSlot ? titleSlot(null) : title}
</a>
{h5Tags}
</div>
{anchorItems}
</div>
);
},
Expand Down
1 change: 0 additions & 1 deletion src/anchor/anchor-target-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

/**
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
* updated at 2021-11-19 10:44:26
* */

export default {
Expand Down
7 changes: 4 additions & 3 deletions src/anchor/anchor.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ name | type | default | description | required
-- | -- | -- | -- | --
affixProps | Object | - | Typescript:`AffixProps`,[Affix API Documents](./affix?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-vue/tree/develop/src/anchor/type.ts) | N
bounds | Number | 5 | \- | N
container | String / Function | () => (() => window) | Typescript:`ScrollContainer` | N
container | String / Function | () => (() => window) | Typescript:`ScrollContainer`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
cursor | Slot / Function | - | Typescript:`TNode`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
size | String | medium | options:small/medium/large。Typescript:`SizeEnum`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
getCurrentAnchor | Function | - | Custom Highlighted Anchor Points。Typescript:`(activeLink: string) => string` | N
size | String | medium | options: small/medium/large。Typescript:`SizeEnum`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
targetOffset | Number | 0 | \- | N
onChange | Function | | Typescript:`(currentLink: string, prevLink: string) => void`<br/> | N
onClick | Function | | Typescript:`(link: { href: string; title: string; e: MouseEvent }) => void`<br/> | N
Expand All @@ -26,7 +27,7 @@ click | `(link: { href: string; title: string; e: MouseEvent })` | \-
name | type | default | description | required
-- | -- | -- | -- | --
href | String | - | required | Y
target | String | _self | options_self/_blank/_parent/_top | N
target | String | _self | options: _self/_blank/_parent/_top | N
title | String / Slot / Function | '' | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N

### AnchorTarget Props
Expand Down
3 changes: 2 additions & 1 deletion src/anchor/anchor.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
-- | -- | -- | -- | --
affixProps | Object | - | 透传 Affix 组件属性,即让 Anchor 组件支持所有 Affix 组件特性。TS 类型:`AffixProps`,[Affix API Documents](./affix?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-vue/tree/develop/src/anchor/type.ts) | N
bounds | Number | 5 | 锚点区域边界 | N
container | String / Function | () => (() => window) | 指定滚动的容器。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body。TS 类型:`ScrollContainer` | N
container | String / Function | () => (() => window) | 指定滚动的容器。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body。TS 类型:`ScrollContainer`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
cursor | Slot / Function | - | 用于自定义选中项左侧游标。TS 类型:`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
getCurrentAnchor | Function | - | 自定义高亮的锚点 。TS 类型:`(activeLink: string) => string` | N
size | String | medium | 组件尺寸,small(120px),medium(200px),large(320px)。可选项:small/medium/large。TS 类型:`SizeEnum`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
targetOffset | Number | 0 | 锚点滚动偏移量 | N
onChange | Function | | TS 类型:`(currentLink: string, prevLink: string) => void`<br/>锚点改变时触发 | N
Expand Down
21 changes: 14 additions & 7 deletions src/anchor/anchor.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Vue, { VueConstructor } from 'vue';
import { ScopedSlotReturnValue } from 'vue/types/vnode';
import { isFunction } from 'lodash';
import { ANCHOR_SHARP_REGEXP, ANCHOR_CONTAINER, getOffsetTop } from './utils';
import {
on, off, getScroll, scrollTo, getScrollContainer,
Expand Down Expand Up @@ -118,6 +119,7 @@ export default mixins(Vue as VueConstructor<Anchor>, classPrefixMixins).extend({
this.activeLineStyle = false;
return;
}

const { offsetTop: top, offsetHeight: height } = ele;
this.activeLineStyle = {
top: `${top}px`,
Expand All @@ -143,9 +145,11 @@ export default mixins(Vue as VueConstructor<Anchor>, classPrefixMixins).extend({
*/
handleLinkClick(link: { href: string; title: string; e: MouseEvent }): void {
this.$emit('click', link);
if (this.onClick) {
this.onClick(link);
}
this.onClick?.(link);

const { getCurrentAnchor } = this.$props;
const newHref = isFunction(getCurrentAnchor) ? getCurrentAnchor(link.href) : link.href;
this.handleScrollTo(newHref);
},
/**
* 滚动到指定锚点
Expand Down Expand Up @@ -202,11 +206,14 @@ export default mixins(Vue as VueConstructor<Anchor>, classPrefixMixins).extend({
},

async mounted() {
const { active } = this;
this.getScrollContainer();
if (active) {
await Vue.nextTick();
this.handleScrollTo(active);
const { getCurrentAnchor } = this.$props;

if (isFunction(getCurrentAnchor)) {
this.setCurrentActiveLink(getCurrentAnchor(this.active));
} else {
const href = window?.location.hash;
this.setCurrentActiveLink(decodeURIComponent(href));
}
},

Expand Down
8 changes: 6 additions & 2 deletions src/anchor/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

/**
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
* updated at 2021-11-19 10:44:26
* */

import { TdAnchorProps } from './type';
Expand All @@ -21,17 +20,22 @@ export default {
/** 指定滚动的容器。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body */
container: {
type: [String, Function] as PropType<TdAnchorProps['container']>,
default: () => (() => window),
default: () => () => window,
},
/** 用于自定义选中项左侧游标 */
cursor: {
type: Function as PropType<TdAnchorProps['cursor']>,
},
/** 自定义高亮的锚点 */
getCurrentAnchor: {
type: Function as PropType<TdAnchorProps['getCurrentAnchor']>,
},
/** 组件尺寸,small(120px),medium(200px),large(320px) */
size: {
type: String as PropType<TdAnchorProps['size']>,
default: 'medium' as TdAnchorProps['size'],
validator(val: TdAnchorProps['size']): boolean {
if (!val) return true;
return ['small', 'medium', 'large'].includes(val);
},
},
Expand Down
10 changes: 7 additions & 3 deletions src/anchor/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ export interface TdAnchorProps {
* 锚点被点击时触发
*/
onClick?: (link: { href: string; title: string; e: MouseEvent }) => void;
};
/**
* 自定义高亮的锚点
*/
getCurrentAnchor?: (activeLink: string) => string;
}

export interface TdAnchorTargetProps {
/**
Expand All @@ -58,7 +62,7 @@ export interface TdAnchorTargetProps {
* @default div
*/
tag?: string;
};
}

export interface TdAnchorItemProps {
/**
Expand All @@ -76,4 +80,4 @@ export interface TdAnchorItemProps {
* @default ''
*/
title?: string | TNode;
};
}
Loading
Loading