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-overflow-indicator

v0.2.2

Published

- Let users know when there’s more content to see in an `overflow` container, in case their device hides scrollbars. - Uses [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) for performance and accuracy –

Downloads

10,346

Readme

react-overflow-indicator ⏬

  • Let users know when there’s more content to see in an overflow container, in case their device hides scrollbars.
  • Uses Intersection Observer for performance and accuracy – no listening for scroll or resize events.
  • Flexible: render any style of indicator you want (shadows, arrows, messages, etc.) wherever you want, using any styling solution.

Shadow animated demo

Fade animated demo

Icon animated demo

Quick Start

Install:

$ yarn add react-overflow-indicator

Import:

import Overflow from 'react-overflow-indicator';

Render indicators automatically using <Overflow.Indicator> inside of <Overflow>:

<Overflow>
  <Overflow.Content>
    Render an element or put your content directly here…
  </Overflow.Content>
  <Overflow.Indicator direction="down">👇</Overflow.Indicator>
</Overflow>

…or, use the onStateChange prop to react to overflow however you like:

const [canScroll, setCanScroll] = useState(false);

return (
  <>
    <Overflow onStateChange={state => setCanScroll(state.canScroll.down)}>
      <Overflow.Content>
        Render an element or put your content directly here…
      </Overflow.Content>
    </Overflow>
    {canScroll ? '👇' : '🌈'}
  </>
);

API

Overflow

The overflow state provider. At a minimum it must contain an <Overflow.Content> element, otherwise it will do nothing.

<Overflow>
  <Overflow.Content>
    Your element(s) here!
  </Overflow.Content>
<Overflow>

As with any standard element, its height must be limited in some way in order for it to actually scroll. Apply that style as you would any other element, with style or className:

<Overflow style={{ maxHeight: 500 }}>…</Overflow>

Usage with styled-components:

const MyContainer = styled(Overflow)`
  max-height: 500px;
`;

Any remaining props beyond those documented below will be passed along to the underlying DOM element. Use this to pass className, style, or any other native attribute.

Props

Elements to render inside the outer container. This should include an <Overflow.Content> element at a minimum, but should also include your scroll indicators if you’d like to overlay them on the scrollable viewport.

Callback that receives the latest overflow state and an object of refs, if you’d like to react to overflow in a custom way.

Distance (number of pixels or CSS length unit like 1em) to the edge of the content at which to consider the viewport fully scrolled. For example, if set to 10, then it will consider scrolling to have reached the end as long as it’s within 10 pixels of the border. You can use this when your content has padding and scrolling close to the edge should be good enough.

Overflow.Content

Wrapper for content to render inside the scrollable viewport. This element will grow to whatever size it needs to hold its content, and will cause the parent viewport element to overflow. It must be rendered inside an <Overflow> ancestor.

Although you can style this element directly by passing additional props like className and style, it’s preferable to include styling on your own element inside <Overflow.Content> instead – otherwise you risk interfering with the styles this component needs to function.

Props

Content to render inside the scrollable viewport.

Overflow.Indicator

A helper component for rendering your custom indicator when the viewport is scrollable in a particular direction (or any direction). Must be rendered inside an <Overflow> ancestor.

You can provide a direction prop to indicate when scrolling is allowed in a particular direction:

<Overflow>
  <Overflow.Content>…</Overflow.Content>
  <Overflow.Indicator direction="right">👉</Overflow.Indicator>
</Overflow>

…or exclude it to indicate when scrolling is allowed in any direction:

<Overflow>
  <Overflow.Content>…</Overflow.Content>
  <Overflow.Indicator>←↕→</Overflow.Indicator>
</Overflow>

This component will mount its children when scrolling is allowed in the requested direction, and unmount them otherwise. If you’d rather remain mounted (to allow transitions, for example), then render a function. It will be supplied with a Boolean (if direction is supplied) or an object with up, left, right, and down properties:

<Overflow>
  <Overflow.Indicator direction="down">
    {canScroll => (canScroll ? '🔽' : '✅')}
  </Overflow.Indicator>
</Overflow>

Props

Indicator to render when scrolling is allowed in the requested direction. If given a function, it will be passed the overflow state and an object containing the viewport ref. You can use this refs parameter to render an indicator that is also a button that scrolls the viewport (for example).

The scrollabe direction to watch for. If not supplied, the indicator will be active when scrolling is allowed in any direction.

useOverflow

This hook provides full access to the Overflow’s context containing its current state and refs. While <Overflow.Indicator> should be good enough for most use cases, you can use this if you have other use cases in mind. Must be used inside an <Overflow> ancestor.

Returns an object like:

{
  state: {
    canScroll: {
      up: Boolean,
      left: Boolean,
      right: Boolean,
      down: Boolean
    }
  },
  dispatch: Function,
  tolerance: Number | String,
  refs: {
    viewport: Object
  }
}

Examples

Make the indicator a button that scrolls the viewport

<Overflow.Indicator direction="down">
  {(canScroll, refs) => (
    <button
      type="button"
      onClick={() => {
        refs.viewport.current.scrollBy({
          top: refs.viewport.current.clientHeight,
          behavior: 'smooth'
        });
      }}
      style={{ position: 'absolute', right: 10, bottom: 10 }}
    >
      {canScroll ? '⏬' : '✅'}
    </button>
  )}
</Overflow.Indicator>

Implementation Details

Instead of the traditional method of listening for scroll and resize events, this uses the more performant Intersection Observer API.

Here, an IntersectionObserver watches each of the 4 sides of the viewport element to see when the scrollable content extends past that edge.

What’s the purpose of each element?

When rendered, you’ll see a structure similar to this:

<div data-overflow-wrapper>
  <div data-overflow-viewport>
    <div data-overflow-content>
      <div data-overflow-tolerance></div> (Optional)
      Finally, your scrollable content here…
    </div>
  </div>
</div>

That seems like a lot! But each one serves a purpose – various CSS and DOM behaviors make this surprisingly difficult to implement otherwise.

From the top down:

  • The element with data-scrollable-wrapper contains everything. If you want to insert some indicator overlay (like shadows, an arrow, a floating message), they should usually be children of this element so that they’ll be siblings of the scrollable viewport and thus will remain in their positions instead of scrolling away. When you define a height or max-height to define the scrollable viewport size, it will be on this element.
  • The element with data-scrollable-viewport is the one with overflow: auto. It will match the size of its parent (the data-scrollable-wrapper). Any indicators you render will usually be siblings of this element.
  • The element with data-scrollable-content contains your content. It will grow to whatever size it needs and potentially cause the data-scrollable-viewport element to overflow.
  • The element with data-scrollable-tolerance will optionally be inserted if you use a nonzero tolerance value; in that case, this element will be observed instead of the content element.