@office-iss/react-native-win32 0.75.2 → 0.76.0-preview.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/.eslintrc.js +11 -0
  2. package/.flowconfig +5 -4
  3. package/CHANGELOG.json +191 -47
  4. package/CHANGELOG.md +63 -28
  5. package/Libraries/Alert/Alert.js +3 -0
  6. package/Libraries/Animated/AnimatedEvent.js +1 -1
  7. package/Libraries/Animated/AnimatedImplementation.js +7 -7
  8. package/Libraries/Animated/NativeAnimatedAllowlist.js +111 -0
  9. package/Libraries/Animated/animations/Animation.js +11 -1
  10. package/Libraries/Animated/animations/DecayAnimation.js +1 -1
  11. package/Libraries/Animated/animations/SpringAnimation.js +1 -1
  12. package/Libraries/Animated/animations/TimingAnimation.js +2 -1
  13. package/Libraries/Animated/components/AnimatedScrollView.js +3 -2
  14. package/Libraries/Animated/createAnimatedComponent.js +10 -9
  15. package/Libraries/Animated/nodes/AnimatedColor.js +1 -1
  16. package/Libraries/Animated/nodes/AnimatedInterpolation.js +3 -2
  17. package/Libraries/Animated/nodes/AnimatedNode.js +42 -33
  18. package/Libraries/Animated/nodes/AnimatedObject.js +56 -50
  19. package/Libraries/Animated/nodes/AnimatedProps.js +77 -40
  20. package/Libraries/Animated/nodes/AnimatedStyle.js +103 -59
  21. package/Libraries/Animated/nodes/AnimatedTracking.js +1 -1
  22. package/Libraries/Animated/nodes/AnimatedTransform.js +102 -67
  23. package/Libraries/Animated/nodes/AnimatedValue.js +2 -1
  24. package/Libraries/Animated/nodes/AnimatedWithChildren.js +21 -22
  25. package/Libraries/Animated/useAnimatedProps.js +142 -7
  26. package/Libraries/BatchedBridge/NativeModules.js +2 -0
  27. package/Libraries/Blob/FileReader.js +1 -1
  28. package/Libraries/Blob/URL.js +2 -62
  29. package/Libraries/Blob/URLSearchParams.js +71 -0
  30. package/Libraries/Components/DrawerAndroid/DrawerLayoutAndroid.android.js +1 -1
  31. package/Libraries/Components/Keyboard/KeyboardAvoidingView.js +17 -0
  32. package/Libraries/Components/RefreshControl/__mocks__/RefreshControlMock.js +1 -1
  33. package/Libraries/Components/ScrollView/ScrollView.js +131 -169
  34. package/Libraries/Components/ScrollView/ScrollViewNativeComponent.js +3 -0
  35. package/Libraries/Components/ScrollView/ScrollViewStickyHeader.js +1 -1
  36. package/Libraries/Components/StatusBar/StatusBar.js +3 -1
  37. package/Libraries/Components/TextInput/RCTTextInputViewConfig.js +10 -0
  38. package/Libraries/Components/TextInput/TextInput.d.ts +32 -2
  39. package/Libraries/Components/TextInput/TextInput.js +230 -94
  40. package/Libraries/Components/TextInput/TextInput.win32.js +230 -100
  41. package/Libraries/Components/View/ReactNativeStyleAttributes.js +23 -1
  42. package/Libraries/Components/View/ReactNativeViewAttributes.js +2 -0
  43. package/Libraries/Components/View/ReactNativeViewAttributes.win32.js +2 -0
  44. package/Libraries/Components/View/ViewAccessibility.d.ts +15 -0
  45. package/Libraries/Components/View/ViewNativeComponent.js +0 -1
  46. package/Libraries/Components/View/ViewPropTypes.js +14 -0
  47. package/Libraries/Components/View/ViewPropTypes.win32.js +14 -0
  48. package/Libraries/Core/ExceptionsManager.js +2 -0
  49. package/Libraries/Core/InitializeCore.js +3 -1
  50. package/Libraries/Core/ReactFiberErrorDialog.js +3 -0
  51. package/Libraries/Core/ReactNativeVersion.js +4 -4
  52. package/Libraries/Core/ReactNativeVersionCheck.win32.js +1 -1
  53. package/Libraries/Core/setUpDeveloperTools.js +5 -1
  54. package/Libraries/Core/setUpErrorHandling.js +7 -1
  55. package/Libraries/Core/setUpGlobals.js +1 -0
  56. package/Libraries/Core/setUpReactRefresh.js +0 -4
  57. package/Libraries/Image/AssetSourceResolver.js +28 -1
  58. package/Libraries/Image/Image.android.js +9 -14
  59. package/Libraries/Image/Image.ios.js +11 -22
  60. package/Libraries/Image/Image.win32.js +11 -24
  61. package/Libraries/Image/ImageBackground.js +1 -8
  62. package/Libraries/Image/ImageUtils.js +9 -9
  63. package/Libraries/Image/ImageViewNativeComponent.js +1 -0
  64. package/Libraries/Inspector/Inspector.js +3 -2
  65. package/Libraries/Inspector/Inspector.win32.js +3 -2
  66. package/Libraries/Inspector/InspectorPanel.js +16 -10
  67. package/Libraries/Inspector/NetworkOverlay.js +1 -1
  68. package/Libraries/Interaction/TaskQueue.js +1 -0
  69. package/Libraries/Lists/FlatList.js +1 -1
  70. package/Libraries/Lists/SectionList.js +2 -2
  71. package/Libraries/Lists/SectionListModern.js +3 -3
  72. package/Libraries/LogBox/Data/LogBoxData.js +24 -3
  73. package/Libraries/LogBox/LogBoxNotificationContainer.js +3 -2
  74. package/Libraries/LogBox/UI/LogBoxInspectorHeader.js +9 -8
  75. package/Libraries/LogBox/UI/LogBoxInspectorHeader.win32.js +9 -29
  76. package/Libraries/Modal/Modal.js +0 -1
  77. package/Libraries/NativeComponent/BaseViewConfig.android.js +9 -1
  78. package/Libraries/NativeComponent/BaseViewConfig.ios.js +17 -1
  79. package/Libraries/NativeComponent/BaseViewConfig.win32.js +17 -1
  80. package/Libraries/NativeComponent/NativeComponentRegistry.js +22 -22
  81. package/Libraries/NativeComponent/StaticViewConfigValidator.js +0 -21
  82. package/Libraries/Network/XMLHttpRequest.js +4 -2
  83. package/Libraries/ReactNative/AppContainer-dev.js +1 -5
  84. package/Libraries/ReactNative/AppContainer-prod.js +1 -5
  85. package/Libraries/ReactNative/AppContainer.js +0 -1
  86. package/Libraries/ReactNative/AppRegistry.js +0 -6
  87. package/Libraries/ReactNative/BridgelessUIManager.js +1 -0
  88. package/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricHostComponent.js +1 -1
  89. package/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance.js +5 -5
  90. package/Libraries/ReactNative/RendererImplementation.js +26 -4
  91. package/Libraries/ReactNative/getNativeComponentAttributes.js +12 -0
  92. package/Libraries/ReactNative/renderApplication.js +0 -2
  93. package/Libraries/Renderer/shims/ReactNativeTypes.js +11 -4
  94. package/Libraries/StyleSheet/StyleSheet.js +1 -1
  95. package/Libraries/StyleSheet/StyleSheet.win32.js +1 -1
  96. package/Libraries/StyleSheet/StyleSheetTypes.d.ts +152 -2
  97. package/Libraries/StyleSheet/StyleSheetTypes.js +60 -5
  98. package/Libraries/StyleSheet/processBackgroundImage.js +384 -0
  99. package/Libraries/StyleSheet/processBoxShadow.js +209 -0
  100. package/Libraries/StyleSheet/processFilter.js +231 -42
  101. package/Libraries/Text/Text.js +394 -196
  102. package/Libraries/Text/Text.win32.js +442 -229
  103. package/Libraries/Text/TextNativeComponent.js +2 -1
  104. package/Libraries/Text/TextNativeComponent.win32.js +1 -1
  105. package/Libraries/TurboModule/TurboModuleRegistry.js +13 -50
  106. package/Libraries/Types/CodegenTypes.js +3 -1
  107. package/Libraries/Utilities/Appearance.js +108 -84
  108. package/Libraries/Utilities/DevLoadingView.js +2 -4
  109. package/Libraries/Utilities/HMRClient.js +8 -6
  110. package/Libraries/Utilities/ReactNativeTestTools.js +1 -1
  111. package/Libraries/Utilities/createPerformanceLogger.js +0 -9
  112. package/Libraries/Utilities/stringifyViewConfig.js +22 -0
  113. package/Libraries/Utilities/useColorScheme.js +3 -3
  114. package/Libraries/WebSocket/WebSocket.js +1 -1
  115. package/Libraries/promiseRejectionTrackingOptions.js +1 -1
  116. package/Libraries/vendor/emitter/EventEmitter.js +6 -5
  117. package/flow/jest.js +2 -2
  118. package/index.js +3 -1
  119. package/index.win32.js +3 -1
  120. package/jest/mockComponent.js +4 -1
  121. package/jest/mockModal.js +1 -3
  122. package/jest/mockScrollView.js +1 -1
  123. package/jest/renderer.js +2 -2
  124. package/jest/setup.js +16 -13
  125. package/jest.config.js +1 -2
  126. package/overrides.json +22 -22
  127. package/package.json +32 -30
  128. package/src/private/animated/NativeAnimatedHelper.js +438 -0
  129. package/src/private/animated/NativeAnimatedHelper.win32.js +440 -0
  130. package/src/private/animated/NativeAnimatedValidation.js +64 -0
  131. package/src/private/components/HScrollViewNativeComponents.js +56 -0
  132. package/src/private/components/SafeAreaView_INTERNAL_DO_NOT_USE.js +29 -0
  133. package/src/private/components/VScrollViewNativeComponents.js +48 -0
  134. package/src/private/components/useSyncOnScroll.js +48 -0
  135. package/src/private/featureflags/ReactNativeFeatureFlags.js +166 -16
  136. package/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +29 -5
  137. package/src/private/fusebox/FuseboxSessionObserver.js +42 -0
  138. package/{Libraries/Core → src/private/renderer/errorhandling}/ErrorHandlers.js +14 -4
  139. package/src/private/setup/setUpDOM.js +28 -0
  140. package/src/private/setup/setUpIntersectionObserver.js +27 -0
  141. package/src/private/setup/setUpMutationObserver.js +26 -0
  142. package/src/private/setup/setUpPerformanceObserver.js +64 -0
  143. package/src/private/specs/modules/NativeAppearance.js +3 -3
  144. package/src/private/specs/modules/NativeLinkingManager.js +1 -1
  145. package/src/private/specs/modules/NativePlatformConstantsWin.js +7 -0
  146. package/src/private/specs/modules/NativeSampleTurboModule.js +14 -1
  147. package/src/private/webapis/dom/nodes/ReadOnlyNode.js +6 -4
  148. package/{Libraries/IntersectionObserver → src/private/webapis/intersectionobserver}/IntersectionObserver.js +5 -3
  149. package/{Libraries/IntersectionObserver → src/private/webapis/intersectionobserver}/IntersectionObserverEntry.js +3 -3
  150. package/{Libraries/IntersectionObserver → src/private/webapis/intersectionobserver}/IntersectionObserverManager.js +14 -17
  151. package/src/private/{specs/modules → webapis/intersectionobserver/specs}/NativeIntersectionObserver.js +2 -2
  152. package/{Libraries/IntersectionObserver → src/private/webapis/intersectionobserver/specs}/__mocks__/NativeIntersectionObserver.js +4 -4
  153. package/{Libraries/MutationObserver → src/private/webapis/mutationobserver}/MutationObserver.js +5 -3
  154. package/{Libraries/MutationObserver → src/private/webapis/mutationobserver}/MutationObserverManager.js +24 -15
  155. package/{Libraries/MutationObserver → src/private/webapis/mutationobserver}/MutationRecord.js +4 -6
  156. package/src/private/{specs/modules → webapis/mutationobserver/specs}/NativeMutationObserver.js +2 -2
  157. package/{Libraries/MutationObserver → src/private/webapis/mutationobserver/specs}/__mocks__/NativeMutationObserver.js +5 -5
  158. package/src/private/webapis/performance/{EventCounts.js → EventTiming.js} +65 -3
  159. package/src/private/webapis/performance/LongTasks.js +39 -0
  160. package/src/private/webapis/performance/Performance.js +22 -9
  161. package/src/private/webapis/performance/PerformanceEntry.js +36 -18
  162. package/src/private/webapis/performance/PerformanceObserver.js +29 -43
  163. package/src/private/webapis/performance/RawPerformanceEntry.js +24 -1
  164. package/src/private/webapis/performance/UserTiming.js +17 -12
  165. package/src/private/webapis/performance/specs/NativePerformanceObserver.js +1 -1
  166. package/src-win/Libraries/Components/View/ViewAccessibility.d.ts +15 -0
  167. package/types/experimental.d.ts +12 -98
  168. package/Libraries/Animated/NativeAnimatedHelper.js +0 -615
  169. package/Libraries/Animated/NativeAnimatedHelper.win32.js +0 -617
  170. package/Libraries/Core/setUpIntersectionObserver.js +0 -16
  171. package/Libraries/Core/setUpMutationObserver.js +0 -16
  172. package/Libraries/Core/setUpPerformanceObserver.js +0 -18
  173. package/Libraries/IntersectionObserver/NativeIntersectionObserver.js +0 -13
  174. package/Libraries/MutationObserver/NativeMutationObserver.js +0 -13
  175. package/Libraries/Utilities/verifyComponentAttributeEquivalence.js +0 -135
  176. package/src/private/core/setUpDOM.js +0 -18
  177. package/src/private/webapis/performance/PerformanceEventTiming.js +0 -55
  178. /package/src/private/{core → styles}/composeStyles.js +0 -0
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict-local
8
+ * @format
9
+ * @oncall react-native
10
+ */
11
+
12
+ import type {ProcessedColorValue} from './processColor';
13
+ import type {BoxShadowValue} from './StyleSheetTypes';
14
+
15
+ import processColor from './processColor';
16
+
17
+ export type ParsedBoxShadow = {
18
+ offsetX: number,
19
+ offsetY: number,
20
+ color?: ProcessedColorValue,
21
+ blurRadius?: number,
22
+ spreadDistance?: number,
23
+ inset?: boolean,
24
+ };
25
+
26
+ export default function processBoxShadow(
27
+ rawBoxShadows: ?($ReadOnlyArray<BoxShadowValue> | string),
28
+ ): Array<ParsedBoxShadow> {
29
+ const result: Array<ParsedBoxShadow> = [];
30
+ if (rawBoxShadows == null) {
31
+ return result;
32
+ }
33
+
34
+ const boxShadowList =
35
+ typeof rawBoxShadows === 'string'
36
+ ? parseBoxShadowString(rawBoxShadows.replace(/\n/g, ' '))
37
+ : rawBoxShadows;
38
+
39
+ for (const rawBoxShadow of boxShadowList) {
40
+ const parsedBoxShadow: ParsedBoxShadow = {
41
+ offsetX: 0,
42
+ offsetY: 0,
43
+ };
44
+
45
+ let value;
46
+ for (const arg in rawBoxShadow) {
47
+ switch (arg) {
48
+ case 'offsetX':
49
+ value =
50
+ typeof rawBoxShadow.offsetX === 'string'
51
+ ? parseLength(rawBoxShadow.offsetX)
52
+ : rawBoxShadow.offsetX;
53
+ if (value == null) {
54
+ return [];
55
+ }
56
+
57
+ parsedBoxShadow.offsetX = value;
58
+ break;
59
+ case 'offsetY':
60
+ value =
61
+ typeof rawBoxShadow.offsetY === 'string'
62
+ ? parseLength(rawBoxShadow.offsetY)
63
+ : rawBoxShadow.offsetY;
64
+ if (value == null) {
65
+ return [];
66
+ }
67
+
68
+ parsedBoxShadow.offsetY = value;
69
+ break;
70
+ case 'spreadDistance':
71
+ value =
72
+ typeof rawBoxShadow.spreadDistance === 'string'
73
+ ? parseLength(rawBoxShadow.spreadDistance)
74
+ : rawBoxShadow.spreadDistance;
75
+ if (value == null) {
76
+ return [];
77
+ }
78
+
79
+ parsedBoxShadow.spreadDistance = value;
80
+ break;
81
+ case 'blurRadius':
82
+ value =
83
+ typeof rawBoxShadow.blurRadius === 'string'
84
+ ? parseLength(rawBoxShadow.blurRadius)
85
+ : rawBoxShadow.blurRadius;
86
+ if (value == null || value < 0) {
87
+ return [];
88
+ }
89
+
90
+ parsedBoxShadow.blurRadius = value;
91
+ break;
92
+ case 'color':
93
+ const color = processColor(rawBoxShadow.color);
94
+ if (color == null) {
95
+ return [];
96
+ }
97
+
98
+ parsedBoxShadow.color = color;
99
+ break;
100
+ case 'inset':
101
+ parsedBoxShadow.inset = rawBoxShadow.inset;
102
+ }
103
+ }
104
+ result.push(parsedBoxShadow);
105
+ }
106
+ return result;
107
+ }
108
+
109
+ function parseBoxShadowString(rawBoxShadows: string): Array<BoxShadowValue> {
110
+ let result: Array<BoxShadowValue> = [];
111
+
112
+ for (const rawBoxShadow of rawBoxShadows
113
+ .split(/,(?![^()]*\))/) // split by comma that is not in parenthesis
114
+ .map(bS => bS.trim())
115
+ .filter(bS => bS !== '')) {
116
+ const boxShadow: BoxShadowValue = {
117
+ offsetX: 0,
118
+ offsetY: 0,
119
+ };
120
+ let offsetX: number | string;
121
+ let offsetY: number | string;
122
+ let keywordDetectedAfterLength = false;
123
+
124
+ let lengthCount = 0;
125
+
126
+ // split rawBoxShadow string by all whitespaces that are not in parenthesis
127
+ const args = rawBoxShadow.split(/\s+(?![^(]*\))/);
128
+ for (const arg of args) {
129
+ const processedColor = processColor(arg);
130
+ if (processedColor != null) {
131
+ if (boxShadow.color != null) {
132
+ return [];
133
+ }
134
+ if (offsetX != null) {
135
+ keywordDetectedAfterLength = true;
136
+ }
137
+ boxShadow.color = arg;
138
+ continue;
139
+ }
140
+
141
+ if (arg === 'inset') {
142
+ if (boxShadow.inset != null) {
143
+ return [];
144
+ }
145
+ if (offsetX != null) {
146
+ keywordDetectedAfterLength = true;
147
+ }
148
+ boxShadow.inset = true;
149
+ continue;
150
+ }
151
+
152
+ switch (lengthCount) {
153
+ case 0:
154
+ offsetX = arg;
155
+ lengthCount++;
156
+ break;
157
+ case 1:
158
+ if (keywordDetectedAfterLength) {
159
+ return [];
160
+ }
161
+ offsetY = arg;
162
+ lengthCount++;
163
+ break;
164
+ case 2:
165
+ if (keywordDetectedAfterLength) {
166
+ return [];
167
+ }
168
+ boxShadow.blurRadius = arg;
169
+ lengthCount++;
170
+ break;
171
+ case 3:
172
+ if (keywordDetectedAfterLength) {
173
+ return [];
174
+ }
175
+ boxShadow.spreadDistance = arg;
176
+ lengthCount++;
177
+ break;
178
+ default:
179
+ return [];
180
+ }
181
+ }
182
+
183
+ if (offsetX == null || offsetY == null) {
184
+ return [];
185
+ }
186
+
187
+ boxShadow.offsetX = offsetX;
188
+ boxShadow.offsetY = offsetY;
189
+
190
+ result.push(boxShadow);
191
+ }
192
+ return result;
193
+ }
194
+
195
+ function parseLength(length: string): ?number {
196
+ // matches on args with units like "1.5 5% -80deg"
197
+ const argsWithUnitsRegex = /([+-]?\d*(\.\d+)?)([\w\W]+)?/g;
198
+ const match = argsWithUnitsRegex.exec(length);
199
+
200
+ if (!match || Number.isNaN(match[1])) {
201
+ return null;
202
+ }
203
+
204
+ if (match[3] != null && match[3] !== 'px') {
205
+ return null;
206
+ }
207
+
208
+ return Number(match[1]);
209
+ }
@@ -4,66 +4,113 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
- * @format
7
+ * @format strict-local
8
8
  * @flow
