Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit cba9873

Browse files
committed
Add refs argument and useOverflow hook
1 parent b97b8e2 commit cba9873

File tree

5 files changed

+133
-57
lines changed

5 files changed

+133
-57
lines changed

‎.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
!.*rc.js
2+
dist

‎.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
!.*rc.js
2+
dist

‎README.md

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ indicators if you’d like to overlay them on the scrollable viewport.
147147
<td valign="top" align="right" rowspan="1"></td>
148148
<td valign="top" valign="top" rowspan="1">
149149

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

153153
</td>
154154
</tr>
@@ -272,8 +272,9 @@ One&nbsp;of... <br>
272272
<td valign="top" valign="top" rowspan="1">
273273

274274
Indicator to render when scrolling is allowed in the requested direction. If
275-
given a function, it will be passed the overflow state and its result will be
276-
rendered.
275+
given a function, it will be passed the overflow state and and object containing
276+
the `viewport` ref (you can use the `refs` parameter to render an indicator that
277+
is also a button that scrolls the viewport).
277278

278279
</td>
279280
</tr>
@@ -298,6 +299,55 @@ active when scrolling is allowed in any direction.
298299
</table>
299300
<!-- AUTO-GENERATED-CONTENT:END -->
300301

302+
### useOverflow
303+
304+
This hook provides full access to the Overflow’s context containing its current
305+
`state` and `refs`. While `<Overflow.Indicator>` should be good enough for most
306+
use cases, you can use this if you have other use cases in mind. Must be used
307+
inside an `<Overflow>` ancestor.
308+
309+
Returns an object like:
310+
311+
```js
312+
{
313+
state: {
314+
canScroll: {
315+
up: Boolean,
316+
left: Boolean,
317+
right: Boolean,
318+
down: Boolean
319+
}
320+
},
321+
dispatch: Function,
322+
tolerance: Number | String,
323+
refs: {
324+
viewport: Object
325+
}
326+
}
327+
```
328+
329+
## Examples
330+
331+
### Make the indicator a button that scrolls the viewport
332+
333+
```jsx
334+
<Overflow.Indicator direction="down">
335+
{(canScroll, refs) => (
336+
<button
337+
onClick={() => {
338+
refs.viewport.current.scrollBy({
339+
top: refs.viewport.current.clientHeight,
340+
behavior: 'smooth'
341+
});
342+
}}
343+
style={{ position: 'absolute', right: 10, bottom: 10 }}
344+
>
345+
{canScroll ? '' : ''}
346+
</button>
347+
)}
348+
</Overflow.Indicator>
349+
```
350+
301351
## Implementation Details
302352

303353
Instead of the traditional method of listening for `scroll` and `resize` events,

