refactor: anchor
parent
fbe3a48ac2
commit
a6e30c2ded
|
@ -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`, {
|
||||||
|
|
|
@ -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,
|
||||||
});
|
});
|
||||||
|
|
|
@ -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;
|
|
@ -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
2
v2-doc
|
@ -1 +1 @@
|
||||||
Subproject commit a7013ae87f69dcbcf547f4b023255b8a7a775557
|
Subproject commit d197053285b81e77718621c0b5b94cb3b21831a2
|
Loading…
Reference in New Issue