Skip to content

Commit 929af3c

Browse files
cleanup
1 parent 7be0d40 commit 929af3c

File tree

1 file changed

+163
-131
lines changed

1 file changed

+163
-131
lines changed

packages/react-native/Libraries/StyleSheet/processBackgroundImage.js

Lines changed: 163 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ const processColor = require('./processColor').default;
1717
// Linear Gradient
1818
const LINEAR_GRADIENT_DIRECTION_REGEX =
1919
/^to\s+(?:top|bottom|left|right)(?:\s+(?:top|bottom|left|right))?/i;
20-
const LINEAR_GRADIENT_ANGLE_UNIT_REGEX = /^([+-]?\d*\.?\d+)(deg|grad|rad|turn)$/i;
20+
const LINEAR_GRADIENT_ANGLE_UNIT_REGEX =
21+
/^([+-]?\d*\.?\d+)(deg|grad|rad|turn)$/i;
2122
const LINEAR_GRADIENT_DEFAULT_DIRECTION: LinearGradientDirection = {
2223
type: 'angle',
2324
value: 180,
@@ -34,13 +35,16 @@ type LinearGradientBackgroundImage = {
3435
color: ColorStopColor,
3536
position: ColorStopPosition,
3637
}>,
37-
}
38+
};
3839

3940
// Radial Gradient
4041
const RADIAL_SHAPE_REGEX = /^(circle|ellipse)(?:\s|$)/i;
41-
const RADIAL_SIZE_REGEX = /^(closest-side|closest-corner|farthest-side|farthest-corner|([+-]?\d*\.?\d+)(px|%)?)/i;
42-
const RADIAL_POSITION_REGEX = /^(?:at\s+)?(center|top|right|bottom|left|([+-]?\d*\.?\d+)(px|%)?)(?:\s+(center|top|right|bottom|left|([+-]?\d*\.?\d+)(px|%)?))?/i;
43-
type RadialExtent = 'closest-corner | closest-side | farthest-corner | farthest-side'
42+
const RADIAL_SIZE_REGEX =
43+
/^(closest-side|closest-corner|farthest-side|farthest-corner|([+-]?\d*\.?\d+)(px|%)?)/i;
44+
const RADIAL_POSITION_REGEX =
45+
/^(?:at\s+)?(center|top|right|bottom|left|([+-]?\d*\.?\d+)(px|%)?)(?:\s+(center|top|right|bottom|left|([+-]?\d*\.?\d+)(px|%)?))?/i;
46+
type RadialExtent =
47+
'closest-corner | closest-side | farthest-corner | farthest-side';
4448
type RadialGradientBackgroundImage = {
4549
type: 'radialGradient',
4650
shape: 'circle' | 'ellipse',
@@ -49,16 +53,17 @@ type RadialGradientBackgroundImage = {
4953
colorStops: $ReadOnlyArray<{
5054
color: ColorStopColor,
5155
position: ColorStopPosition,
52-
}>
53-
}
56+
}>,
57+
};
5458

5559
// null color indicate that the transition hint syntax is used. e.g. red, 20%, blue
5660
type ColorStopColor = ProcessedColorValue | null;
5761
// percentage or pixel value
5862
type ColorStopPosition = number | string | null;
5963

60-
type ParsedBackgroundImageValue = LinearGradientBackgroundImage | RadialGradientBackgroundImage;
61-
64+
type ParsedBackgroundImageValue =
65+
| LinearGradientBackgroundImage
66+
| RadialGradientBackgroundImage;
6267

