<commit_before>// @flow
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Animated, ScrollView, StyleSheet, View, Image, Dimensions } from 'react-native';
import type { ViewProps } from 'ViewPropTypes';
import type { FlatList, SectionList, ListView } from 'react-native';

type ScrollViewProps = {
  onScroll?: ?Function,
  style?: $PropertyType<ViewProps, 'style'>,
  contentContainerStyle?: $PropertyType<ViewProps, 'style'>,
  scrollEventThrottle?: number,
};

type SourceObjectProps = {
  uri?: ?string,
  bundle?: ?string,
  method?: ?string,
  headers?: ?{ [string]: string },
  body?: ?string,
  cache?: ?('default' | 'reload' | 'force-cache' | 'only-if-cached'),
  width?: ?number,
  height?: ?number,
  scale?: ?number,
};
type SourceProps = number | SourceObjectProps | SourceObjectProps[];

export type Props = ScrollViewProps & {
  children?: ?React$Element<any>,
  childrenStyle?: ?any,
  overlayColor: string,
  fadeOutForeground: boolean,
  foregroundParallaxRatio: number,
  maxHeight: number,
  maxOverlayOpacity: number,
  minHeight: number,
  minOverlayOpacity: number,
  renderFixedForeground: () => React$Element<any>,
  renderForeground?: () => React$Element<any>,
  renderHeader: () => React$Element<any>,
  renderTouchableFixedForeground?: ?() => React$Element<any>,
  ScrollViewComponent: React$ComponentType<ScrollViewProps>,
  scrollViewBackgroundColor: string,
  headerImage?: ?SourceProps,
};

export type DefaultProps = {
  overlayColor: string,
  fadeOutForeground: boolean,
  foregroundParallaxRatio: number,
  maxHeight: number,
  maxOverlayOpacity: number,
  minHeight: number,
  minOverlayOpacity: number,
  renderFixedForeground: () => React$Element<any>,
  renderHeader: () => React$Element<any>,
  ScrollViewComponent: React$ComponentType<ScrollViewProps>,
  scrollViewBackgroundColor: string,
};

export type State = {
  scrollY: Animated.Value,
  pageY: number,
};

type ScrollComponent<ItemT> = FlatList<ItemT> | SectionList<ItemT> | ListView | ScrollView;

class ImageHeaderScrollView extends Component<Props, State> {
  container: ?View; // @see https://github.com/facebook/react-native/issues/15955
  scrollViewRef: ?ScrollComponent<any>; // @see https://github.com/facebook/react-native/issues/15955
  state: State;

  static defaultProps: DefaultProps = {
    overlayColor: 'black',
    fadeOutForeground: false,
    foregroundParallaxRatio: 1,
    maxHeight: 125,
    maxOverlayOpacity: 0.3,
    minHeight: 80,
    minOverlayOpacity: 0,
    renderFixedForeground: () => <View />,
    renderHeader: () => <View />,
    ScrollViewComponent: ScrollView,
    scrollViewBackgroundColor: 'white',
  };

  constructor(props: Props) {
    super(props);
    this.state = {
      scrollY: new Animated.Value(0),
      pageY: 0,
    };
  }

  getChildContext() {
    return {
      scrollY: this.state.scrollY,
      scrollPageY: this.state.pageY + this.props.minHeight,
    };
  }

  interpolateOnImageHeight(outputRange: Array<number>) {
    const headerScrollDistance = this.props.maxHeight - this.props.minHeight;
    return this.state.scrollY.interpolate({
      inputRange: [0, headerScrollDistance],
      outputRange,
      extrapolate: 'clamp',
    });
  }

  renderHeaderProps() {
    if (this.props.headerImage) {
      return (
        <Image
          source={this.props.headerImage}
          style={{
            height: this.props.maxHeight,
            width: Dimensions.get('window').width,
          }}
        />
      );
    }
    return this.props.renderHeader();
  }

  renderHeader() {
    const overlayOpacity = this.interpolateOnImageHeight([
      this.props.minOverlayOpacity,
      this.props.maxOverlayOpacity,
    ]);

    const headerScale = this.state.scrollY.interpolate({
      inputRange: [-this.props.maxHeight, 0],
      outputRange: [3, 1],
      extrapolate: 'clamp',
    });

    const headerTransformStyle = {
      height: this.props.maxHeight,
      transform: [{ scale: headerScale }],
    };

    const overlayStyle = [
      styles.overlay,
      { opacity: overlayOpacity, backgroundColor: this.props.overlayColor },
    ];

    return (
      <Animated.View style={[styles.header, headerTransformStyle]}>
        {this.renderHeaderProps()}
        <Animated.View style={overlayStyle} />
        <View style={styles.fixedForeground}>{this.props.renderFixedForeground()}</View>
      </Animated.View>
    );
  }

