npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-awesome-ptr

v1.0.63

Published

A pull-to-refresh component that behaves like in native app

Downloads

268

Readme

react-awesome-ptr

npm version npm bundle size npm type definitions npm GitHub

A pull-to-refresh react component that behave like almost native app.

  • in iOS:
    • It utilizes natively supported scroll bounce.
  • in the others:
    • It makes natural scroll bounce artificially.
  • Suitable for all of mobile browsers and web views (inactivated in desktop).
  • Automatically disable mobile browser's default pull-to-refresh (only in Android).
  • Customizable spinner.
  • Support Typescript.

Make sure it's a mobile-only component that works by touch events.

In default pull to refresh supported browsers like Safari in iOS 15, it works only when the browser is not scrolled. If browser is scrolled, browser's default pull to refresh will be triggered.

For the webviews in hybrid apps, it works with no worries.

A complementary custom spinner component CupertinoSpinner is included. Feel free to use this component to enhance UX like iOS, but keep in mind that the default spinner shows better performance.

Default Spinner example

<img src="https://user-images.githubusercontent.com/17351661/134833837-ad712f07-0a97-43ef-9117-90e214f65032.gif" width="300" />

CupertinoSpinner example

<img src="https://user-images.githubusercontent.com/17351661/145903534-bb3397b8-73a1-4342-9ff1-6b8d57536a70.gif" width="300" />

Usage

Install

npm i --save react-awesome-ptr

import

import PullToRefresh from "react-awesome-ptr";
import "react-awesome-ptr/dist/index.css";

// put the PullToRefresh component where the spinner should be shown

Examples

See examples in a mobile browser, or turn on the browser debugger and toggle device toolbar. You can conveniently open example pages on your mobile by scanning QR codes.

If you see examples in desktop browser, make sure to set the device as any android device.

On the top

import PullToRefresh from "react-awesome-ptr";
import "react-awesome-ptr/dist/index.css";

export const OnTheTop = () => {
  const targetRef = useRef<HTMLDivElement>(null);

  const [isRefreshing, setIsRefreshing] = useState(false);

  const onRefresh = useCallback(() => {
    setIsRefreshing(true);
    setTimeout(() => {
      setIsRefreshing(false);
    }, 3000);
  }, []);

  return (
    <>
      <PullToRefresh
        targetRef={targetRef}
        onRefresh={onRefresh}
        isRefreshing={isRefreshing}
        hasDefaultPullToRefreshPossibly
      />
      <div style={{ height: "100vh", background: "pink", padding: 20 }} ref={targetRef}>
        Pull in a mobile browser
      </div>
    </>
  );
};

With header

import PullToRefresh from "react-awesome-ptr";
import "react-awesome-ptr/dist/index.css";

export const WithHeader = () => {
  const targetRef = useRef<HTMLDivElement>(null);

  const [isRefreshing, setIsRefreshing] = useState(false);

  const onRefresh = useCallback(() => {
    setIsRefreshing(true);
    setTimeout(() => {
      setIsRefreshing(false);
    }, 3000);
  }, []);

  return (
    <>
      <div
        style={{
          height: 60,
          background: "cyan",
          padding: 20,
          position: "fixed",
          top: 0,
          width: "100%",
          zIndex: 1,
        }}
      >
        Header
      </div>
      <PullToRefresh
        targetRef={targetRef}
        onRefresh={onRefresh}
        isRefreshing={isRefreshing}
        originTop={100}
        originMarginTop={100}
        hasDefaultPullToRefreshPossibly
      />
      <div
        style={{ height: "100vh", background: "pink", padding: 20, marginTop: 100 }}
        ref={targetRef}
      >
        Pull in a mobile browser
      </div>
    </>
  );
};

Artificial bounce

import PullToRefresh from "react-awesome-ptr";
import "react-awesome-ptr/dist/index.css";

export const ArtificialBounce = () => {
  const targetRef = useRef<HTMLDivElement>(null);

  const [isRefreshing, setIsRefreshing] = useState(false);

  const onRefresh = useCallback(() => {
    setIsRefreshing(true);
    setTimeout(() => {
      setIsRefreshing(false);
    }, 3000);
  }, []);

  return (
    <>
      <PullToRefresh
        targetRef={targetRef}
        onRefresh={onRefresh}
        isRefreshing={isRefreshing}
        isBounceNotSupported
      />
      <div style={{ height: "100vh", background: "pink", padding: 20 }} ref={targetRef}>
        Pull in a mobile browser (forced artificial bounce)
      </div>
    </>
  );
};

