diff --git a/components/anchor/Anchor.tsx b/components/anchor/Anchor.tsx index e7c3261c8..965764764 100644 --- a/components/anchor/Anchor.tsx +++ b/components/anchor/Anchor.tsx @@ -4,10 +4,8 @@ import { onBeforeUnmount, onMounted, onUpdated, - provide, reactive, ref, - getCurrentInstance, ExtractPropTypes, computed, } from 'vue'; @@ -18,6 +16,7 @@ import Affix from '../affix'; import scrollTo from '../_util/scrollTo'; import getScroll from '../_util/getScroll'; import useConfigInject from '../_util/hooks/useConfigInject'; +import useProvideAnchor from './context'; function getDefaultContainer() { return window; @@ -67,15 +66,7 @@ const anchorProps = { export type AnchorProps = Partial>; -export interface AntAnchor { - registerLink: (link: string) => void; - unregisterLink: (link: string) => void; - $data: AnchorState; - scrollTo: (link: string) => void; - $emit?: Function; -} export interface AnchorState { - activeLink: null | string; scrollContainer: HTMLElement | Window; links: string[]; scrollEvent: any; @@ -89,25 +80,21 @@ export default defineComponent({ emits: ['change', 'click'], setup(props, { emit, attrs, slots }) { const { prefixCls, getTargetContainer, direction } = useConfigInject('anchor', props); - const instance = getCurrentInstance(); const inkNodeRef = ref(); const anchorRef = ref(); const state = reactive({ - activeLink: null, links: [], scrollContainer: null, scrollEvent: null, animating: false, }); + const activeLink = ref(); const getContainer = computed(() => { const { getContainer } = props; - - const getFunc = getContainer || getTargetContainer.value || getDefaultContainer; - - return getFunc(); + return getContainer || getTargetContainer.value || getDefaultContainer; }); // func... - const getCurrentActiveLink = (offsetTop = 0, bounds = 5) => { + const getCurrentAnchor = (offsetTop = 0, bounds = 5) => { const { getCurrentAnchor } = props; if (typeof getCurrentAnchor === 'function') { @@ -140,9 +127,8 @@ export default defineComponent({ return ''; }; const setCurrentActiveLink = (link: string) => { - const { activeLink } = state; - if (activeLink !== link) { - state.activeLink = link; + if (activeLink.value !== link) { + activeLink.value = link; emit('change', link); } }; @@ -178,7 +164,7 @@ export default defineComponent({ return; } const { offsetTop, bounds, targetOffset } = props; - const currentActiveLink = getCurrentActiveLink( + const currentActiveLink = getCurrentAnchor( targetOffset !== undefined ? targetOffset : offsetTop || 0, bounds, ); @@ -196,8 +182,7 @@ export default defineComponent({ } }; - // provide data - provide('antAnchor', { + useProvideAnchor({ registerLink: (link: string) => { if (!state.links.includes(link)) { state.links.push(link); @@ -209,10 +194,12 @@ export default defineComponent({ state.links.splice(index, 1); } }, - $data: state, + activeLink, scrollTo: handleScrollTo, - } as AntAnchor); - provide('antAnchorContext', instance); + handleClick: (e, info) => { + emit('click', e, info); + }, + }); onMounted(() => { nextTick(() => { @@ -244,7 +231,7 @@ export default defineComponent({ const { offsetTop, affix, showInkInFixed } = props; const pre = prefixCls.value; const inkClass = classNames(`${pre}-ink-ball`, { - visible: state.activeLink, + visible: activeLink.value, }); const wrapperClass = classNames(props.wrapperClass, `${pre}-wrapper`, { diff --git a/components/anchor/AnchorLink.tsx b/components/anchor/AnchorLink.tsx index 8658e919c..fb08b0c07 100644 --- a/components/anchor/AnchorLink.tsx +++ b/components/anchor/AnchorLink.tsx @@ -1,8 +1,6 @@ import { - ComponentInternalInstance, defineComponent, ExtractPropTypes, - inject, nextTick, onBeforeUnmount, onMounted, @@ -11,8 +9,8 @@ import { import PropTypes from '../_util/vue-types'; import { getPropsSlot } from '../_util/props-util'; import classNames from '../_util/classNames'; -import { AntAnchor } from './Anchor'; import useConfigInject from '../_util/hooks/useConfigInject'; +import { useInjectAnchor } from './context'; // eslint-disable-next-line @typescript-eslint/no-unused-vars function noop(..._any: any[]): any {} @@ -30,22 +28,19 @@ export default defineComponent({ name: 'AAnchorLink', props: anchorLinkProps, setup(props, { slots }) { - const antAnchor = inject('antAnchor', { - registerLink: noop, - unregisterLink: noop, - scrollTo: noop, - $data: {}, - } as AntAnchor); - const antAnchorContext = inject('antAnchorContext', {}) as ComponentInternalInstance; + const { + handleClick: contextHandleClick, + scrollTo, + unregisterLink, + registerLink, + activeLink, + } = useInjectAnchor(); const { prefixCls } = useConfigInject('anchor', props); const handleClick = (e: Event) => { // antAnchor.scrollTo(props.href); - const { scrollTo } = antAnchor; const { href, title } = props; - if (antAnchorContext.emit) { - antAnchorContext.emit('click', e, { title, href }); - } + contextHandleClick(e, { title, href }); scrollTo(href); }; @@ -53,25 +48,25 @@ export default defineComponent({ () => props.href, (val, oldVal) => { nextTick(() => { - antAnchor.unregisterLink(oldVal); - antAnchor.registerLink(val); + unregisterLink(oldVal); + registerLink(val); }); }, ); onMounted(() => { - antAnchor.registerLink(props.href); + registerLink(props.href); }); onBeforeUnmount(() => { - antAnchor.unregisterLink(props.href); + unregisterLink(props.href); }); return () => { const { href, target } = props; const pre = prefixCls.value; const title = getPropsSlot(slots, props, 'title'); - const active = antAnchor.$data.activeLink === href; + const active = activeLink.value === href; const wrapperClassName = classNames(`${pre}-link`, { [`${pre}-link-active`]: active, }); diff --git a/components/anchor/context.ts b/components/anchor/context.ts new file mode 100644 index 000000000..5df5395d1 --- /dev/null +++ b/components/anchor/context.ts @@ -0,0 +1,31 @@ +import { computed, Ref, inject, InjectionKey, provide } from 'vue'; + +export interface AnchorContext { + registerLink: (link: string) => void; + unregisterLink: (link: string) => void; + activeLink: Ref; + scrollTo: (link: string) => void; + handleClick: (e: Event, info: { title: any; href: string }) => void; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function noop(..._any: any[]): any {} + +export const AnchorContextKey: InjectionKey = Symbol('anchorContextKey'); + +const useProvideAnchor = (state: AnchorContext) => { + provide(AnchorContextKey, state); +}; + +const useInjectAnchor = () => { + return inject(AnchorContextKey, { + registerLink: noop, + unregisterLink: noop, + scrollTo: noop, + activeLink: computed(() => ''), + handleClick: noop, + } as AnchorContext); +}; + +export { useInjectAnchor, useProvideAnchor }; +export default useProvideAnchor; diff --git a/components/style/themes/default.less b/components/style/themes/default.less index dcb8fa8cb..2bc651de7 100644 --- a/components/style/themes/default.less +++ b/components/style/themes/default.less @@ -433,7 +433,11 @@ // Anchor // --- +@anchor-bg: transparent; @anchor-border-color: @border-color-split; +@anchor-link-top: 7px; +@anchor-link-left: 16px; +@anchor-link-padding: @anchor-link-top 0 @anchor-link-top @anchor-link-left; // Tooltip // --- diff --git a/v2-doc b/v2-doc index a7013ae87..d19705328 160000 --- a/v2-doc +++ b/v2-doc @@ -1 +1 @@ -Subproject commit a7013ae87f69dcbcf547f4b023255b8a7a775557 +Subproject commit d197053285b81e77718621c0b5b94cb3b21831a2