1.0.5 • Published 5 years ago

@cawfree/react-native-elastic-footer v1.0.5

Weekly downloads
-
License
MIT
Repository
github
Last release
5 years ago

react-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

NameTypeRequiredDefault ValueDescription
onHandleMixinsfuncfalseonScroll => nullSupplies a callback method which needs to be dispatched to the enclosing 's onScroll prop.
maxHeightnumberfalse100Defines the size of the ElasticFooter when fully opened.
refreshingboolfalsefalseDefines whether the ElasticFooter is expanded.
onRefreshfuncfalse() => nullCalled when the user has dragged the ElasticFooter to the maxHeight, signifying that they wish to refresh.
onCancelfuncfalse() => nullCalled when the user has began to expand the ElasticFooter but bailed out before reaching the maxHeight.
durationnumberfalse300The duration for open/close animations.
debouncenumberfalse120The amount of time to wait before processing scroll events. Warning: You must choose a debounce value which is sufficient for your animation duration.
childrenfunctionfalse({ 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

MIT

1.0.5

5 years ago

1.0.4

5 years ago

1.0.3

5 years ago

1.0.2

5 years ago

1.0.1

5 years ago

1.0.0

5 years ago