Skip to content

Commit

Permalink
add a hide-on-scroll feature to ScrollView
Browse files Browse the repository at this point in the history
Summary:
Add a stickyHeaderHiddenOnScroll option to keep the sticky header hidden during scrolling down, and only slide in when scrolling up
Changelog:
[General][Added] - Add a stickyHeaderHiddenOnScroll option to keep the sticky header hidden during scrolling down, and only slide in when scrolling up

Reviewed By: JoshuaGross

Differential Revision: D26900810

fbshipit-source-id: 6bfb1a4da07fff0763223d60836df187f9d95dd6
  • Loading branch information
Yan Zhang authored and facebook-github-bot committed Mar 9, 2021
1 parent 311d2fb commit ffba25c
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 3 deletions.
6 changes: 6 additions & 0 deletions Libraries/Components/ScrollView/ScrollView.js
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,11 @@ export type Props = $ReadOnly<{|
* The default value is true.
*/
showsVerticalScrollIndicator?: ?boolean,
/**
* When true, Sticky header is hidden when scrolling down, and dock at the top
* when scrolling up
*/
stickyHeaderHiddenOnScroll?: ?boolean,
/**
* An array of child indices determining which children get docked to the
* top of the screen when scrolling. For example, passing
Expand Down Expand Up @@ -1642,6 +1647,7 @@ class ScrollView extends React.Component<Props, State> {
onLayout={event => this._onStickyHeaderLayout(index, event, key)}
scrollAnimatedValue={this._scrollAnimatedValue}
inverted={this.props.invertStickyHeaders}
hiddenOnScroll={this.props.stickyHeaderHiddenOnScroll}
scrollViewHeight={this.state.layoutHeight}>
{child}
</StickyHeaderComponent>
Expand Down
29 changes: 26 additions & 3 deletions Libraries/Components/ScrollView/ScrollViewStickyHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
*/

import AnimatedImplementation from '../../Animated/AnimatedImplementation';
import AnimatedAddition from '../../Animated/nodes/AnimatedAddition';
import AnimatedDiffClamp from '../../Animated/nodes/AnimatedDiffClamp';
import AnimatedNode from '../../Animated/nodes/AnimatedNode';

import * as React from 'react';
import StyleSheet from '../../StyleSheet/StyleSheet';
import View from '../View/View';
Expand All @@ -29,6 +33,7 @@ export type Props = {
// The height of the parent ScrollView. Currently only set when inverted.
scrollViewHeight: ?number,
nativeID?: ?string,
hiddenOnScroll?: ?boolean,
...
};

Expand All @@ -50,7 +55,7 @@ class ScrollViewStickyHeader extends React.Component<Props, State> {
translateY: null,
};

_translateY: ?AnimatedImplementation.Interpolation = null;
_translateY: ?AnimatedNode = null;
_shouldRecreateTranslateY: boolean = true;
_haveReceivedInitialZeroTranslateY: boolean = true;
_ref: any; // TODO T53738161: flow type this, and the whole file
Expand Down Expand Up @@ -87,12 +92,15 @@ class ScrollViewStickyHeader extends React.Component<Props, State> {
updateTranslateListener(
translateY: AnimatedImplementation.Interpolation,
isFabric: boolean,
offset: AnimatedDiffClamp | null,
) {
if (this._translateY != null && this._animatedValueListenerId != null) {
this._translateY.removeListener(this._animatedValueListenerId);
}
offset
? (this._translateY = new AnimatedAddition(translateY, offset))
: (this._translateY = translateY);

this._translateY = translateY;
this._shouldRecreateTranslateY = false;

if (!isFabric) {
Expand Down Expand Up @@ -178,7 +186,6 @@ class ScrollViewStickyHeader extends React.Component<Props, State> {
// eslint-disable-next-line dot-notation
(this._ref && this._ref['_internalInstanceHandle']?.stateNode?.canonical)
);

// Initially and in the case of updated props or layout, we
// recreate this interpolated value. Otherwise, we do not recreate
// when there are state changes.
Expand Down Expand Up @@ -259,6 +266,22 @@ class ScrollViewStickyHeader extends React.Component<Props, State> {
outputRange,
}),
isFabric,
this.props.hiddenOnScroll
? new AnimatedDiffClamp(
this.props.scrollAnimatedValue
.interpolate({
extrapolateLeft: 'clamp',
inputRange: [layoutY, layoutY + 1],
outputRange: ([0, 1]: Array<number>),
})
.interpolate({
inputRange: [0, 1],
outputRange: ([0, -1]: Array<number>),
}),
-this.state.layoutHeight,
0,
)
: null,
);
}

Expand Down

0 comments on commit ffba25c

Please sign in to comment.