From 140833161507a4772313c26f26cd722758a7a6f8 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Wed, 17 Jul 2024 15:51:15 -0700 Subject: [PATCH] JS plumbing to get boxShadow into native (#45452) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/45452 With this we enable to use BoxShadow. BoxShadow property can be a string as defined on MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/box-shadow Or it can also be a list of BoxShadow primitives: ``` [ { offsetX: 10, offsetY: 5, color: 'red', inset: true, }, { ... }, ] ``` The diff includes: * Style sheet changes so typing is valid * Process function to turn boxShadow format into parsed boxShadow primitive * Test for process function * View config changes on Android, iOS and ReactNativeStyleAttributes Changelog: [Internal] Reviewed By: joevilches Differential Revision: D57872933 --- .../View/ReactNativeStyleAttributes.js | 6 + .../NativeComponent/BaseViewConfig.android.js | 3 + .../NativeComponent/BaseViewConfig.ios.js | 3 + .../Libraries/StyleSheet/StyleSheetTypes.d.ts | 65 +++-- .../Libraries/StyleSheet/StyleSheetTypes.js | 56 ++-- .../__tests__/processBoxShadow-test.js | 269 ++++++++++++++++++ .../Libraries/StyleSheet/processBoxShadow.js | 208 ++++++++++++++ .../__snapshots__/public-api-test.js.snap | 67 +++-- packages/react-native/types/experimental.d.ts | 13 +- 9 files changed, 622 insertions(+), 68 deletions(-) create mode 100644 packages/react-native/Libraries/StyleSheet/__tests__/processBoxShadow-test.js create mode 100644 packages/react-native/Libraries/StyleSheet/processBoxShadow.js diff --git a/packages/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js b/packages/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js index 1fda63d5945b..bbbe3c18998c 100644 --- a/packages/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js +++ b/packages/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js @@ -11,6 +11,7 @@ import type {AnyAttributeType} from '../../Renderer/shims/ReactNativeTypes'; import processAspectRatio from '../../StyleSheet/processAspectRatio'; +import processBoxShadow from '../../StyleSheet/processBoxShadow'; import processColor from '../../StyleSheet/processColor'; import processFilter from '../../StyleSheet/processFilter'; import processFontVariant from '../../StyleSheet/processFontVariant'; @@ -125,6 +126,11 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = { */ experimental_mixBlendMode: true, + /* + * BoxShadow + */ + experimental_boxShadow: {process: processBoxShadow}, + /** * View */ diff --git a/packages/react-native/Libraries/NativeComponent/BaseViewConfig.android.js b/packages/react-native/Libraries/NativeComponent/BaseViewConfig.android.js index 29f303eec7a5..431c6810e9ac 100644 --- a/packages/react-native/Libraries/NativeComponent/BaseViewConfig.android.js +++ b/packages/react-native/Libraries/NativeComponent/BaseViewConfig.android.js @@ -170,6 +170,9 @@ const validAttributesForNonEventProps = { process: require('../StyleSheet/processFilter').default, }, experimental_mixBlendMode: true, + experimental_boxShadow: { + process: require('../StyleSheet/processBoxShadow').default, + }, opacity: true, elevation: true, shadowColor: {process: require('../StyleSheet/processColor').default}, diff --git a/packages/react-native/Libraries/NativeComponent/BaseViewConfig.ios.js b/packages/react-native/Libraries/NativeComponent/BaseViewConfig.ios.js index b43e14adbaca..378b5c272dca 100644 --- a/packages/react-native/Libraries/NativeComponent/BaseViewConfig.ios.js +++ b/packages/react-native/Libraries/NativeComponent/BaseViewConfig.ios.js @@ -223,6 +223,9 @@ const validAttributesForNonEventProps = { experimental_filter: { process: require('../StyleSheet/processFilter').default, }, + experimental_boxShadow: { + process: require('../StyleSheet/processBoxShadow').default, + }, borderTopWidth: true, borderTopColor: {process: require('../StyleSheet/processColor').default}, diff --git a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts index 0130b88580bc..24968554e1be 100644 --- a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts +++ b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts @@ -11,25 +11,6 @@ import {Animated} from '../Animated/Animated'; import {ImageResizeMode} from '../Image/ImageResizeMode'; import {ColorValue} from './StyleSheet'; -export type FilterPrimitive = - | {brightness: number | string} - | {blur: number | string} - | {contrast: number | string} - | {grayscale: number | string} - | {'hue-rotate': number | string} - | {invert: number | string} - | {opacity: number | string} - | {saturate: number | string} - | {sepia: number | string} - | {'drop-shadow': DropShadowPrimitive | string}; - -export type DropShadowPrimitive = { - offsetX: number | string; - offsetY: number | string; - standardDeviation?: number | string | undefined; - color?: ColorValue | number | undefined; -}; - type FlexAlignType = | 'flex-start' | 'flex-end' @@ -246,6 +227,52 @@ export interface TransformsStyle { translateY?: AnimatableNumericValue | undefined; } +export type FilterPrimitive = + | {brightness: number | string} + | {blur: number | string} + | {contrast: number | string} + | {grayscale: number | string} + | {'hue-rotate': number | string} + | {invert: number | string} + | {opacity: number | string} + | {saturate: number | string} + | {sepia: number | string} + | {'drop-shadow': DropShadowPrimitive | string}; + +export type DropShadowPrimitive = { + offsetX: number | string; + offsetY: number | string; + standardDeviation?: number | string | undefined; + color?: ColorValue | number | undefined; +}; + +export type BoxShadowPrimitive = { + offsetX: number | string; + offsetY: number | string; + color?: string | undefined; + blurRadius?: ColorValue | number | undefined; + spreadDistance?: number | string | undefined; + inset?: boolean | undefined; +}; + +export type BlendMode = + | 'normal' + | 'multiply' + | 'screen' + | 'overlay' + | 'darken' + | 'lighten' + | 'color-dodge' + | 'color-burn' + | 'hard-light' + | 'soft-light' + | 'difference' + | 'exclusion' + | 'hue' + | 'saturation' + | 'color' + | 'luminosity'; + /** * @see https://reactnative.dev/docs/view#style */ diff --git a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js index 90e100576a0f..a582a57b2a48 100644 --- a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js +++ b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js @@ -34,25 +34,6 @@ export type EdgeInsetsValue = { bottom: number, }; -export type FilterPrimitive = - | {brightness: number | string} - | {blur: number | string} - | {contrast: number | string} - | {grayscale: number | string} - | {'hue-rotate': number | string} - | {invert: number | string} - | {opacity: number | string} - | {saturate: number | string} - | {sepia: number | string} - | {'drop-shadow': DropShadowPrimitive | string}; - -export type DropShadowPrimitive = { - offsetX: number | string, - offsetY: number | string, - standardDeviation?: number | string, - color?: ____ColorValue_Internal | number, -}; - export type DimensionValue = number | string | 'auto' | AnimatedNode | null; export type AnimatableNumericValue = number | AnimatedNode; @@ -709,11 +690,35 @@ export type ____ShadowStyle_Internal = $ReadOnly<{ ...____ShadowStyle_InternalOverrides, }>; -type ____FilterStyle_Internal = $ReadOnly<{ - experimental_filter?: $ReadOnlyArray, -}>; +export type FilterPrimitive = + | {brightness: number | string} + | {blur: number | string} + | {contrast: number | string} + | {grayscale: number | string} + | {'hue-rotate': number | string} + | {invert: number | string} + | {opacity: number | string} + | {saturate: number | string} + | {sepia: number | string} + | {'drop-shadow': DropShadowPrimitive | string}; + +export type DropShadowPrimitive = { + offsetX: number | string, + offsetY: number | string, + standardDeviation?: number | string, + color?: ____ColorValue_Internal, +}; -export type ____MixBlendMode_Internal = +export type BoxShadowPrimitive = { + offsetX: number | string, + offsetY: number | string, + color?: ____ColorValue_Internal, + blurRadius?: number | string, + spreadDistance?: number | string, + inset?: boolean, +}; + +type ____BlendMode_Internal = | 'normal' | 'multiply' | 'screen' @@ -735,8 +740,6 @@ export type ____ViewStyle_InternalCore = $ReadOnly<{ ...$Exact<____LayoutStyle_Internal>, ...$Exact<____ShadowStyle_Internal>, ...$Exact<____TransformStyle_Internal>, - ...____FilterStyle_Internal, - experimental_mixBlendMode?: ____MixBlendMode_Internal, backfaceVisibility?: 'visible' | 'hidden', backgroundColor?: ____ColorValue_Internal, borderColor?: ____ColorValue_Internal, @@ -775,6 +778,9 @@ export type ____ViewStyle_InternalCore = $ReadOnly<{ elevation?: number, pointerEvents?: 'auto' | 'none' | 'box-none' | 'box-only', cursor?: CursorValue, + experimental_boxShadow?: $ReadOnlyArray, + experimental_filter?: $ReadOnlyArray, + experimental_mixBlendMode?: ____BlendMode_Internal, }>; export type ____ViewStyle_Internal = $ReadOnly<{ diff --git a/packages/react-native/Libraries/StyleSheet/__tests__/processBoxShadow-test.js b/packages/react-native/Libraries/StyleSheet/__tests__/processBoxShadow-test.js new file mode 100644 index 000000000000..d65369f62302 --- /dev/null +++ b/packages/react-native/Libraries/StyleSheet/__tests__/processBoxShadow-test.js @@ -0,0 +1,269 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall react_native + */ + +import processBoxShadow from '../processBoxShadow'; +import processColor from '../processColor'; + +// js1 test processBoxShadow +describe('processBoxShadow', () => { + it('should parse basic string', () => { + expect(processBoxShadow('10px 5')).toEqual([ + { + offsetX: 10, + offsetY: 5, + }, + ]); + }); + + it('should parse basic string with multiple whitespaces', () => { + expect(processBoxShadow('10px 5')).toEqual([ + { + offsetX: 10, + offsetY: 5, + }, + ]); + }); + + it('should parse string with color', () => { + expect(processBoxShadow('red 10 5')).toEqual([ + { + color: processColor('red'), + offsetX: 10, + offsetY: 5, + }, + ]); + }); + + it('should parse string with color function rgba', () => { + expect(processBoxShadow('rgba(255, 255, 255, 0.5) 10 5')).toEqual([ + { + color: processColor('rgba(255, 255, 255, 0.5)'), + offsetX: 10, + offsetY: 5, + }, + ]); + }); + + it('should parse string with color function hsl', () => { + expect(processBoxShadow('hsl(318, 69%, 55%) 10 5')).toEqual([ + { + color: processColor('hsl(318, 69%, 55%)'), + offsetX: 10, + offsetY: 5, + }, + ]); + }); + + it('should parse string with hex color', () => { + expect(processBoxShadow('#FFFFFF 10 5')).toEqual([ + { + color: processColor('#FFFFFF'), + offsetX: 10, + offsetY: 5, + }, + ]); + }); + + it('should parse string with blurRadius', () => { + expect(processBoxShadow('red 10 5 2')).toEqual([ + { + color: processColor('red'), + blurRadius: 2, + offsetX: 10, + offsetY: 5, + }, + ]); + }); + + it('should parse string with spreadDistance', () => { + expect(processBoxShadow('red 10 5 2 3')).toEqual([ + { + color: processColor('red'), + blurRadius: 2, + offsetX: 10, + offsetY: 5, + spreadDistance: 3, + }, + ]); + }); + + it('should parse string arguments with units', () => { + expect(processBoxShadow('5px 2px')).toEqual([ + { + offsetX: 5, + offsetY: 2, + }, + ]); + }); + + it('should parse string with inset', () => { + expect(processBoxShadow('5px 2px inset')).toEqual([ + { + offsetX: 5, + offsetY: 2, + inset: true, + }, + ]); + }); + + it('should parse string with inset and color before and after lengths', () => { + expect(processBoxShadow('red 10 10 inset')).toEqual([ + { + color: processColor('red'), + offsetX: 10, + offsetY: 10, + inset: true, + }, + ]); + }); + + it('should parse multiple box shadow strings', () => { + expect( + processBoxShadow('10 5 red, 5 12 inset, inset 10 45 13 red'), + ).toEqual([ + { + offsetX: 10, + offsetY: 5, + color: processColor('red'), + }, + { + offsetX: 5, + offsetY: 12, + inset: true, + }, + { + offsetX: 10, + offsetY: 45, + blurRadius: 13, + inset: true, + color: processColor('red'), + }, + ]); + }); + + it('should fail to parse string with invalid units', () => { + expect(processBoxShadow('red 10em 5$ 2| 3rp')).toEqual([]); + }); + + it('should fail to parse too many lengths', () => { + expect(processBoxShadow('10 5 2 3 10 10')).toEqual([]); + }); + + it('should fail to parse inset between lengths', () => { + expect(processBoxShadow('10 inset 5 2 3,')).toEqual([]); + }); + + it('should fail to parse double color', () => { + expect(processBoxShadow('red red 10 5')).toEqual([]); + }); + + it('should fail to parse double inset', () => { + expect(processBoxShadow('10 5 inset inset')).toEqual([]); + }); + + it('should fail to parse color between lengths', () => { + expect(processBoxShadow('10 red 5 2 3,')).toEqual([]); + }); + + it('should fail to parse invalid unit', () => { + expect(processBoxShadow('red 10foo 5 2 3,')).toEqual([]); + }); + + it('should fail to parse invalid argument', () => { + expect(processBoxShadow('red asf 5 2 3')).toEqual([]); + }); + + it('should fail to parse negative blur', () => { + expect(processBoxShadow('red 5 2 -3')).toEqual([]); + }); + + it('should parse simple object', () => { + expect( + processBoxShadow([ + { + offsetX: 10, + offsetY: 5, + }, + ]), + ).toEqual([ + { + offsetX: 10, + offsetY: 5, + }, + ]); + }); + + it('should parse object with color', () => { + expect( + processBoxShadow([ + { + offsetX: 10, + offsetY: 5, + color: 'red', + }, + ]), + ).toEqual([ + { + offsetX: 10, + offsetY: 5, + color: processColor('red'), + }, + ]); + }); + + it('should parse complex box shadow', () => { + expect( + processBoxShadow([ + { + offsetX: '10px', + offsetY: 5, + blurRadius: 2, + spreadDistance: 3, + inset: true, + color: '#FFFFFF', + }, + ]), + ).toEqual([ + { + offsetX: 10, + offsetY: 5, + blurRadius: 2, + spreadDistance: 3, + inset: true, + color: processColor('#FFFFFF'), + }, + ]); + }); + + it('should fail to parse object with negative blur', () => { + expect( + processBoxShadow([ + { + offsetX: 10, + offsetY: 5, + color: 'red', + blurRadius: -3, + }, + ]), + ).toEqual([]); + }); + + it('should fail to parse object with invalid argument', () => { + expect( + processBoxShadow([ + { + offsetX: 10, + offsetY: 'asdf', + }, + ]), + ).toEqual([]); + }); +}); diff --git a/packages/react-native/Libraries/StyleSheet/processBoxShadow.js b/packages/react-native/Libraries/StyleSheet/processBoxShadow.js new file mode 100644 index 000000000000..adba8cd2ce0d --- /dev/null +++ b/packages/react-native/Libraries/StyleSheet/processBoxShadow.js @@ -0,0 +1,208 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall react-native + */ + +import type {ProcessedColorValue} from './processColor'; +import type {BoxShadowPrimitive} from './StyleSheetTypes'; + +import processColor from './processColor'; + +export type ParsedBoxShadow = { + offsetX: number, + offsetY: number, + color?: ProcessedColorValue, + blurRadius?: number, + spreadDistance?: number, + inset?: boolean, +}; + +export default function processBoxShadow( + rawBoxShadows: $ReadOnlyArray | string, +): Array { + const result: Array = []; + + const boxShadowList = + typeof rawBoxShadows === 'string' + ? parseBoxShadowString(rawBoxShadows) + : rawBoxShadows; + + for (const rawBoxShadow of boxShadowList) { + const parsedBoxShadow: ParsedBoxShadow = { + offsetX: 0, + offsetY: 0, + }; + + let value; + for (const arg in rawBoxShadow) { + switch (arg) { + case 'offsetX': + value = + typeof rawBoxShadow.offsetX === 'string' + ? parseLength(rawBoxShadow.offsetX) + : rawBoxShadow.offsetX; + if (value == null) { + return []; + } + + parsedBoxShadow.offsetX = value; + break; + case 'offsetY': + value = + typeof rawBoxShadow.offsetY === 'string' + ? parseLength(rawBoxShadow.offsetY) + : rawBoxShadow.offsetY; + if (value == null) { + return []; + } + + parsedBoxShadow.offsetY = value; + break; + case 'spreadDistance': + value = + typeof rawBoxShadow.spreadDistance === 'string' + ? parseLength(rawBoxShadow.spreadDistance) + : rawBoxShadow.spreadDistance; + if (value == null) { + return []; + } + + parsedBoxShadow.spreadDistance = value; + break; + case 'blurRadius': + value = + typeof rawBoxShadow.blurRadius === 'string' + ? parseLength(rawBoxShadow.blurRadius) + : rawBoxShadow.blurRadius; + if (value == null || value < 0) { + return []; + } + + parsedBoxShadow.blurRadius = value; + break; + case 'color': + const color = processColor(rawBoxShadow.color); + if (color == null) { + return []; + } + + parsedBoxShadow.color = color; + break; + case 'inset': + parsedBoxShadow.inset = rawBoxShadow.inset; + } + } + result.push(parsedBoxShadow); + } + return result; +} + +function parseBoxShadowString( + rawBoxShadows: string, +): Array { + let result: Array = []; + + for (const rawBoxShadow of rawBoxShadows + .split(/,(?![^()]*\))/) // split by comma that is not in parenthesis + .map(bS => bS.trim()) + .filter(bS => bS !== '')) { + const boxShadow: BoxShadowPrimitive = { + offsetX: 0, + offsetY: 0, + }; + let offsetX: number | string; + let offsetY: number | string; + let keywordDetectedAfterLength = false; + + let lengthCount = 0; + + // split rawBoxShadow string by all whitespaces that are not in parenthesis + const args = rawBoxShadow.split(/\s+(?![^(]*\))/); + for (const arg of args) { + const processedColor = processColor(arg); + if (processedColor != null) { + if (boxShadow.color != null) { + return []; + } + if (offsetX != null) { + keywordDetectedAfterLength = true; + } + boxShadow.color = arg; + continue; + } + + if (arg === 'inset') { + if (boxShadow.inset != null) { + return []; + } + if (offsetX != null) { + keywordDetectedAfterLength = true; + } + boxShadow.inset = true; + continue; + } + + switch (lengthCount) { + case 0: + offsetX = arg; + lengthCount++; + break; + case 1: + if (keywordDetectedAfterLength) { + return []; + } + offsetY = arg; + lengthCount++; + break; + case 2: + if (keywordDetectedAfterLength) { + return []; + } + boxShadow.blurRadius = arg; + lengthCount++; + break; + case 3: + if (keywordDetectedAfterLength) { + return []; + } + boxShadow.spreadDistance = arg; + lengthCount++; + break; + default: + return []; + } + } + + if (offsetX == null || offsetY == null) { + return []; + } + + boxShadow.offsetX = offsetX; + boxShadow.offsetY = offsetY; + + result.push(boxShadow); + } + return result; +} + +function parseLength(length: string): ?number { + // matches on args with units like "1.5 5% -80deg" + const argsWithUnitsRegex = /([+-]?\d*(\.\d+)?)([\w\W]+)?/g; + const match = argsWithUnitsRegex.exec(length); + + if (!match || Number.isNaN(match[1])) { + return null; + } + + if (match[3] != null && match[3] !== 'px') { + return null; + } + + return Number(match[1]); +} diff --git a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap index aad3ddfa0a4d..93f88d5f51a0 100644 --- a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap +++ b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap @@ -7571,23 +7571,6 @@ export type EdgeInsetsValue = { right: number, bottom: number, }; -export type FilterPrimitive = - | { brightness: number | string } - | { blur: number | string } - | { contrast: number | string } - | { grayscale: number | string } - | { \\"hue-rotate\\": number | string } - | { invert: number | string } - | { opacity: number | string } - | { saturate: number | string } - | { sepia: number | string } - | { \\"drop-shadow\\": DropShadowPrimitive | string }; -export type DropShadowPrimitive = { - offsetX: number | string, - offsetY: number | string, - standardDeviation?: number | string, - color?: ____ColorValue_Internal | number, -}; export type DimensionValue = number | string | \\"auto\\" | AnimatedNode | null; export type AnimatableNumericValue = number | AnimatedNode; export type CursorValue = \\"auto\\" | \\"pointer\\"; @@ -7700,10 +7683,32 @@ export type ____ShadowStyle_Internal = $ReadOnly<{ ...____ShadowStyle_InternalCore, ...____ShadowStyle_InternalOverrides, }>; -type ____FilterStyle_Internal = $ReadOnly<{ - experimental_filter?: $ReadOnlyArray, -}>; -export type ____MixBlendMode_Internal = +export type FilterPrimitive = + | { brightness: number | string } + | { blur: number | string } + | { contrast: number | string } + | { grayscale: number | string } + | { \\"hue-rotate\\": number | string } + | { invert: number | string } + | { opacity: number | string } + | { saturate: number | string } + | { sepia: number | string } + | { \\"drop-shadow\\": DropShadowPrimitive | string }; +export type DropShadowPrimitive = { + offsetX: number | string, + offsetY: number | string, + standardDeviation?: number | string, + color?: ____ColorValue_Internal, +}; +export type BoxShadowPrimitive = { + offsetX: number | string, + offsetY: number | string, + color?: ____ColorValue_Internal, + blurRadius?: number | string, + spreadDistance?: number | string, + inset?: boolean, +}; +type ____BlendMode_Internal = | \\"normal\\" | \\"multiply\\" | \\"screen\\" @@ -7724,8 +7729,6 @@ export type ____ViewStyle_InternalCore = $ReadOnly<{ ...$Exact<____LayoutStyle_Internal>, ...$Exact<____ShadowStyle_Internal>, ...$Exact<____TransformStyle_Internal>, - ...____FilterStyle_Internal, - experimental_mixBlendMode?: ____MixBlendMode_Internal, backfaceVisibility?: \\"visible\\" | \\"hidden\\", backgroundColor?: ____ColorValue_Internal, borderColor?: ____ColorValue_Internal, @@ -7764,6 +7767,9 @@ export type ____ViewStyle_InternalCore = $ReadOnly<{ elevation?: number, pointerEvents?: \\"auto\\" | \\"none\\" | \\"box-none\\" | \\"box-only\\", cursor?: CursorValue, + experimental_boxShadow?: $ReadOnlyArray, + experimental_filter?: $ReadOnlyArray, + experimental_mixBlendMode?: ____BlendMode_Internal, }>; export type ____ViewStyle_Internal = $ReadOnly<{ ...____ViewStyle_InternalCore, @@ -8005,6 +8011,21 @@ declare module.exports: processAspectRatio; " `; +exports[`public API should not change unintentionally Libraries/StyleSheet/processBoxShadow.js 1`] = ` +"export type ParsedBoxShadow = { + offsetX: number, + offsetY: number, + color?: ProcessedColorValue, + blurRadius?: number, + spreadDistance?: number, + inset?: boolean, +}; +declare export default function processBoxShadow( + rawBoxShadows: $ReadOnlyArray | string +): Array; +" +`; + exports[`public API should not change unintentionally Libraries/StyleSheet/processColor.js 1`] = ` "export type ProcessedColorValue = number | NativeColorValue; declare function processColor( diff --git a/packages/react-native/types/experimental.d.ts b/packages/react-native/types/experimental.d.ts index 2e9f69d0c842..ab0fafec57dc 100644 --- a/packages/react-native/types/experimental.d.ts +++ b/packages/react-native/types/experimental.d.ts @@ -32,7 +32,12 @@ * Either the import or the reference only needs to appear once, anywhere in the project. */ -import {DimensionValue} from 'react-native/Libraries/StyleSheet/StyleSheetTypes'; +import { + BlendMode, + BoxShadowPrimitive, + DimensionValue, + FilterPrimitive, +} from 'react-native/Libraries/StyleSheet/StyleSheetTypes'; export {}; @@ -142,4 +147,10 @@ declare module '.' { */ experimental_layoutConformance?: 'strict' | 'classic' | undefined; } + + export interface ViewStyle { + experimental_boxShadow?: BoxShadowPrimitive | undefined; + experimental_filter?: ReadonlyArray | undefined; + experimental_mixBlendMode?: BlendMode | undefined; + } }