  renderForeground() {
    const headerTranslate = this.state.scrollY.interpolate({
      inputRange: [0, this.props.maxHeight * 2],
      outputRange: [0, -this.props.maxHeight * 2 * this.props.foregroundParallaxRatio],
      extrapolate: 'clamp',
    });
    const opacity = this.interpolateOnImageHeight([1, -0.3]);

    const headerTransformStyle = {
      height: this.props.maxHeight,
      transform: [{ translateY: headerTranslate }],
      opacity: this.props.fadeOutForeground ? opacity : 1,
    };

    if (!this.props.renderForeground) {
      return <View />;
    }

    return (
      <Animated.View style={[styles.header, headerTransformStyle]}>
        {this.props.renderForeground()}
      </Animated.View>
    );
  }

  renderTouchableFixedForeground() {
    const height = this.interpolateOnImageHeight([this.props.maxHeight, this.props.minHeight]);

    if (!this.props.renderTouchableFixedForeground) {
      return <View />;
    }

    return (
      <Animated.View style={[styles.header, styles.touchableFixedForeground, { height }]}>
        {this.props.renderTouchableFixedForeground()}
      </Animated.View>
    );
  }

  onContainerLayout = () => {
    if (!this.container) {
      return;
    }
    this.container.measureInWindow((x, y) => {
			if (this.container) {
				this.setState(() => ({ pageY: y }))
			}
		});
  };

  onScroll = (e: *) => {
    if (this.props.onScroll) {
      this.props.onScroll(e);
    }
    const scrollY = e.nativeEvent.contentOffset.y;
    this.state.scrollY.setValue(scrollY);
  };