‎pages/index.js

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -372,29 +372,37 @@ export default function DemoPage() {
372372
</ul>
373373
</Overflow.Content>
374374
<Overflow.Indicator direction="down">
375-
{canScroll => (
376-
<span
375+
{(canScroll, refs) => (
376+
<button
377+
onClick={() => {
378+
console.log(refs.viewport.current);
379+
refs.viewport.current.scrollBy({
380+
top: refs.viewport.current.clientHeight,
381+
behavior: 'smooth'
382+
});
383+
}}
377384
style={{
385+
display: 'inline-block',
378386
position: 'absolute',
379387
right: 0,
380388
bottom: 12,
381-
transform: 'translate3d(-50%, 0, 0)',
382-
display: 'inline-block',
383389
width: 40,
384390
height: 40,
385-
fontSize: 24,
391+
transform: 'translate3d(-50%, 0, 0)',
386392
border: '1px solid #ddd',
387-
lineHeight: '40px',
388-
background: 'white',
389393
borderRadius: '50%',
394+
padding: 0,
395+
fontSize: 24,
396+
lineHeight: '40px',
390397
textAlign: 'center',
398+
background: 'white',
391399
opacity: canScroll ? 1 : 0,
392400
animation: 'bounce 2s infinite ease',
393401
transition: 'opacity 500ms 500ms ease-out'
394402
}}
395403
>
396404
{canScroll ? '⏬' : '✅'}
397-
</span>
405+
</button>
398406
)}
399407
</Overflow.Indicator>
400408
</Overflow>

‎src/index.js

Lines changed: 61 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import PropTypes from 'prop-types';
99

1010
const Context = React.createContext();
1111

12+
export function useOverflow() {
13+
return useContext(Context);
14+
}
15+
1216
const containerStyle = {
1317
display: 'flex',
1418
flexDirection: 'column',
@@ -60,8 +64,6 @@ function getInitialState() {
6064
};
6165
}
6266

63-
const emptyStyle = {};
64-
6567
/**
6668
* The overflow state provider. At a minimum it must contain an
6769
* `<Overflow.Content>` element, otherwise it will do nothing.
@@ -99,12 +101,13 @@ const emptyStyle = {};
99101
export default function Overflow({
100102
children,
101103
onStateChange,
104+
style: styleProp,
102105
tolerance = 0,
103106
...rest
104107
}) {
105108
const [state, dispatch] = useReducer(reducer, null, getInitialState);
106109
const hidden = rest.hidden;
107-
const styleProp = rest.style||emptyStyle;
110+
const viewportRef = useRef();
108111

109112
const style = useMemo(
110113
() => ({
@@ -116,27 +119,33 @@ export default function Overflow({
116119
// `display: none` and allow that, otherwise ensure we use the value from
117120
// `containerStyle`.
118121
display:
119-
hidden || styleProp.display === 'none' ? 'none' : containerStyle.display
122+
hidden || (styleProp && styleProp.display === 'none')
123+
? 'none'
124+
: containerStyle.display
120125
}),
121126
[hidden, styleProp]
122127
);
123128

124-
const context = useMemo(() => {
125-
return {
129+
const refs = useMemo(() => ({ viewport: viewportRef }), []);
130+
131+
const context = useMemo(
132+
() => ({
126133
state,
127134
dispatch,
128-
tolerance
129-
};
130-
}, [state, tolerance]);
135+
tolerance,
136+
refs
137+
}),
138+
[refs, state, tolerance]
139+
);
131140

132141
useEffect(() => {
133142
if (onStateChange) {
134-
onStateChange(state);
143+
onStateChange(state,refs);
135144
}
136-
}, [onStateChange, state]);
145+
}, [onStateChange, refs,state]);
137146

138147
return (
139-
<div data-overflow-wrapper="" {...rest}style={style}>
148+
<div data-overflow-wrapper="" style={style}{...rest}>
140149
<Context.Provider value={context}>{children}</Context.Provider>
141150
</div>
142151
);
@@ -150,8 +159,8 @@ Overflow.propTypes = {
150159
*/
151160
children: PropTypes.node,
152161
/**
153-
* Callback that receives the latest overflow state, if you’d like to react
154-
* to scrollability in a custom way.
162+
* Callback that receives the latest overflow state and an object of refs, if
163+
* you’d like to react to overflow in a custom way.
155164
*/
156165
onStateChange: PropTypes.func,
157166
/**
@@ -176,8 +185,8 @@ Overflow.propTypes = {
176185
* interfering with the styles this component needs to function.
177186
*/
178187
function OverflowContent({ children, style: styleProp, ...rest }) {
179-
const { dispatch, tolerance} = useContext(Context);
180-
const rootRef=useRef();
188+
const { dispatch, tolerance, refs } = useOverflow();
189+
const {viewport: viewportRef}=refs;
181190
const contentRef = useRef();
182191
const toleranceRef = useRef();
183192
const watchRef = tolerance ? toleranceRef : contentRef;
@@ -186,7 +195,7 @@ function OverflowContent({ children, style: styleProp, ...rest }) {
186195
useEffect(() => {
187196
let ignore = false;
188197

189-
const root = rootRef.current;
198+
const root = viewportRef.current;
190199

191200
const createObserver = (direction, rootMargin) => {
192201
const threshold = 1e-12;
@@ -228,7 +237,7 @@ function OverflowContent({ children, style: styleProp, ...rest }) {
228237
observers.right.disconnect();
229238
observers.down.disconnect();
230239
};
231-
}, [dispatch]);
240+
}, [dispatch,viewportRef]);
232241

233242
useEffect(() => {
234243
const observers = observersRef.current;
@@ -254,25 +263,31 @@ function OverflowContent({ children, style: styleProp, ...rest }) {
254263
};
255264
}, [styleProp]);
256265

266+
const toleranceElement = useMemo(
267+
() =>
268+
tolerance ? (
269+
<div
270+
data-overflow-tolerance
271+
ref={toleranceRef}
272+
style={{
273+
position: 'absolute',
274+
top: tolerance,
275+
left: tolerance,
276+
right: tolerance,
277+
bottom: tolerance,
278+
background: 'transparent',
279+
pointerEvents: 'none',
280+
zIndex: -1
281+
}}
282+
/>
283+
) : null,
284+
[tolerance]
285+
);
286+
257287
return (
258-
<div ref={rootRef} data-overflow-viewport="" style={viewportStyle}>
288+
<div ref={viewportRef} data-overflow-viewport="" style={viewportStyle}>
259289
<div ref={contentRef} data-overflow-content="" style={style} {...rest}>
260-
{tolerance ? (
261-
<div
262-
data-overflow-tolerance
263-
ref={toleranceRef}
264-
style={{
265-
position: 'absolute',
266-
top: tolerance,
267-
left: tolerance,
268-
right: tolerance,
269-
bottom: tolerance,
270-
background: 'transparent',
271-
pointerEvents: 'none',
272-
zIndex: -1
273-
}}
274-
/>
275-
) : null}
290+
{toleranceElement}
276291
{children}
277292
</div>
278293
</div>
@@ -330,18 +345,18 @@ OverflowContent.propTypes = {
330345
* ```
331346
*/
332347
function OverflowIndicator({ children, direction }) {
333-
const context=useContext(Context);
334-
const { canScroll: state} = context.state;
335-
const canScroll = direction
336-
? state[direction]
337-
: state.up || state.left || state.right || state.down;
348+
const { state, refs }=useOverflow();
349+
const { canScroll} = state;
350+
const isActive = direction
351+
? canScroll[direction]
352+
: canScroll.up || canScroll.left || canScroll.right || canScroll.down;
338353

339-
let shouldRender = canScroll;
354+
let shouldRender = isActive;
340355

341356
if (typeof children === 'function') {
342-
const arg = direction ? canScroll : state;
343357
shouldRender = true;
344-
children = children(arg);
358+
const stateArg = direction ? isActive : canScroll;
359+
children = children(stateArg, refs);
345360
}
346361

347362
return shouldRender ? <>{children}</> : null;
@@ -352,8 +367,9 @@ OverflowIndicator.displayName = 'Overflow.Indicator';
352367
OverflowIndicator.propTypes = {
353368
/**
354369
* Indicator to render when scrolling is allowed in the requested direction.
355-
* If given a function, it will be passed the overflow state and its result
356-
* will be rendered.
370+
* If given a function, it will be passed the overflow state and and object
371+
* containing the `viewport` ref (you can use the `refs` parameter to render
372+
* an indicator that is also a button that scrolls the viewport).
357373
*/
358374
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
359375
/**

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /