import * as React from 'react'; import CSSMotion from 'rc-motion'; import addEventListener from 'rc-util/lib/Dom/addEventListener'; import useMergedState from 'rc-util/lib/hooks/useMergedState'; import classNames from 'classnames'; import omit from 'rc-util/lib/omit'; import VerticalAlignTopOutlined from '@ant-design/icons/VerticalAlignTopOutlined'; import { throttleByAnimationFrame } from '../_util/throttleByAnimationFrame'; import { ConfigContext } from '../config-provider'; import getScroll from '../_util/getScroll'; import scrollTo from '../_util/scrollTo'; import { cloneElement } from '../_util/reactNode'; import useStyle from './style'; export interface BackTopProps { visibilityHeight?: number; onClick?: React.MouseEventHandler; target?: () => HTMLElement | Window | Document; prefixCls?: string; children?: React.ReactNode; className?: string; style?: React.CSSProperties; duration?: number; visible?: boolean; // Only for test. Don't use it. } const BackTop: React.FC = props => { const [visible, setVisible] = useMergedState(false, { value: props.visible, }); const ref = React.createRef(); const scrollEvent = React.useRef(); const getDefaultTarget = () => ref.current && ref.current.ownerDocument ? ref.current.ownerDocument : window; const handleScroll = throttleByAnimationFrame( (e: React.UIEvent | { target: any }) => { const { visibilityHeight } = props; const scrollTop = getScroll(e.target, true); setVisible(scrollTop > visibilityHeight!); }, ); const bindScrollEvent = () => { const { target } = props; const getTarget = target || getDefaultTarget; const container = getTarget(); scrollEvent.current = addEventListener(container, 'scroll', (e: React.UIEvent) => { handleScroll(e); }); handleScroll({ target: container, }); }; React.useEffect(() => { bindScrollEvent(); return () => { if (scrollEvent.current) { scrollEvent.current.remove(); } (handleScroll as any).cancel(); }; }, [props.target]); const scrollToTop = (e: React.MouseEvent) => { const { onClick, target, duration = 450 } = props; scrollTo(0, { getContainer: target || getDefaultTarget, duration, }); if (typeof onClick === 'function') { onClick(e); } }; const renderChildren = ({ prefixCls, rootPrefixCls, }: { prefixCls: string; rootPrefixCls: string; }) => { const { children } = props; const defaultElement = (
); return ( {({ className: motionClassName }) => cloneElement(children || defaultElement, ({ className }) => ({ className: classNames(motionClassName, className), })) } ); }; const { getPrefixCls, direction } = React.useContext(ConfigContext); const { prefixCls: customizePrefixCls, className = '' } = props; const prefixCls = getPrefixCls('back-top', customizePrefixCls); const rootPrefixCls = getPrefixCls(); const [wrapSSR, hashId] = useStyle(prefixCls); const classString = classNames( hashId, prefixCls, { [`${prefixCls}-rtl`]: direction === 'rtl', }, className, ); // fix https://fb.me/react-unknown-prop const divProps = omit(props, [ 'prefixCls', 'className', 'children', 'visibilityHeight', 'target', 'visible', ]); return wrapSSR(
{renderChildren({ prefixCls, rootPrefixCls })}
, ); }; BackTop.defaultProps = { visibilityHeight: 400, }; export default React.memo(BackTop);