  render() {
    /* eslint-disable no-unused-vars */
    const {
      childrenStyle,
      overlayColor,
      fadeOutForeground,
      foregroundParallaxRatio,
      maxHeight,
      maxOverlayOpacity,
      minHeight,
      minOverlayOpacity,
      renderFixedForeground,
      renderForeground,
      renderHeader,
      renderTouchableFixedForeground,
      style,
      contentContainerStyle,
      onScroll,
      ScrollViewComponent,
      scrollViewBackgroundColor,<commit_msg>Change tabs to spaces to make CI happy<commit_after>// @flow
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Animated, ScrollView, StyleSheet, View, Image, Dimensions } from 'react-native';
import type { ViewProps } from 'ViewPropTypes';
import type { FlatList, SectionList, ListView } from 'react-native';

type ScrollViewProps = {
  onScroll?: ?Function,
  style?: $PropertyType<ViewProps, 'style'>,
  contentContainerStyle?: $PropertyType<ViewProps, 'style'>,
  scrollEventThrottle?: number,
};

type SourceObjectProps = {
  uri?: ?string,
  bundle?: ?string,
  method?: ?string,
  headers?: ?{ [string]: string },
  body?: ?string,
  cache?: ?('default' | 'reload' | 'force-cache' | 'only-if-cached'),
  width?: ?number,
  height?: ?number,
  scale?: ?number,
};
type SourceProps = number | SourceObjectProps | SourceObjectProps[];

export type Props = ScrollViewProps & {
  children?: ?React$Element<any>,
  childrenStyle?: ?any,
  overlayColor: string,
  fadeOutForeground: boolean,
  foregroundParallaxRatio: number,
  maxHeight: number,
  maxOverlayOpacity: number,
  minHeight: number,
  minOverlayOpacity: number,
  renderFixedForeground: () => React$Element<any>,
  renderForeground?: () => React$Element<any>,
  renderHeader: () => React$Element<any>,
  renderTouchableFixedForeground?: ?() => React$Element<any>,
  ScrollViewComponent: React$ComponentType<ScrollViewProps>,
  scrollViewBackgroundColor: string,
  headerImage?: ?SourceProps,
};

export type DefaultProps = {
  overlayColor: string,
  fadeOutForeground: boolean,
  foregroundParallaxRatio: number,
  maxHeight: number,
  maxOverlayOpacity: number,
  minHeight: number,
  minOverlayOpacity: number,
  renderFixedForeground: () => React$Element<any>,
  renderHeader: () => React$Element<any>,
  ScrollViewComponent: React$ComponentType<ScrollViewProps>,
  scrollViewBackgroundColor: string,
};

export type State = {
  scrollY: Animated.Value,
  pageY: number,
};

type ScrollComponent<ItemT> = FlatList<ItemT> | SectionList<ItemT> | ListView | ScrollView;

class ImageHeaderScrollView extends Component<Props, State> {
  container: ?View; // @see https://github.com/facebook/react-native/issues/15955
  scrollViewRef: ?ScrollComponent<any>; // @see https://github.com/facebook/react-native/issues/15955
  state: State;

  static defaultProps: DefaultProps = {
    overlayColor: 'black',
    fadeOutForeground: false,
    foregroundParallaxRatio: 1,
    maxHeight: 125,
    maxOverlayOpacity: 0.3,
    minHeight: 80,
    minOverlayOpacity: 0,
    renderFixedForeground: () => <View />,
    renderHeader: () => <View />,
    ScrollViewComponent: ScrollView,
    scrollViewBackgroundColor: 'white',
  };

  constructor(props: Props) {
    super(props);
    this.state = {
      scrollY: new Animated.Value(0),
      pageY: 0,
    };
  }

  getChildContext() {
    return {
      scrollY: this.state.scrollY,
      scrollPageY: this.state.pageY + this.props.minHeight,
    };
  }

  interpolateOnImageHeight(outputRange: Array<number>) {
    const headerScrollDistance = this.props.maxHeight - this.props.minHeight;
    return this.state.scrollY.interpolate({
      inputRange: [0, headerScrollDistance],
      outputRange,
      extrapolate: 'clamp',
    });
  }

  renderHeaderProps() {
    if (this.props.headerImage) {
      return (
        <Image
          source={this.props.headerImage}
          style={{
            height: this.props.maxHeight,
            width: Dimensions.get('window').width,
          }}
        />
      );
    }
    return this.props.renderHeader();
  }

  renderHeader() {
    const overlayOpacity = this.interpolateOnImageHeight([
      this.props.minOverlayOpacity,
      this.props.maxOverlayOpacity,
    ]);

    const headerScale = this.state.scrollY.interpolate({
      inputRange: [-this.props.maxHeight, 0],
      outputRange: [3, 1],
      extrapolate: 'clamp',
    });

    const headerTransformStyle = {
      height: this.props.maxHeight,
      transform: [{ scale: headerScale }],
    };

    const overlayStyle = [
      styles.overlay,
      { opacity: overlayOpacity, backgroundColor: this.props.overlayColor },
    ];

    return (
      <Animated.View style={[styles.header, headerTransformStyle]}>
        {this.renderHeaderProps()}
        <Animated.View style={overlayStyle} />
        <View style={styles.fixedForeground}>{this.props.renderFixedForeground()}</View>
      </Animated.View>
    );
  }

  renderForeground() {
    const headerTranslate = this.state.scrollY.interpolate({
      inputRange: [0, this.props.maxHeight * 2],
      outputRange: [0, -this.props.maxHeight * 2 * this.props.foregroundParallaxRatio],
      extrapolate: 'clamp',
    });
    const opacity = this.interpolateOnImageHeight([1, -0.3]);

    const headerTransformStyle = {
      height: this.props.maxHeight,
      transform: [{ translateY: headerTranslate }],
      opacity: this.props.fadeOutForeground ? opacity : 1,
    };

    if (!this.props.renderForeground) {
      return <View />;
    }

    return (
      <Animated.View style={[styles.header, headerTransformStyle]}>
        {this.props.renderForeground()}
      </Animated.View>
    );
  }

  renderTouchableFixedForeground() {
    const height = this.interpolateOnImageHeight([this.props.maxHeight, this.props.minHeight]);

    if (!this.props.renderTouchableFixedForeground) {
      return <View />;
    }

    return (
      <Animated.View style={[styles.header, styles.touchableFixedForeground, { height }]}>
        {this.props.renderTouchableFixedForeground()}
      </Animated.View>
    );
  }

  onContainerLayout = () => {
    if (!this.container) {
      return;
    }
    this.container.measureInWindow((x, y) => {
      if (this.container) {
        this.setState(() => ({ pageY: y }))
      }
    });
  };

  onScroll = (e: *) => {
    if (this.props.onScroll) {
      this.props.onScroll(e);
    }
    const scrollY = e.nativeEvent.contentOffset.y;
    this.state.scrollY.setValue(scrollY);
  };

  render() {
    /* eslint-disable no-unused-vars */
    const {
      childrenStyle,
      overlayColor,
      fadeOutForeground,
      foregroundParallaxRatio,
      maxHeight,
      maxOverlayOpacity,
      minHeight,
      minOverlayOpacity,
      renderFixedForeground,
      renderForeground,
      renderHeader,
      renderTouchableFixedForeground,
      style,
      contentContainerStyle,
      onScroll,
      ScrollViewComponent,
      scrollViewBackgroundColor,
{'fraction_non_alphanumeric': 0.13145105299699145, 'fraction_numerical': 0.0050914140245313586, 'mean_word_length': 3.350335570469799, 'pattern_counts': {'":': 0, '<': 65, '<?xml version=': 0, '>': 89, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2618334', 'n_tokens_mistral': 4070, 'n_tokens_neox': 3981, 'n_words': 742}