All files index.tsx

81.25% Statements 26/32
50% Branches 17/34
75% Functions 6/8
79.31% Lines 23/29

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144                                            1x                     1x         1x       1x                       1x                           1x 1x 1x 1x                         1x 1x 1x 1x   1x                 1x 1x 1x 1x   1x 1x 1x         1x       1x                                                          
import React, { useEffect, useMemo, useRef, useState } from 'react';
 
export interface BackToUpProps extends React.AllHTMLAttributes<HTMLDivElement> {
  prefixCls?: string;
  /** Scroll bar area @default document.documentElement */
  element?: HTMLElement | null;
  /** Whether to use smooth scrolling* @default true */
  smooth?: boolean;
  /** Classname to add/override styling (note, !important for overrides might be needed) */
  className?: string;
  /** Object to add/override styling */
  style?: React.CSSProperties;
  /** Height after page scroll to be visible @default 120 */
  top?: number;
  /** The Button width & height @default 35 */
  size?: number;
  /** the width of the progress bar */
  strokeWidth?: number;
  /** hide progress icon */
  hideProgress?: boolean;
}
 
const warpperStyle: React.CSSProperties = {
  position: 'sticky',
  bottom: 15,
  right: 15,
  visibility: 'visible',
  opacity: 0,
  transition: 'visibility 0.3s linear 0s, opacity 0.3s linear 0s',
  cursor: 'pointer',
  userSelect: 'none',
};
 
const svgStyle: React.CSSProperties = {
  display: 'block',
  transform: 'rotate(-90deg)',
};
 
const circleStyle: React.CSSProperties = {
  transition: 'stroke-dashoffset 0.3s linear 0s',
};
 
const childStyle: React.CSSProperties = {
  position: 'absolute',
  top: 0,
  display: 'flex',
  height: '100%',
  width: '100%',
  alignItems: 'center',
  justifyContent: 'center',
  color: '#fff',
  fontSize: 12,
};
 
const documentElement = document.documentElement;
 
export default function BackToUp(props: BackToUpProps = {}) {
  const {
    className,
    prefixCls = 'w-back-to-up',
    element = documentElement,
    top = 120,
    size = 35,
    strokeWidth = 3,
    smooth = true,
    hideProgress = false,
    children,
    ...others
  } = props;
  const $dom = useRef<HTMLDivElement>(null);
  const cls = [className, prefixCls].filter(Boolean).join(' ');
  const style: React.CSSProperties = Object.assign(
    {},
    warpperStyle,
    {
      position: element === documentElement ? 'fixed' : 'sticky',
    },
    others.style,
    {
      width: size,
      height: size,
      opacity: top === 0 ? 1 : 0,
    },
  );
  const center = useMemo(() => size / 2, [size]);
  const radius = useMemo(() => size / 2 - strokeWidth / 2, [size, strokeWidth]);
  const dasharray = useMemo(() => Math.PI * radius * 2, [radius]);
  const [progress, setProgress] = useState(dasharray || 0);
 
  const handleScroll = (ev: MouseEventInit) => {
    const { clientHeight, scrollHeight, scrollTop } = element || documentElement;
    const percentage = scrollTop / (scrollHeight - clientHeight);
    setProgress(dasharray - dasharray * percentage);
    if ($dom.current && top > 0) {
      $dom.current.style.opacity = scrollTop > top ? '1' : '0';
    }
  };
 
  useEffect(() => {
    const scrollElement = element === documentElement ? document : element;
    Eif (scrollElement) {
      scrollElement.addEventListener('scroll', handleScroll, { passive: true });
    }
    return () => {
      Eif (scrollElement) {
        scrollElement.removeEventListener('scroll', handleScroll);
      }
    };
  }, [element]);
 
  const goToUp = (ev: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
    element!.scrollTo({ top: 0, behavior: smooth ? 'smooth' : 'auto' });
  };
 
  return (
    <div className={cls} ref={$dom} {...others} onClick={goToUp} style={style}>
      {!hideProgress && (
        <svg viewBox={`0 0 ${size} ${size}`} width={size} height={size} focusable="false" style={svgStyle}>
          <circle
            fill="rgb(0 0 0 / 75%)"
            stroke="rgb(200 200 200 / 85%)"
            strokeWidth={strokeWidth}
            r={radius}
            cx={center}
            cy={center}
          />
          <circle
            fill="none"
            stroke="rgb(0 0 0 / 50%)"
            strokeWidth={strokeWidth}
            r={radius}
            cx={center}
            cy={center}
            strokeDasharray={dasharray}
            strokeDashoffset={progress}
            style={circleStyle}
          />
        </svg>
      )}
      {children && <div style={childStyle}>{children}</div>}
    </div>
  );
}