text: import React, { PureComponent } from 'react';
  
  import {
    Animated,
    Image,
    StyleSheet,
    PanResponder,
    View,
    Easing,
    ViewPropTypes,
    I18nManager
  } from 'react-native';
  
  import PropTypes from 'prop-types';
  
  const TRACK_SIZE = 40;
  const THUMB_SIZE = 40;
  
  const DEFAULT_ANIMATION_CONFIGS = {
    spring: {
      friction: 7,
      tension: 100
    },
    timing: {
      duration: 150,
      easing: Easing.inOut(Easing.ease),
      delay: 0
    }
  };
  
  export class Slider extends PureComponent {
    static propTypes = {
      value: PropTypes.number,
      disabled: PropTypes.bool,
      minimumValue: PropTypes.number,
      maximumValue: PropTypes.number,
      step: PropTypes.number,
      minimumTrackTintColor: PropTypes.string,
      maximumTrackTintColor: PropTypes.string,
      thumbTintColor: PropTypes.string,
      thumbTouchSize: PropTypes.shape({
        width: PropTypes.number,
        height: PropTypes.number
      }),
      onValueChange: PropTypes.func,
      onSlidingStart: PropTypes.func,
      onSlidingComplete: PropTypes.func,
      style: ViewPropTypes.style,
      trackStyle: ViewPropTypes.style,
      thumbStyle: ViewPropTypes.style,
      thumbImage: Image.propTypes.source,
      debugTouchArea: PropTypes.bool,
      animateTransitions: PropTypes.bool,
      animationType: PropTypes.oneOf(['spring', 'timing']),
      animationConfig: PropTypes.object
    };
  
    static defaultProps = {
      value: 0,
      minimumValue: 0,
      maximumValue: 100,
      step: 0,
      minimumTrackTintColor: '#3f3f3f',
      maximumTrackTintColor: '#b3b3b3',
      thumbTintColor: '#343434',
      thumbTouchSize: { width: 40, height: 40 },
      debugTouchArea: false,
      animationType: 'timing'
    };
  
    state = {
      containerSize: { width: 0, height: 0 },
      trackSize: { width: 0, height: 0 },
      thumbSize: { width: 0, height: 0 },
      allMeasured: false,
      value: new Animated.Value(this.props.value)
    };
  
    componentWillMount() {
      this._panResponder = PanResponder.create({
        onStartShouldSetPanResponder: this._handleStartShouldSetPanResponder,
        onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder,
        onPanResponderGrant: this._handlePanResponderGrant,
        onPanResponderMove: this._handlePanResponderMove,
        onPanResponderRelease: this._handlePanResponderEnd,
        onPanResponderTerminationRequest: this._handlePanResponderRequestEnd,
        onPanResponderTerminate: this._handlePanResponderEnd
      });
    }
  
    componentWillReceiveProps(nextProps) {
      const newValue = nextProps.value;
  
      if (this.props.value !== newValue) {
        if (this.props.animateTransitions) {
          this._setCurrentValueAnimated(newValue);
        } else {
          this._setCurrentValue(newValue);
        }
      }
    }
  
    render() {
      const {
        minimumValue,
        maximumValue,
        minimumTrackTintColor,
        maximumTrackTintColor,
        thumbTintColor,
        thumbImage,
        styles,
        style,
        trackStyle,
        thumbStyle,
        debugTouchArea,
        onValueChange,
        thumbTouchSize,
        animationType,
        animateTransitions,
        ...other
      } = this.props;
      const { value, containerSize, thumbSize, allMeasured } = this.state;
      const mainStyles = styles || defaultStyles;
      const thumbLeft = value.interpolate({
        inputRange: [minimumValue, maximumValue],
        outputRange: I18nManager.isRTL
          ? [0, -(containerSize.width - thumbSize.width)]
          : [0, containerSize.width - thumbSize.width]
      });
      const minimumTrackWidth = value.interpolate({
        inputRange: [minimumValue, maximumValue],
        outputRange: [0, containerSize.width - thumbSize.width]
      });
      const valueVisibleStyle = {};
      if (!allMeasured) {
        valueVisibleStyle.opacity = 0;
      }
  
      const minimumTrackStyle = {
        position: 'absolute',
        width: Animated.add(minimumTrackWidth, thumbSize.width / 2),
        backgroundColor: minimumTrackTintColor,
        ...valueVisibleStyle
      };
  
      return (
        <View
          {...other}
          style={[mainStyles.container, style]}
          onLayout={this._measureContainer}
        >
          <View
            style={[
              { backgroundColor: maximumTrackTintColor },
              mainStyles.track,
              trackStyle
            ]}
            renderToHardwareTextureAndroid
            onLayout={this._measureTrack}
          />
          <Animated.View
            renderToHardwareTextureAndroid
            style={[mainStyles.track, trackStyle, minimumTrackStyle]}
          />
          <Animated.View
            onLayout={this._measureThumb}
            renderToHardwareTextureAndroid
            style={[
              { backgroundColor: thumbTintColor },
              mainStyles.thumb,
              thumbStyle,
              {
                transform: [{ translateX: thumbLeft }, { translateY: 0 }],
                ...valueVisibleStyle
              }
            ]}
            {...this._panResponder.panHandlers}
          >
            {this._renderThumbImage()}
            {debugTouchArea === true &&
              this._renderDebugThumbTouchRect(minimumTrackWidth)}
          </Animated.View>
        </View>
      );
    }
  
    _getPropsForComponentUpdate(props) {
      const {
        value,
        onValueChange,
        onSlidingStart,
        onSlidingComplete,
        style,
        trackStyle,
        thumbStyle,
        ...otherProps
      } = props;
  
      return otherProps;
    }
  
    _handleStartShouldSetPanResponder() {
      return true;
    }
  
    _handleMoveShouldSetPanResponder() {
      return false;
    }
  
    _handlePanResponderGrant = () => {
      this._previousLeft = this._getThumbLeft(this._getCurrentValue());
      this._fireChangeEvent('onSlidingStart');
    };
  
    _handlePanResponderMove = (e, gestureState) => {
      if (this.props.disabled) {
        return;
      }
  
      this._setCurrentValue(this._getValue(gestureState));
      this._fireChangeEvent('onValueChange');
    };
  
    _handlePanResponderRequestEnd(e, gestureState) {
      return false;
    }
  
    _handlePanResponderEnd = (e, gestureState) => {
      if (this.props.disabled) {
        return;
      }
  
      this._setCurrentValue(this._getValue(gestureState));
      this._fireChangeEvent('onSlidingComplete');
    };
  
    _measureContainer = (x) => {
      this._handleMeasure('containerSize', x);
    };
  
    _measureTrack = (x) => {
      this._handleMeasure('trackSize', x);
    };
  
    _measureThumb = (x) => {
      this._handleMeasure('thumbSize', x);
    };
  
    _handleMeasure = (name, x) => {
      const { width, height } = x.nativeEvent.layout;
      const size = { width, height };
  
      const storeName = `_${name}`;
      const currentSize = this[storeName];
      if (
        currentSize &&
        width === currentSize.width &&
        height === currentSize.height
      ) {
        return;
      }
      this[storeName] = size;
  
      if (this._containerSize && this._trackSize && this._thumbSize) {
        this.setState({
          containerSize: this._containerSize,
          trackSize: this._trackSize,
          thumbSize: this._thumbSize,
          allMeasured: true
        });
      }
    };
  
    _getRatio = (value) =>
      (value - this.props.minimumValue) /
      (this.props.maximumValue - this.props.minimumValue);
  
    _getThumbLeft = (value) => {
      const nonRtlRatio = this._getRatio(value);
      const ratio = I18nManager.isRTL ? 1 - nonRtlRatio : nonRtlRatio;
      return (
        ratio * (this.state.containerSize.width - this.state.thumbSize.width)
      );
    };
  
    _getValue = (gestureState) => {
      const length = this.state.containerSize.width - this.state.thumbSize.width;
      const thumbLeft = this._previousLeft + gestureState.dx;
  
      const nonRtlRatio = thumbLeft / length;
      const ratio = I18nManager.isRTL ? 1 - nonRtlRatio : nonRtlRatio;
  
      if (this.props.step) {
        return Math.max(
          this.props.minimumValue,
          Math.min(
            this.props.maximumValue,
            this.props.minimumValue +
              Math.round(
                (ratio * (this.props.maximumValue - this.props.minimumValue)) /
                  this.props.step
              ) *
                this.props.step
          )
        );
      }
      return Math.max(
        this.props.minimumValue,
        Math.min(
          this.props.maximumValue,
          ratio * (this.props.maximumValue - this.props.minimumValue) +
            this.props.minimumValue
        )
      );
    };
  
    _getCurrentValue = () => this.state.value.__getValue();
  
    _setCurrentValue = (value) => {
      this.state.value.setValue(value);
    };
  
    _setCurrentValueAnimated = (value) => {
      const animationType = this.props.animationType;
      const animationConfig = Object.assign(
        {},
        DEFAULT_ANIMATION_CONFIGS[animationType],
        this.props.animationConfig,
        {
          toValue: value
        }
      );
  
      Animated[animationType](this.state.value, animationConfig).start();
    };
  
    _fireChangeEvent = (event) => {
      if (this.props[event]) {
        this.props[event](this._getCurrentValue());
      }
    };
  
    _renderDebugThumbTouchRect = () => {
      return (
        <View style={[defaultStyles.debugThumbTouchArea]} pointerEvents="none" />
      );
    };
  
    _renderThumbImage = () => {
      const { thumbImage } = this.props;
  
      if (!thumbImage) return;
  
      return <Image source={thumbImage} />;
    };
  }
  
  var defaultStyles = StyleSheet.create({
    container: {
      height: 40,
      justifyContent: 'center'
    },
    track: {
      height: TRACK_SIZE,
      borderRadius: TRACK_SIZE / 2
    },
    thumb: {
      position: 'absolute',
      width: THUMB_SIZE,
      height: THUMB_SIZE,
      borderRadius: THUMB_SIZE / 2
    },
    debugThumbTouchArea: {
      width: '100%',
      height: '100%',
      backgroundColor: 'green',
      opacity: 0.5
    }
  });
  