1import * as React from 'react';
2import { Platform, processColor, ViewProps } from 'react-native';
3
4import NativeLinearGradient from './NativeLinearGradient';
5import { NativeLinearGradientPoint } from './NativeLinearGradient.types';
6
7// @needsAudit
8/**
9 * An object `{ x: number; y: number }` or array `[x, y]` that represents the point
10 * at which the gradient starts or ends, as a fraction of the overall size of the gradient ranging
11 * from `0` to `1`, inclusive.
12 */
13export type LinearGradientPoint =
14  | {
15      /**
16       * A number ranging from `0` to `1`, representing the position of gradient transformation.
17       */
18      x: number;
19      /**
20       * A number ranging from `0` to `1`, representing the position of gradient transformation.
21       */
22      y: number;
23    }
24  | NativeLinearGradientPoint;
25
26// @needsAudit
27export type LinearGradientProps = ViewProps & {
28  /**
29   * An array of colors that represent stops in the gradient. At least two colors are required
30   * (for a single-color background, use the `style.backgroundColor` prop on a `View` component).
31   */
32  colors: string[];
33  /**
34   * An array that contains `number`s ranging from `0` to `1`, inclusive, and is the same length as the `colors` property.
35   * Each number indicates a color-stop location where each respective color should be located.
36   *
37   * For example, `[0.5, 0.8]` would render:
38   * - the first color, solid, from the beginning of the gradient view to 50% through (the middle);
39   * - a gradient from the first color to the second from the 50% point to the 80% point; and
40   * - the second color, solid, from the 80% point to the end of the gradient view.
41   *
42   * > The color-stop locations must be ascending from least to greatest.
43   */
44  locations?: number[] | null;
45  /**
46   * For example, `{ x: 0.1, y: 0.2 }` means that the gradient will start `10%` from the left and `20%` from the top.
47   *
48   * **On web**, this only changes the angle of the gradient because CSS gradients don't support changing the starting position.
49   */
50  start?: LinearGradientPoint | null;
51  /**
52   * For example, `{ x: 0.1, y: 0.2 }` means that the gradient will end `10%` from the left and `20%` from the bottom.
53   *
54   * **On web**, this only changes the angle of the gradient because CSS gradients don't support changing the end position.
55   */
56  end?: LinearGradientPoint | null;
57};
58
59/**
60 * Renders a native view that transitions between multiple colors in a linear direction.
61 */
62export class LinearGradient extends React.Component<LinearGradientProps> {
63  render() {
64    const { colors, locations, start, end, ...props } = this.props;
65    let resolvedLocations = locations;
66    if (locations && colors.length !== locations.length) {
67      console.warn('LinearGradient colors and locations props should be arrays of the same length');
68      resolvedLocations = locations.slice(0, colors.length);
69    }
70
71    return (
72      <NativeLinearGradient
73        {...props}
74        colors={Platform.select({
75          web: colors as any,
76          default: colors.map(processColor),
77        })}
78        locations={resolvedLocations}
79        startPoint={_normalizePoint(start)}
80        endPoint={_normalizePoint(end)}
81      />
82    );
83  }
84}
85
86function _normalizePoint(
87  point: LinearGradientPoint | null | undefined
88): NativeLinearGradientPoint | undefined {
89  if (!point) {
90    return undefined;
91  }
92
93  if (Array.isArray(point) && point.length !== 2) {
94    console.warn('start and end props for LinearGradient must be of the format [x,y] or {x, y}');
95    return undefined;
96  }
97
98  return Array.isArray(point) ? point : [point.x, point.y];
99}
100