CupertinoSpinner as Custom spinner

import PullToRefresh, { CupertinoSpinner } from "react-awesome-ptr";
import "react-awesome-ptr/dist/index.css";

export const CupertinoSpinnerAsCustomSpinner = () => {
  const targetRef = useRef<HTMLDivElement>(null);

  const [isRefreshing, setIsRefreshing] = useState(false);

  const [progress, setProgress] = useState(0);
  const [pullToRefreshState, setPullToRefreshState] = useState<PullToRefreshState>(
    "idle",
  );

  const isTriggerReady = useMemo(() => {
    return pullToRefreshState === "triggerReady";
  }, [pullToRefreshState]);

  const onRefresh = useCallback(() => {
    setIsRefreshing(true);
    setTimeout(() => {
      setIsRefreshing(false);
    }, 3000);
  }, []);

  const onPull = useCallback((progress: number) => {
    setProgress(progress);
  }, []);

  const onChangeState = useCallback((state: PullToRefreshState) => {
    setPullToRefreshState(state);
  }, []);

  const customSpinner = useMemo(() => {
    return (
      <CupertinoSpinner
        progress={progress}
        isRefreshing={isRefreshing}
        isTriggerReady={isTriggerReady}
      />
    );
  }, [pullToRefreshState, progress, isRefreshing, isTriggerReady]);

  return (
    <>
      <PullToRefresh
        targetRef={targetRef}
        onRefresh={onRefresh}
        isRefreshing={isRefreshing}
        onPull={onPull}
        onChangeState={onChangeState}
        customSpinner={customSpinner}
        hasDefaultPullToRefreshPossibly
        isOpacityChangeOnPullDisabled
        isRotationSpinnerOnPullDisabled
        completeDelay={200}
      />
      <div style={{ height: "100vh", background: "pink", padding: 20 }} ref={targetRef}>
        <p>Pull in a mobile browser (CupertinoSpinner as custom spinner)</p>
      </div>
    </>
  );
};

Custom spinner

import PullToRefresh from "react-awesome-ptr";
import "react-awesome-ptr/dist/index.css";

export const CustomSpinner = () => {
  const targetRef = useRef<HTMLDivElement>(null);

  const [isRefreshing, setIsRefreshing] = useState(false);

  const [progress, setProgress] = useState(0);
  const [pullToRefreshState, setPullToRefreshState] = useState("idle");

  const onRefresh = useCallback(() => {
    setIsRefreshing(true);
    setTimeout(() => {
      setIsRefreshing(false);
    }, 3000);
  }, []);

  const onPull = useCallback((progress: number) => {
    setProgress(progress);
  }, []);

  const onChangeState = useCallback((state: PullToRefreshState) => {
    setPullToRefreshState(state);
  }, []);

  const customSpinner = useMemo(() => {
    return (
      <div style={{ textAlign: "center", marginTop: 15 }}>
        {pullToRefreshState === "triggerReady"
          ? "⬆️ Release"
          : pullToRefreshState === "refreshing"
          ? "Refreshing..."
          : pullToRefreshState === "complete"
          ? "Complete"
          : `⬇️ Pull to refresh (${(progress * 100).toFixed()}%)`}
      </div>
    );
  }, [pullToRefreshState, isRefreshing, progress]);

  return (
    <>
      <PullToRefresh
        targetRef={targetRef}
        onRefresh={onRefresh}
        isRefreshing={isRefreshing}
        onPull={onPull}
        onChangeState={onChangeState}
        customSpinner={customSpinner}
        completeDelay={500}
        hasDefaultPullToRefreshPossibly
      />
      <div style={{ height: "100vh", background: "pink", padding: 20 }} ref={targetRef}>
        <p>Pull in a mobile browser (custom spinner)</p>
      </div>
    </>
  );
};

Hidden spinner during refreshing

import PullToRefresh from "react-awesome-ptr";
import "react-awesome-ptr/dist/index.css";

