1import React from 'react';
2import { StyleSheet, View } from 'react-native';
3
4type Props = {
5  colors: number[];
6  locations?: number[] | null;
7  startPoint?: Point | null;
8  endPoint?: Point | null;
9  onLayout?: Function;
10} & React.ComponentProps<typeof View>;
11
12type State = {
13  width?: number;
14  height?: number;
15};
16
17type Point = [number, number];
18
19export default class NativeLinearGradient extends React.PureComponent<Props, State> {
20  state = {
21    width: undefined,
22    height: undefined,
23  };
24
25  onLayout = event => {
26    this.setState({
27      width: event.nativeEvent.layout.width,
28      height: event.nativeEvent.layout.height,
29    });
30    if (this.props.onLayout) {
31      this.props.onLayout(event);
32    }
33  };
34
35  getAngle(): string {
36    const startPoint = this.props.startPoint ? this.props.startPoint : [0.5, 0.0];
37    const endPoint = this.props.endPoint ? this.props.endPoint : [0.5, 1.0];
38    const { width = 0, height = 0 } = this.state;
39    let angle = 0;
40
41    const gradientWidth = height * (endPoint[0] - startPoint[0]);
42    const gradientHeight = width * (endPoint[1] - startPoint[1]);
43    angle = Math.atan2(gradientHeight, gradientWidth) + Math.PI / 2;
44
45    return `${angle}rad`;
46  }
47
48  getColors(): string {
49    const { colors } = this.props;
50    return colors
51      .map((color, index) => {
52        const colorStr = `${color.toString(16)}`;
53        const hex = `#${colorStr.substring(2, colorStr.length)}`;
54
55        const location = this.props.locations && this.props.locations[index];
56        if (location) {
57          return `${hex} ${location * 100}%`;
58        }
59        return hex;
60      })
61      .join(',');
62  }
63
64  getBackgroundImage(): string | null {
65    if (this.state.width && this.state.height) {
66      return `linear-gradient(${this.getAngle()},${this.getColors()})`;
67    } else {
68      return 'transparent';
69    }
70  }
71
72  render() {
73    const { colors, locations, startPoint, endPoint, onLayout, style, ...props } = this.props;
74    let compiledStyle = StyleSheet.flatten(style) || {};
75
76    const flatStyle = {
77      ...compiledStyle,
78      // @ts-ignore: [ts] Property 'backgroundImage' does not exist on type 'ViewStyle'.
79      backgroundImage: this.getBackgroundImage(),
80    };
81    // TODO: Bacon: In the future we could consider adding `backgroundRepeat: "no-repeat"`. For more browser support.
82    return <View style={flatStyle} onLayout={this.onLayout} {...props} />;
83  }
84}
85