Detect overflow and render shadows, fades, arrows, etc.
MIT License
overflow
container, inscroll
or resize
events.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 ? '' : ''}
</>
);
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.
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 youd like to overlay them on the scrollable viewport.
Callback that receives the latest overflow state and an object of refs, if youd 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 its
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.
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
, its preferable to include styling on your own element
inside <Overflow.Content>
instead otherwise you risk interfering with the
styles this component needs to function.
Content to render inside the scrollable viewport.
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 youd 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>
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.
This hook provides full access to the Overflows 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
}
}
<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>
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.
When rendered, youll 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:
data-scrollable-wrapper
contains everything. If you want toheight
or max-height
to define thedata-scrollable-viewport
is the one with overflow: auto
.data-scrollable-wrapper
). Anydata-scrollable-content
contains your content. It will growdata-scrollable-viewport
data-scrollable-tolerance
will optionally be inserted iftolerance
value; in that case, this element will be