6368
export default function processBackgroundImage(
6469
backgroundImage: ?($ReadOnlyArray<BackgroundImageValue> | string),
@@ -69,7 +74,7 @@ export default function processBackgroundImage(
6974
}
7075

7176
if (typeof backgroundImage === 'string') {
72-
result = parseCSSLinearGradient(backgroundImage.replace(/\n/g, ' '));
77+
result = parseBackgroundImageCSSString(backgroundImage.replace(/\n/g, ' '));
7378
} else if (Array.isArray(backgroundImage)) {
7479
for (const bgImage of backgroundImage) {
7580
const processedColorStops: Array<{
@@ -128,7 +133,8 @@ export default function processBackgroundImage(
128133
}
129134
}
130135

131-
let direction: LinearGradientDirection = LINEAR_GRADIENT_DEFAULT_DIRECTION;
136+
let direction: LinearGradientDirection =
137+
LINEAR_GRADIENT_DEFAULT_DIRECTION;
132138
const bgDirection =
133139
bgImage.direction != null ? bgImage.direction.toLowerCase() : null;
134140

@@ -169,147 +175,173 @@ export default function processBackgroundImage(
169175
return result;
170176
}
171177

172-
function parseCSSLinearGradient(
178+
function parseBackgroundImageCSSString(
173179
cssString: string,
174180
): $ReadOnlyArray<ParsedBackgroundImageValue> {
175181
const gradients = [];
176182
let match;
183+
// matches one or more linear-gradient or radial-gradient functions in CSS
184+
const gradientRegex =
185+
/(linear|radial)-gradient\s*\(((?:\([^)]*\)|[^()])*)\)/gi;
177186

178-
// matches one or more linear-gradient functions in CSS
179-
const linearGradientRegex = /linear-gradient\s*\(((?:\([^)]*\)|[^())])*)\)/gi;
180-
181-
while ((match = linearGradientRegex.exec(cssString))) {
182-
const gradientContent = match[1];
183-
const parts = gradientContent.split(',');
184-
let direction: LinearGradientDirection = LINEAR_GRADIENT_DEFAULT_DIRECTION;
185-
const trimmedDirection = parts[0].trim().toLowerCase();
186-
187-
if (LINEAR_GRADIENT_ANGLE_UNIT_REGEX.test(trimmedDirection)) {
188-
const parsedAngle = getAngleInDegrees(trimmedDirection);
189-
if (parsedAngle != null) {
190-
direction = {
191-
type: 'angle',
192-
value: parsedAngle,
193-
};
194-
parts.shift();
195-
} else {
196-
// If an angle is invalid, return an empty array and do not apply any gradient. Same as web.
197-
return [];
198-
}
199-
} else if (LINEAR_GRADIENT_DIRECTION_REGEX.test(trimmedDirection)) {
200-
const parsedDirection = getDirectionForKeyword(trimmedDirection);
201-
if (parsedDirection != null) {
202-
direction = parsedDirection;
203-
parts.shift();
204-
} else {
205-
// If a direction is invalid, return an empty array and do not apply any gradient. Same as web.
206-
return [];
207-
}
187+
while ((match = gradientRegex.exec(cssString))) {
188+
const [, type, gradientContent] = match;
189+
const isRadial = type.toLowerCase() === 'radial';
190+
191+
const gradient = isRadial
192+
? parseRadialGradientCSSString(gradientContent)
193+
: parseLinearGradientCSSString(gradientContent);
194+
195+
if (gradient != null) {
196+
gradients.push(gradient);
208197
}
198+
}
209199

210-
const colorStopsString = parts.join(',');
211-
const colorStops = [];
212-
// split by comma, but not if it's inside a parentheses. e.g. red, rgba(0, 0, 0, 0.5), green => ["red", "rgba(0, 0, 0, 0.5)", "green"]
213-
const stops = colorStopsString.split(/,(?![^(]*\))/);
214-
let prevStop = null;
215-
for (let i = 0; i < stops.length; i++) {
216-
const stop = stops[i];
217-
const trimmedStop = stop.trim().toLowerCase();
218-
// Match function like pattern or single words
219-
const colorStopParts = trimmedStop.match(/\S+\([^)]*\)|\S+/g);
220-
if (colorStopParts == null) {
221-
// If a color stop is invalid, return an empty array and do not apply any gradient. Same as web.
222-
return [];
200+
return gradients;
201+
}
202+
203+
function parseRadialGradientCSSString(
204+
cssString: string,
205+
): RadialGradientBackgroundImage {
206+
return {
207+
type: 'radialGradient',
208+
shape: 'circle',
209+
size: 'closest-corner',
210+
position: 'center',
211+
colorStops: [],
212+
};
213+
}
214+
215+
function parseLinearGradientCSSString(
216+
gradientContent: string,
217+
): LinearGradientBackgroundImage | null {
218+
const parts = gradientContent.split(',');
219+
let direction: LinearGradientDirection = LINEAR_GRADIENT_DEFAULT_DIRECTION;
220+
const trimmedDirection = parts[0].trim().toLowerCase();
221+
222+
if (LINEAR_GRADIENT_ANGLE_UNIT_REGEX.test(trimmedDirection)) {
223+
const parsedAngle = getAngleInDegrees(trimmedDirection);
224+
if (parsedAngle != null) {
225+
direction = {
226+
type: 'angle',
227+
value: parsedAngle,
228+
};
229+
parts.shift();
230+
} else {
231+
// If an angle is invalid, return null and do not apply any gradient. Same as web.
232+
return null;
233+
}
234+
} else if (LINEAR_GRADIENT_DIRECTION_REGEX.test(trimmedDirection)) {
235+
const parsedDirection = getDirectionForKeyword(trimmedDirection);
236+
if (parsedDirection != null) {
237+
direction = parsedDirection;
238+
parts.shift();
239+
} else {
240+
// If a direction is invalid, return null and do not apply any gradient. Same as web.
241+
return null;
242+
}
243+
}
244+
245+
const colorStopsString = parts.join(',');
246+
const colorStops = [];
247+
// split by comma, but not if it's inside a parentheses. e.g. red, rgba(0, 0, 0, 0.5), green => ["red", "rgba(0, 0, 0, 0.5)", "green"]
248+
const stops = colorStopsString.split(/,(?![^(]*\))/);
249+
let prevStop = null;
250+
for (let i = 0; i < stops.length; i++) {
251+
const stop = stops[i];
252+
const trimmedStop = stop.trim().toLowerCase();
253+
// Match function like pattern or single words
254+
const colorStopParts = trimmedStop.match(/\S+\([^)]*\)|\S+/g);
255+
if (colorStopParts == null) {
256+
// If a color stop is invalid, return null and do not apply any gradient. Same as web.
257+
return null;
258+
}
259+
// Case 1: [color, position, position]
260+
if (colorStopParts.length === 3) {
261+
const color = colorStopParts[0];
262+
const position1 = getPositionFromCSSValue(colorStopParts[1]);
263+
const position2 = getPositionFromCSSValue(colorStopParts[2]);
264+
const processedColor = processColor(color);
265+
if (processedColor == null) {
266+
// If a color is invalid, return null and do not apply any gradient. Same as web.
267+
return null;
223268
}
224-
// Case 1: [color, position, position]
225-
if (colorStopParts.length === 3) {
226-
const color = colorStopParts[0];
227-
const position1 = getPositionFromCSSValue(colorStopParts[1]);
228-
const position2 = getPositionFromCSSValue(colorStopParts[2]);
229-
const processedColor = processColor(color);
230-
if (processedColor == null) {
231-
// If a color is invalid, return an empty array and do not apply any gradient. Same as web.
232-
return [];
233-
}
234269

235-
if (position1 == null || position2 == null) {
236-
// If a position is invalid, return an empty array and do not apply any gradient. Same as web.
237-
return [];
238-
}
270+
if (position1 == null || position2 == null) {
271+
// If a position is invalid, return null and do not apply any gradient. Same as web.
272+
return null;
273+
}
239274

275+
colorStops.push({
276+
color: processedColor,
277+
position: position1,
278+
});
279+
colorStops.push({
280+
color: processedColor,
281+
position: position2,
282+
});
283+
}
284+
// Case 2: [color, position]
285+
else if (colorStopParts.length === 2) {
286+
const color = colorStopParts[0];
287+
const position = getPositionFromCSSValue(colorStopParts[1]);
288+
const processedColor = processColor(color);
289+
if (processedColor == null) {
290+
// If a color is invalid, return null and do not apply any gradient. Same as web.
291+
return null;
292+
}
293+
if (position == null) {
294+
// If a position is invalid, return null and do not apply any gradient. Same as web.
295+
return null;
296+
}
297+
colorStops.push({
298+
color: processedColor,
299+
position,
300+
});
301+
}
302+
// Case 3: [color]
303+
// Case 4: [position] => transition hint syntax
304+
else if (colorStopParts.length === 1) {
305+
const position = getPositionFromCSSValue(colorStopParts[0]);
306+
if (position != null) {
307+
// handle invalid transition hint syntax. transition hint syntax must have color before and after the position. e.g. red, 20%, blue
308+
if (
309+
(prevStop != null &&
310+
prevStop.length === 1 &&
311+
getPositionFromCSSValue(prevStop[0]) != null) ||
312+
i === stops.length - 1 ||
313+
i === 0
314+
) {
315+
// If the last stop is a transition hint syntax, return null and do not apply any gradient. Same as web.
316+
return null;
317+
}
240318
colorStops.push({
241-
color: processedColor,
242-
position: position1,
243-
});
244-
colorStops.push({
245-
color: processedColor,
246-
position: position2,
319+
color: null,
320+
position,
247321
});
248-
}
249-
// Case 2: [color, position]
250-
else if (colorStopParts.length === 2) {
251-
const color = colorStopParts[0];
252-
const position = getPositionFromCSSValue(colorStopParts[1]);
253-
const processedColor = processColor(color);
322+
} else {
323+
const processedColor = processColor(colorStopParts[0]);
254324
if (processedColor == null) {
255-
// If a color is invalid, return an empty array and do not apply any gradient. Same as web.
256-
return [];
257-
}
258-
if (position == null) {
259-
// If a position is invalid, return an empty array and do not apply any gradient. Same as web.
260-
return [];
325+
// If a color is invalid, return null and do not apply any gradient. Same as web.
326+
return null;
261327
}
262328
colorStops.push({
263329
color: processedColor,
264-
position,
330+
position: null,
265331
});
266332
}
267-
// Case 3: [color]
268-
// Case 4: [position] => transition hint syntax
269-
else if (colorStopParts.length === 1) {
270-
const position = getPositionFromCSSValue(colorStopParts[0]);
271-
if (position != null) {
272-
// handle invalid transition hint syntax. transition hint syntax must have color before and after the position. e.g. red, 20%, blue
273-
if (
274-
(prevStop != null &&
275-
prevStop.length === 1 &&
276-
getPositionFromCSSValue(prevStop[0]) != null) ||
277-
i === stops.length - 1 ||
278-
i === 0
279-
) {
280-
// If the last stop is a transition hint syntax, return an empty array and do not apply any gradient. Same as web.
281-
return [];
282-
}
283-
colorStops.push({
284-
color: null,
285-
position,
286-
});
287-
} else {
288-
const processedColor = processColor(colorStopParts[0]);
289-
if (processedColor == null) {
290-
// If a color is invalid, return an empty array and do not apply any gradient. Same as web.
291-
return [];
292-
}
293-
colorStops.push({
294-
color: processedColor,
295-
position: null,
296-
});
297-
}
298-
} else {
299-
// If a color stop is invalid, return an empty array and do not apply any gradient. Same as web.
300-
return [];
301-
}
302-
prevStop = colorStopParts;
333+
} else {
334+
// If a color stop is invalid, return null and do not apply any gradient. Same as web.
335+
return null;
303336
}
304-
305-
gradients.push({
306-
type: 'linearGradient',
307-
direction,
308-
colorStops,
309-
});
337+
prevStop = colorStopParts;
310338
}
311339

312-
return gradients;
340+
return {
341+
type: 'linearGradient',
342+
direction,
343+
colorStops,
344+
};
313345
}
314346

315347
function getDirectionForKeyword(direction?: string): ?LinearGradientDirection {

0 commit comments

Comments
 (0)