import type { ExtractPropTypes, PropType } from 'vue'; import { defineComponent, nextTick, onActivated, onBeforeUnmount, onMounted, reactive, ref, watch, onDeactivated, } from 'vue'; import VerticalAlignTopOutlined from '@ant-design/icons-vue/VerticalAlignTopOutlined'; import addEventListener from '../vc-util/Dom/addEventListener'; import getScroll from '../_util/getScroll'; import { getTransitionProps, Transition } from '../_util/transition'; import scrollTo from '../_util/scrollTo'; import { withInstall } from '../_util/type'; import throttleByAnimationFrame from '../_util/throttleByAnimationFrame'; import useConfigInject from '../_util/hooks/useConfigInject'; import type { MouseEventHandler } from '../_util/EventInterface'; export const backTopProps = () => ({ visibilityHeight: { type: Number, default: 400 }, duration: { type: Number, default: 450 }, target: Function as PropType<() => HTMLElement | Window | Document>, prefixCls: String, onClick: Function as PropType, // visible: { type: Boolean, default: undefined }, // Only for test. Don't use it. }); export type BackTopProps = Partial>; const BackTop = defineComponent({ name: 'ABackTop', inheritAttrs: false, props: backTopProps(), // emits: ['click'], setup(props, { slots, attrs, emit }) { const { prefixCls, direction } = useConfigInject('back-top', props); const domRef = ref(); const state = reactive({ visible: false, scrollEvent: null, }); const getDefaultTarget = () => domRef.value && domRef.value.ownerDocument ? domRef.value.ownerDocument : window; const scrollToTop = (e: Event) => { const { target = getDefaultTarget, duration } = props; scrollTo(0, { getContainer: target, duration, }); emit('click', e); }; const handleScroll = throttleByAnimationFrame((e: Event | { target: any }) => { const { visibilityHeight } = props; const scrollTop = getScroll(e.target, true); state.visible = scrollTop > visibilityHeight; }); const bindScrollEvent = () => { const { target } = props; const getTarget = target || getDefaultTarget; const container = getTarget(); state.scrollEvent = addEventListener(container, 'scroll', (e: Event) => { handleScroll(e); }); handleScroll({ target: container, }); }; const scrollRemove = () => { if (state.scrollEvent) { state.scrollEvent.remove(); } (handleScroll as any).cancel(); }; watch( () => props.target, () => { scrollRemove(); nextTick(() => { bindScrollEvent(); }); }, ); onMounted(() => { nextTick(() => { bindScrollEvent(); }); }); onActivated(() => { nextTick(() => { bindScrollEvent(); }); }); onDeactivated(() => { scrollRemove(); }); onBeforeUnmount(() => { scrollRemove(); }); return () => { const defaultElement = (
); const divProps = { ...attrs, onClick: scrollToTop, class: { [`${prefixCls.value}`]: true, [`${attrs.class}`]: attrs.class, [`${prefixCls.value}-rtl`]: direction.value === 'rtl', }, }; const transitionProps = getTransitionProps('fade'); return (
{slots.default?.() || defaultElement}
); }; }, }); export default withInstall(BackTop);