export const HiddenSpinnerDuringRefreshing = () => {
  const targetRef = useRef<HTMLDivElement>(null);

  const [isRefreshing, setIsRefreshing] = useState(false);

  const onRefresh = useCallback(() => {
    setIsRefreshing(true);
    setTimeout(() => {
      setIsRefreshing(false);
    }, 3000);
  }, []);

  return (
    <>
      <PullToRefresh
        targetRef={targetRef}
        onRefresh={onRefresh}
        isRefreshing={isRefreshing}
        isSpinnerHiddenDuringRefreshing
        hasDefaultPullToRefreshPossibly
      />
      <div style={{ height: "100vh", background: "pink", padding: 20 }} ref={targetRef}>
        <p>Pull in a mobile browser (hidden spinner during refreshing)</p>
        <p>{isRefreshing ? "Refreshing..." : ""}</p>
      </div>
    </>
  );
};

Dark mode

import PullToRefresh from "react-awesome-ptr";
import "react-awesome-ptr/dist/index.css";

export const HiddenSpinnerDuringRefreshing = () => {
  const targetRef = useRef<HTMLDivElement>(null);

  const [isRefreshing, setIsRefreshing] = useState(false);

  const onRefresh = useCallback(() => {
    setIsRefreshing(true);
    setTimeout(() => {
      setIsRefreshing(false);
    }, 3000);
  }, []);

  useEffect(() => {
    const originBackgroundColor = document.body.style.backgroundColor;
    document.body.style.backgroundColor = "#000";
    return () => {
      document.body.style.backgroundColor = originBackgroundColor;
    };
  }, []);

  return (
    <>
      <PullToRefresh
        targetRef={targetRef}
        onRefresh={onRefresh}
        isRefreshing={isRefreshing}
        isDarkMode
        hasDefaultPullToRefreshPossibly
      />
      <div style={{ height: "100vh", background: "pink", padding: 20 }} ref={targetRef}>
        Pull in a mobile browser (dark mode)
      </div>
    </>
  );
};

Props

| name | type | required | default | description | | ------------------------------- | :-----------------------------------: | :------: | :-----: | ---------------------------------------------------------------------------------------------------------------------------------------------- | | targetRef | React.RefObject<HTMLElement> | O | | Target element to pull | | originTop | number | | 0 | Top of the target where pull-to-refresh starts based on clientRects | | originMarginTop | number | | 0 | Original margin of the target | | triggerHeight | number | | 80 | The height(distance) at which pull-to-refresh is triggered | | progressHeight | number | | 50 | Height to keep during refresh | | onRefresh | VoidFunction | O | | Callback to refresh | | refreshDelay | number | | 0 | If refresh time is too short to show spinner, use this prop to delay. | | isRefreshing | boolean | O | | Set true during refresh. | | spinnerSize | number | | 32 | Size of spinner in pixel | | tension | number | | 0.8 | Value of artificial tension. Set under 1, 0 is the most powerful tension. (0.85 ~ 0.75 is appropriate) | | isBounceSupported | boolean | | | Set true if native scroll bounce is supported not in iOS. | | isBounceNotSupported | boolean | | | Set true if native scroll bounce is not supported in iOS. | | customSpinner | React.ReactNode | | | Custom spinner | | onPull | (progress: number) => void | | | Callback passing progress 0 to 1 as a param that is called when user is pulling | | onRelease | VoidFunction | | | Callback that is called when user releases target | | onChangeState | (state: PullToRefreshState) => void | | | Callback passing state idle, pulling, triggerReady, refreshing, complete when state changes | | completeDelay | number | | 0 | Set milliseconds if you want to show complete message during complete | | isHiddenSpinnerDuringRefreshing | boolean | | | Set true if you have to hide spinner during refreshing | | hideDelay | number | | | Set milliseconds with the prop isHiddenSpinnerDuringRefreshing as true if you want to delay hiding spinner instead of hiding directly. | | hasDefaultPullToRefreshPossibly | boolean | | | Set true if your service is possibly served in browsers that has default pull-to-refresh. | | isDarkMode | boolean | | | Set true if default spinner needs to be shown above dark background | | spinnerZIndex | number | | -1 | | | isDisabled | boolean | | | Set true to disable pull to refresh | | isOpacityChangeOnPullDisabled | boolean | | | Set true if you don't want to change spinner's opacity on pull | | isRotationSpinnerOnPullDisabled | boolean | | | Set true if you don't want to rotate spinner on pull |

Contributions

Contributions will be welcomed! Just make PRs to https://github.com/eunvanz/react-awesome-ptr.

Have some Github contributions?

You probably like my side project 👉 https://gitkemon.com/link/sl_68A

License

react-awesome-pull-to-refresh is released under the MIT license.