9
+ * @oncall react-native
9
10
  */
10
11
 
11
12
  'use strict';
12
13
 
13
- export type FilterPrimitive =
14
- | {brightness: number | string}
15
- | {blur: number | string}
16
- | {contrast: number | string}
17
- | {grayscale: number | string}
18
- | {hueRotate: number | string}
19
- | {invert: number | string}
20
- | {opacity: number | string}
21
- | {saturate: number | string}
22
- | {sepia: number | string};
14
+ import type {ColorValue} from './StyleSheet';
15
+ import type {DropShadowValue, FilterFunction} from './StyleSheetTypes';
16
+
17
+ import processColor from './processColor';
18
+
19
+ type ParsedFilter =
20
+ | {brightness: number}
21
+ | {blur: number}
22
+ | {contrast: number}
23
+ | {grayscale: number}
24
+ | {hueRotate: number}
25
+ | {invert: number}
26
+ | {opacity: number}
27
+ | {saturate: number}
28
+ | {sepia: number}
29
+ | {dropShadow: ParsedDropShadow};
30
+
31
+ type ParsedDropShadow = {
32
+ offsetX: number,
33
+ offsetY: number,
34
+ standardDeviation?: number,
35
+ color?: ColorValue,
36
+ };
23
37
 
24
38
  export default function processFilter(
25
- filter: $ReadOnlyArray<FilterPrimitive> | string,
26
- ): $ReadOnlyArray<FilterPrimitive> {
27
- let result: Array<FilterPrimitive> = [];
39
+ filter: ?($ReadOnlyArray<FilterFunction> | string),
40
+ ): $ReadOnlyArray<ParsedFilter> {
41
+ let result: Array<ParsedFilter> = [];
42
+ if (filter == null) {
43
+ return result;
44
+ }
45
+
28
46
  if (typeof filter === 'string') {
29
- // matches on functions with args like "brightness(1.5)"
30
- const regex = new RegExp(/(\w+)\(([^)]+)\)/g);
47
+ filter = filter.replace(/\n/g, ' ');
48
+
49
+ // matches on functions with args and nested functions like "drop-shadow(10 10 10 rgba(0, 0, 0, 1))"
50
+ const regex = /([\w-]+)\(([^()]*|\([^()]*\)|[^()]*\([^()]*\)[^()]*)\)/g;
31
51
  let matches;
32
52
 
33
53
  while ((matches = regex.exec(filter))) {
34
- const amount = _getFilterAmount(matches[1], matches[2]);
35
-
36
- if (amount != null) {
37
- const filterPrimitive = {};
38
- // $FlowFixMe The key will be the correct one but flow can't see that.
39
- filterPrimitive[matches[1]] = amount;
40
- // $FlowFixMe The key will be the correct one but flow can't see that.
41
- result.push(filterPrimitive);
54
+ let filterName = matches[1].toLowerCase();
55
+ if (filterName === 'drop-shadow') {
56
+ const dropShadow = parseDropShadow(matches[2]);
57
+ if (dropShadow != null) {
58
+ result.push({dropShadow});
59
+ } else {
60
+ return [];
61
+ }
42
62
  } else {
43
- // If any primitive is invalid then apply none of the filters. This is how
44
- // web works and makes it clear that something is wrong becuase no
45
- // graphical effects are happening.
46
- return [];
63
+ const camelizedName =
64
+ filterName === 'drop-shadow'
65
+ ? 'dropShadow'
66
+ : filterName === 'hue-rotate'
67
+ ? 'hueRotate'
68
+ : filterName;
69
+ const amount = _getFilterAmount(camelizedName, matches[2]);
70
+
71
+ if (amount != null) {
72
+ const filterFunction = {};
73
+ // $FlowFixMe The key will be the correct one but flow can't see that.
74
+ filterFunction[camelizedName] = amount;
75
+ // $FlowFixMe The key will be the correct one but flow can't see that.
76
+ result.push(filterFunction);
77
+ } else {
78
+ // If any primitive is invalid then apply none of the filters. This is how
79
+ // web works and makes it clear that something is wrong becuase no
80
+ // graphical effects are happening.
81
+ return [];
82
+ }
47
83
  }
48
84
  }
49
- } else {
50
- for (const filterPrimitive of filter) {
51
- const [filterName, filterValue] = Object.entries(filterPrimitive)[0];
52
- const amount = _getFilterAmount(filterName, filterValue);
53
-
54
- if (amount != null) {
55
- const resultObject = {};
56
- // $FlowFixMe
57
- resultObject[filterName] = amount;
85
+ } else if (Array.isArray(filter)) {
86
+ for (const filterFunction of filter) {
87
+ const [filterName, filterValue] = Object.entries(filterFunction)[0];
88
+ if (filterName === 'dropShadow') {
58
89
  // $FlowFixMe
59
- result.push(resultObject);
90
+ const dropShadow = parseDropShadow(filterValue);
91
+ if (dropShadow == null) {
92
+ return [];
93
+ }
94
+ result.push({dropShadow});
60
95
  } else {
61
- // If any primitive is invalid then apply none of the filters. This is how
62
- // web works and makes it clear that something is wrong becuase no
63
- // graphical effects are happening.
64
- return [];
96
+ const amount = _getFilterAmount(filterName, filterValue);
97
+
98
+ if (amount != null) {
99
+ const resultObject = {};
100
+ // $FlowFixMe
101
+ resultObject[filterName] = amount;
102
+ // $FlowFixMe
103
+ result.push(resultObject);
104
+ } else {
105
+ // If any primitive is invalid then apply none of the filters. This is how
106
+ // web works and makes it clear that something is wrong becuase no
107
+ // graphical effects are happening.
108
+ return [];
109
+ }
65
110
  }
66
111
  }
112
+ } else {
113
+ throw new TypeError(`${typeof filter} filter is not a string or array`);
67
114
  }
68
115
 
69
116
  return result;
@@ -130,3 +177,145 @@ function _getFilterAmount(filterName: string, filterArgs: mixed): ?number {
130
177
  return undefined;
131
178
  }
132
179
  }
180
+
181
+ function parseDropShadow(
182
+ rawDropShadow: string | DropShadowValue,
183
+ ): ?ParsedDropShadow {
184
+ const dropShadow =
185
+ typeof rawDropShadow === 'string'
186
+ ? parseDropShadowString(rawDropShadow)
187
+ : rawDropShadow;
188
+
189
+ const parsedDropShadow: ParsedDropShadow = {
190
+ offsetX: 0,
191
+ offsetY: 0,
192
+ };
193
+ let offsetX: number;
194
+ let offsetY: number;
195
+
196
+ for (const arg in dropShadow) {
197
+ let value;
198
+ switch (arg) {
199
+ case 'offsetX':
200
+ value =
201
+ typeof dropShadow.offsetX === 'string'
202
+ ? parseLength(dropShadow.offsetX)
203
+ : dropShadow.offsetX;
204
+ if (value == null) {
205
+ return null;
206
+ }
207
+ offsetX = value;
208
+ break;
209
+ case 'offsetY':
210
+ value =
211
+ typeof dropShadow.offsetY === 'string'
212
+ ? parseLength(dropShadow.offsetY)
213
+ : dropShadow.offsetY;
214
+ if (value == null) {
215
+ return null;
216
+ }
217
+ offsetY = value;
218
+ break;
219
+ case 'standardDeviation':
220
+ value =
221
+ typeof dropShadow.standardDeviation === 'string'
222
+ ? parseLength(dropShadow.standardDeviation)
223
+ : dropShadow.standardDeviation;
224
+ if (value == null || value < 0) {
225
+ return null;
226
+ }
227
+ parsedDropShadow.standardDeviation = value;
228
+ break;
229
+ case 'color':
230
+ const color = processColor(dropShadow.color);
231
+ if (color == null) {
232
+ return null;
233
+ }
234
+ parsedDropShadow.color = color;
235
+ break;
236
+ default:
237
+ return null;
238
+ }
239
+ }
240
+
241
+ if (offsetX == null || offsetY == null) {
242
+ return null;
243
+ }
244
+
245
+ parsedDropShadow.offsetX = offsetX;
246
+ parsedDropShadow.offsetY = offsetY;
247
+
248
+ return parsedDropShadow;
249
+ }
250
+
251
+ function parseDropShadowString(rawDropShadow: string): ?DropShadowValue {
252
+ const dropShadow: DropShadowValue = {
253
+ offsetX: 0,
254
+ offsetY: 0,
255
+ };
256
+ let offsetX: string;
257
+ let offsetY: string;
258
+ let lengthCount = 0;
259
+ let keywordDetectedAfterLength = false;
260
+
261
+ // split args by all whitespaces that are not in parenthesis
262
+ for (const arg of rawDropShadow.split(/\s+(?![^(]*\))/)) {
263
+ const processedColor = processColor(arg);
264
+ if (processedColor != null) {
265
+ if (dropShadow.color != null) {
266
+ return null;
267
+ }
268
+ if (offsetX != null) {
269
+ keywordDetectedAfterLength = true;
270
+ }
271
+ dropShadow.color = arg;
272
+ continue;
273
+ }
274
+
275
+ switch (lengthCount) {
276
+ case 0:
277
+ offsetX = arg;
278
+ lengthCount++;
279
+ break;
280
+ case 1:
281
+ if (keywordDetectedAfterLength) {
282
+ return null;
283
+ }
284
+ offsetY = arg;
285
+ lengthCount++;
286
+ break;
287
+ case 2:
288
+ if (keywordDetectedAfterLength) {
289
+ return null;
290
+ }
291
+ dropShadow.standardDeviation = arg;
292
+ lengthCount++;
293
+ break;
294
+ default:
295
+ return null;
296
+ }
297
+ }
298
+ if (offsetX == null || offsetY == null) {
299
+ return null;
300
+ }
301
+
302
+ dropShadow.offsetX = offsetX;
303
+ dropShadow.offsetY = offsetY;
304
+ return dropShadow;
305
+ }
306
+
307
+ function parseLength(length: string): ?number {
308
+ // matches on args with units like "1.5 5% -80deg"
309
+ const argsWithUnitsRegex = /([+-]?\d*(\.\d+)?)([\w\W]+)?/g;
310
+ const match = argsWithUnitsRegex.exec(length);
311
+
312
+ if (!match || Number.isNaN(match[1])) {
313
+ return null;
314
+ }
315
+
316
+ if (match[3] != null && match[3] !== 'px') {
317
+ return null;
318
+ }
319
+
320
+ return Number(match[1]);
321
+ }