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   * If not specified, the colors will be distributed evenly across the gradient.
37   *
38   * For example, `[0.5, 0.8]` would render:
39   * - the first color, solid, from the beginning of the gradient view to 50% through (the middle);
40   * - a gradient from the first color to the second from the 50% point to the 80% point; and
41   * - the second color, solid, from the 80% point to the end of the gradient view.
42   *
43   * > The color-stop locations must be ascending from least to greatest.
44   * @default []
45   */
46  locations?: number[] | null;
47  /**
48   * For example, `{ x: 0.1, y: 0.2 }` means that the gradient will start `10%` from the left and `20%` from the top.
49   *
50   * **On web**, this only changes the angle of the gradient because CSS gradients don't support changing the starting position.
51   * @default { x: 0.5, y: 0.0 }
52   */
53  start?: LinearGradientPoint | null;
54  /**
55   * For example, `{ x: 0.1, y: 0.2 }` means that the gradient will end `10%` from the left and `20%` from the bottom.
56   *
57   * **On web**, this only changes the angle of the gradient because CSS gradients don't support changing the end position.
58   * @default { x: 0.5, y: 1.0 }
59   */
60  end?: LinearGradientPoint | null;
61};
62
63/**
64 * Renders a native view that transitions between multiple colors in a linear direction.
65 */
66export class LinearGradient extends React.Component<LinearGradientProps> {
67  render() {
68    const { colors, locations, start, end, ...props } = this.props;
69    let resolvedLocations = locations;
70    if (locations && colors.length !== locations.length) {
71      console.warn('LinearGradient colors and locations props should be arrays of the same length');
72      resolvedLocations = locations.slice(0, colors.length);
73    }
74
75    return (
76      <NativeLinearGradient
77        {...props}
78        colors={Platform.select({
79          web: colors as any,
80          default: colors.map(processColor),
81        })}
82        locations={resolvedLocations}
83        startPoint={_normalizePoint(start)}
84        endPoint={_normalizePoint(end)}
85      />
86    );
87  }
88}
89
90function _normalizePoint(
91  point: LinearGradientPoint | null | undefined
92): NativeLinearGradientPoint | undefined {
93  if (!point) {
94    return undefined;
95  }
96
97  if (Array.isArray(point) && point.length !== 2) {
98    console.warn('start and end props for LinearGradient must be of the format [x,y] or {x, y}');
99    return undefined;
100  }
101
102  return Array.isArray(point) ? point : [point.x, point.y];
103}
104