@cawfree/react-native-elastic-footer
v1.0.5react-native-elastic-footer
A React Native ScrollView footer with a little give.
Compatible with React Native Web.
🚀 Getting Started
Using npm:
npm install --save @cawfree/react-native-elastic-footer
Using yarn:
yarn add @cawfree/react-native-elastic-footer
✍️ Example
In the example below, we show how to use the <ElasticFooter/>
to drive a dynamic update on the enclosing <ScrollView/>
.
import React from 'react';
import {
Animated,
Image,
ScrollView,
Platform,
View,
StyleSheet,
} from 'react-native';
import ElasticFooter from '@cawfree/react-native-elastic-footer';
const styles = StyleSheet.create(
{
container: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
backgroundColor: 'black',
},
scrollView: {
flexDirection: 'row',
},
image: {
width: 300,
height: 350,
},
footer: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
overflow: 'hidden',
alignItems: 'center',
justifyContent: 'center',
},
icon: {
width: 70,
height: 70,
},
},
);
class App extends React.Component {
constructor(nextProps) {
super(nextProps);
this.__onHandleMixins = this.__onHandleMixins.bind(this);
this.__onRefresh = this.__onRefresh.bind(this);
this.__onCancel = this.__onCancel.bind(this);
this.state = {
onScroll: null,
refreshing: false,
items: [
{ key: 'https://magic.wizards.com/sites/mtg/files/images/card/en_5eDl5u25Hzm.png' },
{ key: 'https://magic.wizards.com/sites/mtg/files/images/card/en_WUdaayMnfL.png' },
{ key: 'https://magic.wizards.com/sites/mtg/files/images/card/QRay1SEQ9e.png' },
{ key: 'https://magic.wizards.com/sites/mtg/files/images/card/Tz0PAj1LTD_0.png' },
],
animRotate: new Animated.Value(0),
};
}
componentWillUpdate(nextProps, nextState) {
const {
refreshing,
animRotate,
} = nextState;
if (refreshing && !this.state.refreshing) {
animRotate.setValue(0);
Animated.timing(
animRotate,
{
toValue: 1,
duration: 1000,
useNativeDriver: true,
},
)
.start(() => console.log('fin'));
}
}
__onHandleMixins(onScroll) {
this.setState(
{
onScroll,
},
);
}
__onCancel(contentHeight) {
const { scrollView } = this.refs;
scrollView
.scrollTo(
{
x: 0,
y: contentHeight - 20,
animated: true,
},
);
this.setState(
{
refreshing: false,
},
);
}
__onRefresh(contentHeight) {
// XXX: You can just leave the ElasticFooter open...
return new Promise(resolve => this.setState({ refreshing: true }, resolve))
// XXX: Or you can fetch data and programmatically close it once you're done.
.then(() => new Promise(resolve => setTimeout(resolve, 1000)))
.then(() => new Promise(resolve => this.setState({
items: [
{ key: 'https://media.wizards.com/2018/images/magic/GRN/callout/en_Z6I4dSYzkH.png' },
...this.state.items,
],
refreshing: false,
}, resolve)))
.then(() => {
const { scrollView } = this.refs;
scrollView.scrollTo(
{
x: 0,
y: 0,
animated: true,
},
);
});
}
render() {
const {
onScroll,
refreshing,
items,
animRotate,
} = this.state;
return (
<View
style={styles.container}
>
<ScrollView
ref="scrollView"
onScroll={onScroll}
style={styles.scrollView}
scrollEventThrottle={0.1}
>
{items.map(({ key }) => (
<Image
key={key}
style={styles.image}
source={{ uri: key }}
/>
))}
<ElasticFooter
maxHeight={120}
onHandleMixins={this.__onHandleMixins}
onRefresh={this.__onRefresh}
onCancel={this.__onCancel}
refreshing={refreshing}
>
{({ animValue, refreshing }) => (
<Animated.View
style={[
styles.footer,
{
backgroundColor: '#000000',
},
]}
>
<Animated.Image
style={[
styles.icon,
{
opacity: Animated.add(0.5, animValue),
transform: [
{
rotate: animRotate.interpolate(
{
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
},
),
},
],
},
]}
source={{ uri: 'https://flaticons.net/gd/makefg.php?i=icons/Miscellaneous/Sword-03.png&r=255&g=255&b=255' }}
/>
</Animated.View>
)}
</ElasticFooter>
</ScrollView>
</View>
);
}
}
let hotWrapper = () => () => App;
if (Platform.OS === 'web') {
const { hot } = require('react-hot-loader');
hotWrapper = hot;
}
export default hotWrapper(module)(App);
📜 Prop Types
Name | Type | Required | Default Value | Description |
---|---|---|---|---|
onHandleMixins | func | false | onScroll => null | Supplies a callback method which needs to be dispatched to the enclosing |
maxHeight | number | false | 100 | Defines the size of the ElasticFooter when fully opened. |
refreshing | bool | false | false | Defines whether the ElasticFooter is expanded. |
onRefresh | func | false | () => null | Called when the user has dragged the ElasticFooter to the maxHeight, signifying that they wish to refresh. |
onCancel | func | false | () => null | Called when the user has began to expand the ElasticFooter but bailed out before reaching the maxHeight. |
duration | number | false | 300 | The duration for open/close animations. |
debounce | number | false | 120 | The amount of time to wait before processing scroll events. Warning: You must choose a debounce value which is sufficient for your animation duration. |
children | function | false | ({ animValue, refreshing }) => ... | A function which accepts the animated value animValue, which varies from 0 -> 1, and is an indicator of the percentage of expansion. |
✌️ License
npm i @cawfree/react-native-elastic-footer
Source Code
github.com/cawfree/react-nat...Metadata
- MIT
- Whatever
- @cawfree
- released 6/16/2019