refactor: anchor

pull/4134/head
tangjinzhou 2021-05-24 22:54:07 +08:00
parent fbe3a48ac2
commit a6e30c2ded
5 changed files with 64 additions and 47 deletions

View File

@ -4,10 +4,8 @@ import {
onBeforeUnmount, onBeforeUnmount,
onMounted, onMounted,
onUpdated, onUpdated,
provide,
reactive, reactive,
ref, ref,
getCurrentInstance,
ExtractPropTypes, ExtractPropTypes,
computed, computed,
} from 'vue'; } from 'vue';
@ -18,6 +16,7 @@ import Affix from '../affix';
import scrollTo from '../_util/scrollTo'; import scrollTo from '../_util/scrollTo';
import getScroll from '../_util/getScroll'; import getScroll from '../_util/getScroll';
import useConfigInject from '../_util/hooks/useConfigInject'; import useConfigInject from '../_util/hooks/useConfigInject';
import useProvideAnchor from './context';
function getDefaultContainer() { function getDefaultContainer() {
return window; return window;
@ -67,15 +66,7 @@ const anchorProps = {
export type AnchorProps = Partial<ExtractPropTypes<typeof anchorProps>>; export type AnchorProps = Partial<ExtractPropTypes<typeof anchorProps>>;
export interface AntAnchor {
registerLink: (link: string) => void;
unregisterLink: (link: string) => void;
$data: AnchorState;
scrollTo: (link: string) => void;
$emit?: Function;
}
export interface AnchorState { export interface AnchorState {
activeLink: null | string;
scrollContainer: HTMLElement | Window; scrollContainer: HTMLElement | Window;
links: string[]; links: string[];
scrollEvent: any; scrollEvent: any;
@ -89,25 +80,21 @@ export default defineComponent({
emits: ['change', 'click'], emits: ['change', 'click'],
setup(props, { emit, attrs, slots }) { setup(props, { emit, attrs, slots }) {
const { prefixCls, getTargetContainer, direction } = useConfigInject('anchor', props); const { prefixCls, getTargetContainer, direction } = useConfigInject('anchor', props);
const instance = getCurrentInstance();
const inkNodeRef = ref(); const inkNodeRef = ref();
const anchorRef = ref(); const anchorRef = ref();
const state = reactive<AnchorState>({ const state = reactive<AnchorState>({
activeLink: null,
links: [], links: [],
scrollContainer: null, scrollContainer: null,
scrollEvent: null, scrollEvent: null,
animating: false, animating: false,
}); });
const activeLink = ref();
const getContainer = computed(() => { const getContainer = computed(() => {
const { getContainer } = props; const { getContainer } = props;
return getContainer || getTargetContainer.value || getDefaultContainer;
const getFunc = getContainer || getTargetContainer.value || getDefaultContainer;
return getFunc();
}); });
// func... // func...
const getCurrentActiveLink = (offsetTop = 0, bounds = 5) => { const getCurrentAnchor = (offsetTop = 0, bounds = 5) => {
const { getCurrentAnchor } = props; const { getCurrentAnchor } = props;
if (typeof getCurrentAnchor === 'function') { if (typeof getCurrentAnchor === 'function') {
@ -140,9 +127,8 @@ export default defineComponent({
return ''; return '';
}; };
const setCurrentActiveLink = (link: string) => { const setCurrentActiveLink = (link: string) => {
const { activeLink } = state; if (activeLink.value !== link) {
if (activeLink !== link) { activeLink.value = link;
state.activeLink = link;
emit('change', link); emit('change', link);
} }
}; };
@ -178,7 +164,7 @@ export default defineComponent({
return; return;
} }
const { offsetTop, bounds, targetOffset } = props; const { offsetTop, bounds, targetOffset } = props;
const currentActiveLink = getCurrentActiveLink( const currentActiveLink = getCurrentAnchor(
targetOffset !== undefined ? targetOffset : offsetTop || 0, targetOffset !== undefined ? targetOffset : offsetTop || 0,
bounds, bounds,
); );
@ -196,8 +182,7 @@ export default defineComponent({
} }
}; };
// provide data useProvideAnchor({
provide('antAnchor', {
registerLink: (link: string) => { registerLink: (link: string) => {
if (!state.links.includes(link)) { if (!state.links.includes(link)) {
state.links.push(link); state.links.push(link);
@ -209,10 +194,12 @@ export default defineComponent({
state.links.splice(index, 1); state.links.splice(index, 1);
} }
}, },
$data: state, activeLink,
scrollTo: handleScrollTo, scrollTo: handleScrollTo,
} as AntAnchor); handleClick: (e, info) => {
provide('antAnchorContext', instance); emit('click', e, info);
},
});
onMounted(() => { onMounted(() => {
nextTick(() => { nextTick(() => {
@ -244,7 +231,7 @@ export default defineComponent({
const { offsetTop, affix, showInkInFixed } = props; const { offsetTop, affix, showInkInFixed } = props;
const pre = prefixCls.value; const pre = prefixCls.value;
const inkClass = classNames(`${pre}-ink-ball`, { const inkClass = classNames(`${pre}-ink-ball`, {
visible: state.activeLink, visible: activeLink.value,
}); });
const wrapperClass = classNames(props.wrapperClass, `${pre}-wrapper`, { const wrapperClass = classNames(props.wrapperClass, `${pre}-wrapper`, {

View File

@ -1,8 +1,6 @@
import { import {
ComponentInternalInstance,
defineComponent, defineComponent,
ExtractPropTypes, ExtractPropTypes,
inject,
nextTick, nextTick,
onBeforeUnmount, onBeforeUnmount,
onMounted, onMounted,
@ -11,8 +9,8 @@ import {
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 classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import { AntAnchor } from './Anchor';
import useConfigInject from '../_util/hooks/useConfigInject'; import useConfigInject from '../_util/hooks/useConfigInject';
import { useInjectAnchor } from './context';
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
function noop(..._any: any[]): any {} function noop(..._any: any[]): any {}
@ -30,22 +28,19 @@ export default defineComponent({
name: 'AAnchorLink', name: 'AAnchorLink',
props: anchorLinkProps, props: anchorLinkProps,
setup(props, { slots }) { setup(props, { slots }) {
const antAnchor = inject('antAnchor', { const {
registerLink: noop, handleClick: contextHandleClick,
unregisterLink: noop, scrollTo,
scrollTo: noop, unregisterLink,
$data: {}, registerLink,
} as AntAnchor); activeLink,
const antAnchorContext = inject('antAnchorContext', {}) as ComponentInternalInstance; } = useInjectAnchor();
const { prefixCls } = useConfigInject('anchor', props); const { prefixCls } = useConfigInject('anchor', props);
const handleClick = (e: Event) => { const handleClick = (e: Event) => {
// antAnchor.scrollTo(props.href); // antAnchor.scrollTo(props.href);
const { scrollTo } = antAnchor;
const { href, title } = props; const { href, title } = props;
if (antAnchorContext.emit) { contextHandleClick(e, { title, href });
antAnchorContext.emit('click', e, { title, href });
}
scrollTo(href); scrollTo(href);
}; };
@ -53,25 +48,25 @@ export default defineComponent({
() => props.href, () => props.href,
(val, oldVal) => { (val, oldVal) => {
nextTick(() => { nextTick(() => {
antAnchor.unregisterLink(oldVal); unregisterLink(oldVal);
antAnchor.registerLink(val); registerLink(val);
}); });
}, },
); );
onMounted(() => { onMounted(() => {
antAnchor.registerLink(props.href); registerLink(props.href);
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
antAnchor.unregisterLink(props.href); unregisterLink(props.href);
}); });
return () => { return () => {
const { href, target } = props; const { href, target } = props;
const pre = prefixCls.value; const pre = prefixCls.value;
const title = getPropsSlot(slots, props, 'title'); const title = getPropsSlot(slots, props, 'title');
const active = antAnchor.$data.activeLink === href; const active = activeLink.value === href;
const wrapperClassName = classNames(`${pre}-link`, { const wrapperClassName = classNames(`${pre}-link`, {
[`${pre}-link-active`]: active, [`${pre}-link-active`]: active,
}); });

View File

@ -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<string>;
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<AnchorContext> = 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;

View File

@ -433,7 +433,11 @@
// Anchor // Anchor
// --- // ---
@anchor-bg: transparent;
@anchor-border-color: @border-color-split; @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 // Tooltip
// --- // ---

2
v2-doc

@ -1 +1 @@
Subproject commit a7013ae87f69dcbcf547f4b023255b8a7a775557 Subproject commit d197053285b81e77718621c0b5b94cb3b21831a2