@symbiote-native/engine 0.1.0

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 (195) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +124 -0
  3. package/build/accessibility-info/index.android.d.ts +3 -0
  4. package/build/accessibility-info/index.android.js +166 -0
  5. package/build/accessibility-info/index.d.ts +1 -0
  6. package/build/accessibility-info/index.ios.d.ts +3 -0
  7. package/build/accessibility-info/index.ios.js +219 -0
  8. package/build/accessibility-info/index.js +5 -0
  9. package/build/accessibility-info/shared.d.ts +34 -0
  10. package/build/accessibility-info/shared.js +13 -0
  11. package/build/action-sheet-ios/index.d.ts +36 -0
  12. package/build/action-sheet-ios/index.js +74 -0
  13. package/build/alert/index.android.d.ts +5 -0
  14. package/build/alert/index.android.js +117 -0
  15. package/build/alert/index.d.ts +1 -0
  16. package/build/alert/index.ios.d.ts +7 -0
  17. package/build/alert/index.ios.js +83 -0
  18. package/build/alert/index.js +8 -0
  19. package/build/alert/shared.d.ts +19 -0
  20. package/build/alert/shared.js +17 -0
  21. package/build/animated/animated-component-shared.d.ts +5 -0
  22. package/build/animated/animated-component-shared.js +54 -0
  23. package/build/animated/animation.d.ts +9 -0
  24. package/build/animated/animation.js +6 -0
  25. package/build/animated/animations/base.d.ts +27 -0
  26. package/build/animated/animations/base.js +90 -0
  27. package/build/animated/animations/composition.d.ts +38 -0
  28. package/build/animated/animations/composition.js +236 -0
  29. package/build/animated/animations/decay.d.ts +22 -0
  30. package/build/animated/animations/decay.js +65 -0
  31. package/build/animated/animations/raf.d.ts +5 -0
  32. package/build/animated/animations/raf.js +39 -0
  33. package/build/animated/animations/spring-config.d.ts +6 -0
  34. package/build/animated/animations/spring-config.js +55 -0
  35. package/build/animated/animations/spring.d.ts +50 -0
  36. package/build/animated/animations/spring.js +207 -0
  37. package/build/animated/animations/timing.d.ts +27 -0
  38. package/build/animated/animations/timing.js +101 -0
  39. package/build/animated/animations/tracking.d.ts +14 -0
  40. package/build/animated/animations/tracking.js +43 -0
  41. package/build/animated/bezier.d.ts +1 -0
  42. package/build/animated/bezier.js +101 -0
  43. package/build/animated/color.d.ts +37 -0
  44. package/build/animated/color.js +183 -0
  45. package/build/animated/easing.d.ts +20 -0
  46. package/build/animated/easing.js +96 -0
  47. package/build/animated/event.d.ts +36 -0
  48. package/build/animated/event.js +252 -0
  49. package/build/animated/graph.d.ts +38 -0
  50. package/build/animated/graph.js +227 -0
  51. package/build/animated/index.d.ts +20 -0
  52. package/build/animated/index.js +28 -0
  53. package/build/animated/interpolation-node.d.ts +16 -0
  54. package/build/animated/interpolation-node.js +57 -0
  55. package/build/animated/interpolation.d.ts +22 -0
  56. package/build/animated/interpolation.js +199 -0
  57. package/build/animated/mock.d.ts +56 -0
  58. package/build/animated/mock.js +127 -0
  59. package/build/animated/native/native-animated.d.ts +43 -0
  60. package/build/animated/native/native-animated.js +146 -0
  61. package/build/animated/operators.d.ts +80 -0
  62. package/build/animated/operators.js +266 -0
  63. package/build/animated/props.d.ts +20 -0
  64. package/build/animated/props.js +187 -0
  65. package/build/animated/style.d.ts +26 -0
  66. package/build/animated/style.js +187 -0
  67. package/build/animated/value-xy.d.ts +35 -0
  68. package/build/animated/value-xy.js +106 -0
  69. package/build/animated/value.d.ts +36 -0
  70. package/build/animated/value.js +185 -0
  71. package/build/app-registry/index.d.ts +40 -0
  72. package/build/app-registry/index.js +144 -0
  73. package/build/app-state/index.d.ts +16 -0
  74. package/build/app-state/index.js +105 -0
  75. package/build/appearance/index.d.ts +12 -0
  76. package/build/appearance/index.js +84 -0
  77. package/build/back-handler/index.d.ts +14 -0
  78. package/build/back-handler/index.js +106 -0
  79. package/build/commit.d.ts +16 -0
  80. package/build/commit.js +678 -0
  81. package/build/debug.d.ts +5 -0
  82. package/build/debug.js +18 -0
  83. package/build/dimensions/index.d.ts +28 -0
  84. package/build/dimensions/index.js +148 -0
  85. package/build/dispatch.d.ts +2 -0
  86. package/build/dispatch.js +18 -0
  87. package/build/events/index.d.ts +1 -0
  88. package/build/events/index.js +691 -0
  89. package/build/fabric.d.ts +32 -0
  90. package/build/fabric.js +59 -0
  91. package/build/host-instance/index.d.ts +11 -0
  92. package/build/host-instance/index.js +49 -0
  93. package/build/i18n-manager/index.d.ts +13 -0
  94. package/build/i18n-manager/index.js +91 -0
  95. package/build/index.d.ts +80 -0
  96. package/build/index.js +72 -0
  97. package/build/interaction-manager/index.d.ts +45 -0
  98. package/build/interaction-manager/index.js +222 -0
  99. package/build/keyboard/index.d.ts +31 -0
  100. package/build/keyboard/index.js +142 -0
  101. package/build/layout-animation/index.d.ts +66 -0
  102. package/build/layout-animation/index.js +183 -0
  103. package/build/linking/index.android.d.ts +2 -0
  104. package/build/linking/index.android.js +18 -0
  105. package/build/linking/index.d.ts +1 -0
  106. package/build/linking/index.ios.d.ts +2 -0
  107. package/build/linking/index.ios.js +9 -0
  108. package/build/linking/index.js +6 -0
  109. package/build/linking/shared.d.ts +32 -0
  110. package/build/linking/shared.js +98 -0
  111. package/build/native-events.d.ts +24 -0
  112. package/build/native-events.js +129 -0
  113. package/build/native-modules.d.ts +6 -0
  114. package/build/native-modules.js +57 -0
  115. package/build/node.d.ts +36 -0
  116. package/build/node.js +194 -0
  117. package/build/pan-responder/index.d.ts +53 -0
  118. package/build/pan-responder/index.js +353 -0
  119. package/build/permissions-android/index.d.ts +115 -0
  120. package/build/permissions-android/index.js +185 -0
  121. package/build/pixel-ratio/index.d.ts +8 -0
  122. package/build/pixel-ratio/index.js +27 -0
  123. package/build/platform/index.android.d.ts +22 -0
  124. package/build/platform/index.android.js +60 -0
  125. package/build/platform/index.d.ts +1 -0
  126. package/build/platform/index.ios.d.ts +18 -0
  127. package/build/platform/index.ios.js +62 -0
  128. package/build/platform/index.js +5 -0
  129. package/build/platform/shared.d.ts +25 -0
  130. package/build/platform/shared.js +41 -0
  131. package/build/platform-color.d.ts +19 -0
  132. package/build/platform-color.js +25 -0
  133. package/build/post-commit.d.ts +4 -0
  134. package/build/post-commit.js +16 -0
  135. package/build/process-aspect-ratio.d.ts +1 -0
  136. package/build/process-aspect-ratio.js +34 -0
  137. package/build/process-background-image/index.d.ts +28 -0
  138. package/build/process-background-image/index.js +557 -0
  139. package/build/process-box-shadow/index.d.ts +11 -0
  140. package/build/process-box-shadow/index.js +193 -0
  141. package/build/process-filter.d.ts +31 -0
  142. package/build/process-filter.js +304 -0
  143. package/build/process-font-variant.d.ts +1 -0
  144. package/build/process-font-variant.js +17 -0
  145. package/build/process-transform/index.d.ts +5 -0
  146. package/build/process-transform/index.js +120 -0
  147. package/build/process-transform-origin/index.d.ts +3 -0
  148. package/build/process-transform-origin/index.js +108 -0
  149. package/build/registry.d.ts +31 -0
  150. package/build/registry.js +145 -0
  151. package/build/settings/index.d.ts +8 -0
  152. package/build/settings/index.js +126 -0
  153. package/build/share/index.android.d.ts +3 -0
  154. package/build/share/index.android.js +56 -0
  155. package/build/share/index.d.ts +1 -0
  156. package/build/share/index.ios.d.ts +3 -0
  157. package/build/share/index.ios.js +47 -0
  158. package/build/share/index.js +6 -0
  159. package/build/share/shared.d.ts +32 -0
  160. package/build/share/shared.js +32 -0
  161. package/build/status-bar/index.android.d.ts +5 -0
  162. package/build/status-bar/index.android.js +83 -0
  163. package/build/status-bar/index.d.ts +1 -0
  164. package/build/status-bar/index.ios.d.ts +5 -0
  165. package/build/status-bar/index.ios.js +66 -0
  166. package/build/status-bar/index.js +4 -0
  167. package/build/status-bar/shared.d.ts +22 -0
  168. package/build/status-bar/shared.js +22 -0
  169. package/build/style/index.d.ts +1 -0
  170. package/build/style/index.js +30 -0
  171. package/build/style-registry/index.d.ts +11 -0
  172. package/build/style-registry/index.js +165 -0
  173. package/build/style-sheet/index.d.ts +20 -0
  174. package/build/style-sheet/index.js +121 -0
  175. package/build/styles.d.ts +220 -0
  176. package/build/styles.js +7 -0
  177. package/build/surface.d.ts +16 -0
  178. package/build/surface.js +67 -0
  179. package/build/tags.d.ts +1 -0
  180. package/build/tags.js +10 -0
  181. package/build/text-input-state.d.ts +5 -0
  182. package/build/text-input-state.js +29 -0
  183. package/build/toast-android/index.d.ts +10 -0
  184. package/build/toast-android/index.js +108 -0
  185. package/build/vibration/index.android.d.ts +2 -0
  186. package/build/vibration/index.android.js +18 -0
  187. package/build/vibration/index.d.ts +1 -0
  188. package/build/vibration/index.ios.d.ts +2 -0
  189. package/build/vibration/index.ios.js +54 -0
  190. package/build/vibration/index.js +6 -0
  191. package/build/vibration/shared.d.ts +15 -0
  192. package/build/vibration/shared.js +68 -0
  193. package/build/view-config.d.ts +1 -0
  194. package/build/view-config.js +114 -0
  195. package/package.json +41 -0
@@ -0,0 +1,193 @@
1
+ // JS-side port of RN's processBoxShadow (Libraries/StyleSheet/processBoxShadow.js).
2
+ // RN registers `boxShadow` with enableNativeCSSParsing(), which DEFAULTS TO FALSE, so
3
+ // the stock path parses the CSS string / structured array in JS and sends only the
4
+ // processed array to native; Fabric's C++ never sees the raw string. symbiote was
5
+ // forwarding the raw value, which native (CSS parsing off) silently ignores: shadow
6
+ // rendered nothing. This restores the missing JS parse + per-color processColor.
7
+ //
8
+ // processColor is referenced from ./commit at RUNTIME only (inside the function bodies),
9
+ // never at module-init, so the cyclic import (commit -> here -> commit) has no TDZ hazard.
10
+ import { processColor } from '../commit';
11
+ import { isOpaqueColorValue } from '../platform-color';
12
+ import { dlog } from '../debug';
13
+ // RN processBoxShadow.js:16-19: split args only on the delimiters that are NOT inside
14
+ // a parenthesized color like rgba(0,0,0,1).
15
+ const COMMA_SPLIT_REGEX = /,(?![^()]*\))/;
16
+ const WHITESPACE_SPLIT_REGEX = /\s+(?![^(]*\))/;
17
+ const LENGTH_PARSE_REGEX = /^([+-]?\d*\.?\d+)(px)?$/;
18
+ const NEWLINE_REGEX = /\n/g;
19
+ // A length field may be a CSS string ("22px") or a number; resolve to a number or null.
20
+ // Mirrors RN's `typeof x === 'string' ? parseLength(x) : x`, but typed from `unknown`.
21
+ function resolveLength(value) {
22
+ if (typeof value === 'string')
23
+ return parseLength(value);
24
+ if (typeof value === 'number')
25
+ return value;
26
+ return null;
27
+ }
28
+ // A shadow color may already be a platform int (number) or undefined; processColor only
29
+ // types CSS strings / opaque PlatformColor objects. A number is passed through (already
30
+ // resolved), anything else (incl. undefined) is null, i.e. unprocessable, like RN.
31
+ function processShadowColor(color) {
32
+ if (typeof color === 'number')
33
+ return color;
34
+ if (typeof color === 'string' || isOpaqueColorValue(color))
35
+ return processColor(color);
36
+ return null;
37
+ }
38
+ // RN processBoxShadow.js:30-111. Returns [] on any invalid primitive (matches web: an
39
+ // invalid box-shadow paints nothing rather than a partial shadow).
40
+ export function processBoxShadow(rawBoxShadows) {
41
+ const result = [];
42
+ if (rawBoxShadows == null) {
43
+ return result;
44
+ }
45
+ const boxShadowList = typeof rawBoxShadows === 'string'
46
+ ? parseBoxShadowString(rawBoxShadows.replace(NEWLINE_REGEX, ' '))
47
+ : rawBoxShadows;
48
+ for (const rawBoxShadow of boxShadowList) {
49
+ const parsedBoxShadow = { offsetX: 0, offsetY: 0 };
50
+ for (const arg of Object.keys(rawBoxShadow)) {
51
+ switch (arg) {
52
+ case 'offsetX': {
53
+ const value = resolveLength(rawBoxShadow.offsetX);
54
+ if (value == null) {
55
+ dlog(`processBoxShadow reject: invalid offsetX "${String(rawBoxShadow.offsetX)}"`);
56
+ return [];
57
+ }
58
+ parsedBoxShadow.offsetX = value;
59
+ break;
60
+ }
61
+ case 'offsetY': {
62
+ const value = resolveLength(rawBoxShadow.offsetY);
63
+ if (value == null) {
64
+ dlog(`processBoxShadow reject: invalid offsetY "${String(rawBoxShadow.offsetY)}"`);
65
+ return [];
66
+ }
67
+ parsedBoxShadow.offsetY = value;
68
+ break;
69
+ }
70
+ case 'spreadDistance': {
71
+ const value = resolveLength(rawBoxShadow.spreadDistance);
72
+ if (value == null) {
73
+ dlog(`processBoxShadow reject: invalid spreadDistance`);
74
+ return [];
75
+ }
76
+ parsedBoxShadow.spreadDistance = value;
77
+ break;
78
+ }
79
+ case 'blurRadius': {
80
+ const value = resolveLength(rawBoxShadow.blurRadius);
81
+ if (value == null || value < 0) {
82
+ dlog(`processBoxShadow reject: invalid blurRadius`);
83
+ return [];
84
+ }
85
+ parsedBoxShadow.blurRadius = value;
86
+ break;
87
+ }
88
+ case 'color': {
89
+ const color = processShadowColor(rawBoxShadow.color);
90
+ if (color == null) {
91
+ dlog(`processBoxShadow reject: unprocessable color`);
92
+ return [];
93
+ }
94
+ parsedBoxShadow.color = color;
95
+ break;
96
+ }
97
+ case 'inset':
98
+ if (typeof rawBoxShadow.inset === 'boolean')
99
+ parsedBoxShadow.inset = rawBoxShadow.inset;
100
+ }
101
+ }
102
+ result.push(parsedBoxShadow);
103
+ }
104
+ return result;
105
+ }
106
+ // RN processBoxShadow.js:113-197. Walks each comma-separated shadow, classifying each
107
+ // whitespace-separated arg as a color, the `inset` keyword, or a length by position.
108
+ function parseBoxShadowString(rawBoxShadows) {
109
+ const result = [];
110
+ for (const rawBoxShadow of rawBoxShadows
111
+ .split(COMMA_SPLIT_REGEX)
112
+ .map(bS => bS.trim())
113
+ .filter(bS => bS !== '')) {
114
+ const boxShadow = { offsetX: 0, offsetY: 0 };
115
+ let offsetX;
116
+ let offsetY;
117
+ let keywordDetectedAfterLength = false;
118
+ let lengthCount = 0;
119
+ const args = rawBoxShadow.split(WHITESPACE_SPLIT_REGEX);
120
+ for (const arg of args) {
121
+ const processedColor = processColor(arg);
122
+ if (processedColor != null) {
123
+ if (boxShadow.color != null) {
124
+ return [];
125
+ }
126
+ if (offsetX != null) {
127
+ keywordDetectedAfterLength = true;
128
+ }
129
+ boxShadow.color = arg;
130
+ continue;
131
+ }
132
+ if (arg === 'inset') {
133
+ if (boxShadow.inset != null) {
134
+ return [];
135
+ }
136
+ if (offsetX != null) {
137
+ keywordDetectedAfterLength = true;
138
+ }
139
+ boxShadow.inset = true;
140
+ continue;
141
+ }
142
+ switch (lengthCount) {
143
+ case 0:
144
+ offsetX = arg;
145
+ lengthCount++;
146
+ break;
147
+ case 1:
148
+ if (keywordDetectedAfterLength) {
149
+ return [];
150
+ }
151
+ offsetY = arg;
152
+ lengthCount++;
153
+ break;
154
+ case 2:
155
+ if (keywordDetectedAfterLength) {
156
+ return [];
157
+ }
158
+ boxShadow.blurRadius = arg;
159
+ lengthCount++;
160
+ break;
161
+ case 3:
162
+ if (keywordDetectedAfterLength) {
163
+ return [];
164
+ }
165
+ boxShadow.spreadDistance = arg;
166
+ lengthCount++;
167
+ break;
168
+ default:
169
+ return [];
170
+ }
171
+ }
172
+ if (offsetX == null || offsetY == null) {
173
+ return [];
174
+ }
175
+ boxShadow.offsetX = offsetX;
176
+ boxShadow.offsetY = offsetY;
177
+ result.push(boxShadow);
178
+ }
179
+ return result;
180
+ }
181
+ // RN processBoxShadow.js:199-214. Accepts a unitless 0 or any `<n>px`; rejects a
182
+ // non-zero number with no unit (CSS requires a unit on lengths).
183
+ function parseLength(length) {
184
+ const match = LENGTH_PARSE_REGEX.exec(length);
185
+ if (!match) {
186
+ return null;
187
+ }
188
+ const value = parseFloat(match[1]);
189
+ if (match[2] == null && value !== 0) {
190
+ return null;
191
+ }
192
+ return value;
193
+ }
@@ -0,0 +1,31 @@
1
+ export interface IParsedDropShadow {
2
+ offsetX: number;
3
+ offsetY: number;
4
+ standardDeviation?: number;
5
+ color?: unknown;
6
+ }
7
+ export type IParsedFilter = {
8
+ brightness: number;
9
+ } | {
10
+ blur: number;
11
+ } | {
12
+ contrast: number;
13
+ } | {
14
+ grayscale: number;
15
+ } | {
16
+ hueRotate: number;
17
+ } | {
18
+ invert: number;
19
+ } | {
20
+ opacity: number;
21
+ } | {
22
+ saturate: number;
23
+ } | {
24
+ sepia: number;
25
+ } | {
26
+ dropShadow: IParsedDropShadow;
27
+ };
28
+ type IRawDropShadow = Record<string, unknown>;
29
+ type IRawFilterFunction = Record<string, unknown>;
30
+ export declare function processFilter(filter: ReadonlyArray<IRawFilterFunction> | string | undefined): IParsedFilter[];
31
+ export type { IRawDropShadow, IRawFilterFunction };
@@ -0,0 +1,304 @@
1
+ // JS-side port of RN's processFilter (Libraries/StyleSheet/processFilter.js).
2
+ // Same root cause as boxShadow: `filter` registers with enableNativeCSSParsing()
3
+ // (DEFAULT FALSE), so RN parses the CSS string / array in JS and forwards only the
4
+ // structured result. symbiote forwarded the raw value; the array form already worked
5
+ // (Fabric accepts it raw, `filter:[{brightness:0.5}]` was device-verified), but the
6
+ // CSS-string form and drop-shadow color processing were missing. This restores them.
7
+ //
8
+ // processColor is referenced from ./commit at RUNTIME only (inside function bodies),
9
+ // never at module-init, so the cyclic import (commit -> here -> commit) has no TDZ hazard.
10
+ import { processColor } from './commit';
11
+ import { isOpaqueColorValue } from './platform-color';
12
+ import { dlog } from './debug';
13
+ // RN processFilter.js:19-24: pre-compiled patterns.
14
+ const NEWLINE_REGEX = /\n/g;
15
+ const FILTER_FUNCTION_REGEX = /([\w-]+)\(([^()]*|\([^()]*\)|[^()]*\([^()]*\)[^()]*)\)/g;
16
+ const ARGS_WITH_UNITS_REGEX = /([+-]?\d*(\.\d+)?)([a-zA-Z%]+)?/g;
17
+ const WHITESPACE_SPLIT_REGEX = /\s+(?![^(]*\))/;
18
+ const LENGTH_PARSE_REGEX = /([+-]?\d*(\.\d+)?)([\w\W]+)?/g;
19
+ const RADIANS_TO_DEGREES = 180 / Math.PI;
20
+ function isRecord(value) {
21
+ return typeof value === 'object' && value !== null;
22
+ }
23
+ // A length field may be a CSS string or a number; resolve to a number or null.
24
+ function resolveLength(value) {
25
+ if (typeof value === 'string')
26
+ return parseLength(value);
27
+ if (typeof value === 'number')
28
+ return value;
29
+ return null;
30
+ }
31
+ // A drop-shadow color may already be a platform int (number) or undefined; processColor
32
+ // only types CSS strings / opaque PlatformColor objects. Number passes through, anything
33
+ // else (incl. undefined) is null: unprocessable, like RN.
34
+ function processShadowColor(color) {
35
+ if (typeof color === 'number')
36
+ return color;
37
+ if (typeof color === 'string' || isOpaqueColorValue(color))
38
+ return processColor(color);
39
+ return null;
40
+ }
41
+ // RN processFilter.js:45-124. Returns [] on any invalid primitive (web semantics: an
42
+ // invalid filter applies none of them, so the missing effect is obvious).
43
+ export function processFilter(filter) {
44
+ const result = [];
45
+ if (filter == null) {
46
+ return result;
47
+ }
48
+ if (typeof filter === 'string') {
49
+ const text = filter.replace(NEWLINE_REGEX, ' ');
50
+ // Matches functions with args and nested functions like
51
+ // "drop-shadow(10 10 10 rgba(0, 0, 0, 1))".
52
+ FILTER_FUNCTION_REGEX.lastIndex = 0;
53
+ let matches;
54
+ while ((matches = FILTER_FUNCTION_REGEX.exec(text)) !== null) {
55
+ const filterName = matches[1].toLowerCase();
56
+ if (filterName === 'drop-shadow') {
57
+ const dropShadow = parseDropShadow(matches[2]);
58
+ if (dropShadow != null) {
59
+ result.push({ dropShadow });
60
+ }
61
+ else {
62
+ dlog(`processFilter reject: invalid drop-shadow "${matches[2]}"`);
63
+ return [];
64
+ }
65
+ }
66
+ else {
67
+ const camelizedName = filterName === 'hue-rotate' ? 'hueRotate' : filterName;
68
+ const amount = getFilterAmount(camelizedName, matches[2]);
69
+ if (amount != null) {
70
+ result.push(filterEntry(camelizedName, amount));
71
+ }
72
+ else {
73
+ dlog(`processFilter reject: invalid ${camelizedName} "${matches[2]}"`);
74
+ return [];
75
+ }
76
+ }
77
+ }
78
+ }
79
+ else if (Array.isArray(filter)) {
80
+ for (const filterFunction of filter) {
81
+ const [filterName, filterValue] = Object.entries(filterFunction)[0];
82
+ if (filterName === 'dropShadow') {
83
+ const dropShadow = isDropShadowValue(filterValue) ? parseDropShadow(filterValue) : null;
84
+ if (dropShadow == null) {
85
+ dlog(`processFilter reject: invalid dropShadow in array`);
86
+ return [];
87
+ }
88
+ result.push({ dropShadow });
89
+ }
90
+ else {
91
+ const amount = getFilterAmount(filterName, filterValue);
92
+ if (amount != null) {
93
+ result.push(filterEntry(filterName, amount));
94
+ }
95
+ else {
96
+ dlog(`processFilter reject: invalid ${filterName} in array`);
97
+ return [];
98
+ }
99
+ }
100
+ }
101
+ }
102
+ else {
103
+ throw new TypeError(`${typeof filter} filter is not a string or array`);
104
+ }
105
+ return result;
106
+ }
107
+ // RN sets `filterFunction[camelizedName] = amount` and pushes the loose object. We
108
+ // build the discriminated union explicitly to keep it typed without an `as` cast.
109
+ function filterEntry(name, amount) {
110
+ switch (name) {
111
+ case 'brightness':
112
+ return { brightness: amount };
113
+ case 'blur':
114
+ return { blur: amount };
115
+ case 'contrast':
116
+ return { contrast: amount };
117
+ case 'grayscale':
118
+ return { grayscale: amount };
119
+ case 'hueRotate':
120
+ return { hueRotate: amount };
121
+ case 'invert':
122
+ return { invert: amount };
123
+ case 'opacity':
124
+ return { opacity: amount };
125
+ case 'saturate':
126
+ return { saturate: amount };
127
+ default:
128
+ return { sepia: amount };
129
+ }
130
+ }
131
+ function isDropShadowValue(value) {
132
+ return typeof value === 'string' || (isRecord(value) && 'offsetX' in value);
133
+ }
134
+ // RN processFilter.js:126-186.
135
+ function getFilterAmount(filterName, filterArgs) {
136
+ let filterArgAsNumber;
137
+ let unit;
138
+ if (typeof filterArgs === 'string') {
139
+ // Matches args with units like "1.5 5% -80deg".
140
+ ARGS_WITH_UNITS_REGEX.lastIndex = 0;
141
+ const match = ARGS_WITH_UNITS_REGEX.exec(filterArgs);
142
+ if (!match || isNaN(Number(match[1]))) {
143
+ return undefined;
144
+ }
145
+ filterArgAsNumber = Number(match[1]);
146
+ unit = match[3];
147
+ }
148
+ else if (typeof filterArgs === 'number') {
149
+ filterArgAsNumber = filterArgs;
150
+ }
151
+ else {
152
+ return undefined;
153
+ }
154
+ switch (filterName) {
155
+ // hueRotate takes an angle that can carry a unit and be negative; bare 0 is allowed.
156
+ case 'hueRotate':
157
+ if (filterArgAsNumber === 0) {
158
+ return 0;
159
+ }
160
+ if (unit !== 'deg' && unit !== 'rad') {
161
+ return undefined;
162
+ }
163
+ return unit === 'rad' ? RADIANS_TO_DEGREES * filterArgAsNumber : filterArgAsNumber;
164
+ // blur takes any non-negative CSS length that is not a percent; RN only has DIPs.
165
+ case 'blur':
166
+ if ((unit != null && unit !== 'px') || filterArgAsNumber < 0) {
167
+ return undefined;
168
+ }
169
+ return filterArgAsNumber;
170
+ // The rest take a non-negative number or percentage (50% == 0.5).
171
+ case 'brightness':
172
+ case 'contrast':
173
+ case 'grayscale':
174
+ case 'invert':
175
+ case 'opacity':
176
+ case 'saturate':
177
+ case 'sepia':
178
+ if ((unit != null && unit !== '%' && unit !== 'px') || filterArgAsNumber < 0) {
179
+ return undefined;
180
+ }
181
+ return unit === '%' ? filterArgAsNumber / 100 : filterArgAsNumber;
182
+ default:
183
+ return undefined;
184
+ }
185
+ }
186
+ // RN processFilter.js:188-256.
187
+ function parseDropShadow(rawDropShadow) {
188
+ const dropShadow = typeof rawDropShadow === 'string' ? parseDropShadowString(rawDropShadow) : rawDropShadow;
189
+ if (dropShadow == null) {
190
+ return null;
191
+ }
192
+ const parsedDropShadow = { offsetX: 0, offsetY: 0 };
193
+ let offsetX;
194
+ let offsetY;
195
+ for (const arg of Object.keys(dropShadow)) {
196
+ switch (arg) {
197
+ case 'offsetX': {
198
+ const value = resolveLength(dropShadow.offsetX);
199
+ if (value == null) {
200
+ return null;
201
+ }
202
+ offsetX = value;
203
+ break;
204
+ }
205
+ case 'offsetY': {
206
+ const value = resolveLength(dropShadow.offsetY);
207
+ if (value == null) {
208
+ return null;
209
+ }
210
+ offsetY = value;
211
+ break;
212
+ }
213
+ case 'standardDeviation': {
214
+ const value = resolveLength(dropShadow.standardDeviation);
215
+ if (value == null || value < 0) {
216
+ return null;
217
+ }
218
+ parsedDropShadow.standardDeviation = value;
219
+ break;
220
+ }
221
+ case 'color': {
222
+ const color = processShadowColor(dropShadow.color);
223
+ if (color == null) {
224
+ return null;
225
+ }
226
+ parsedDropShadow.color = color;
227
+ break;
228
+ }
229
+ default:
230
+ return null;
231
+ }
232
+ }
233
+ if (offsetX == null || offsetY == null) {
234
+ return null;
235
+ }
236
+ parsedDropShadow.offsetX = offsetX;
237
+ parsedDropShadow.offsetY = offsetY;
238
+ return parsedDropShadow;
239
+ }
240
+ // RN processFilter.js:258-312.
241
+ function parseDropShadowString(rawDropShadow) {
242
+ const dropShadow = { offsetX: 0, offsetY: 0 };
243
+ let offsetX;
244
+ let offsetY;
245
+ let lengthCount = 0;
246
+ let keywordDetectedAfterLength = false;
247
+ for (const arg of rawDropShadow.split(WHITESPACE_SPLIT_REGEX)) {
248
+ const processedColor = processColor(arg);
249
+ if (processedColor != null) {
250
+ if (dropShadow.color != null) {
251
+ return null;
252
+ }
253
+ if (offsetX != null) {
254
+ keywordDetectedAfterLength = true;
255
+ }
256
+ dropShadow.color = arg;
257
+ continue;
258
+ }
259
+ switch (lengthCount) {
260
+ case 0:
261
+ offsetX = arg;
262
+ lengthCount++;
263
+ break;
264
+ case 1:
265
+ if (keywordDetectedAfterLength) {
266
+ return null;
267
+ }
268
+ offsetY = arg;
269
+ lengthCount++;
270
+ break;
271
+ case 2:
272
+ if (keywordDetectedAfterLength) {
273
+ return null;
274
+ }
275
+ dropShadow.standardDeviation = arg;
276
+ lengthCount++;
277
+ break;
278
+ default:
279
+ return null;
280
+ }
281
+ }
282
+ if (offsetX == null || offsetY == null) {
283
+ return null;
284
+ }
285
+ dropShadow.offsetX = offsetX;
286
+ dropShadow.offsetY = offsetY;
287
+ return dropShadow;
288
+ }
289
+ // RN processFilter.js:314-332. Accepts a unitless 0 or any `<n>px`; rejects a non-zero
290
+ // unitless length or a non-px unit.
291
+ function parseLength(length) {
292
+ LENGTH_PARSE_REGEX.lastIndex = 0;
293
+ const match = LENGTH_PARSE_REGEX.exec(length);
294
+ if (!match || isNaN(Number(match[1]))) {
295
+ return null;
296
+ }
297
+ if (match[3] != null && match[3] !== 'px') {
298
+ return null;
299
+ }
300
+ if (match[3] == null && match[1] !== '0') {
301
+ return null;
302
+ }
303
+ return Number(match[1]);
304
+ }
@@ -0,0 +1 @@
1
+ export declare function processFontVariant(fontVariant: ReadonlyArray<string> | string): ReadonlyArray<string>;
@@ -0,0 +1,17 @@
1
+ // JS-side port of RN's processFontVariant (Libraries/StyleSheet/processFontVariant.js).
2
+ // Same root cause family as boxShadow/filter: RN registers `fontVariant` with a JS
3
+ // `process` because enableNativeCSSParsing() defaults to false, so a space-separated CSS
4
+ // string ('small-caps tabular-nums') is split into the array native expects IN JS. An
5
+ // array (the common, already-working form) passes through untouched; this is a no-op
6
+ // for it.
7
+ // RN processFontVariant.js:15-28. Array → array; space-separated string → array of variants.
8
+ export function processFontVariant(fontVariant) {
9
+ if (Array.isArray(fontVariant)) {
10
+ return fontVariant;
11
+ }
12
+ if (typeof fontVariant === 'string') {
13
+ return fontVariant.split(' ').filter(Boolean);
14
+ }
15
+ // Neither an array nor a string: nothing to split, hand back an empty variant list.
16
+ return [];
17
+ }
@@ -0,0 +1,5 @@
1
+ type ITransformEntry = Record<string, ITransformValue>;
2
+ type ITransformValue = number | string | Array<number | string> | undefined;
3
+ type IRawTransform = Record<string, unknown>;
4
+ export declare function processTransform(transform: ReadonlyArray<IRawTransform> | string | undefined): ReadonlyArray<IRawTransform>;
5
+ export type { ITransformEntry, IRawTransform };
@@ -0,0 +1,120 @@
1
+ // JS-side port of RN's processTransform (Libraries/StyleSheet/processTransform.js).
2
+ // Same root cause as transformOrigin/boxShadow: ReactNativeStyleAttributes registers
3
+ // `transform` with `nativeCSSParsing ? true : {process: processTransform}`, and
4
+ // enableNativeCSSParsing() DEFAULTS TO FALSE, so RN's stock path runs processTransform
5
+ // IN JS. It only does work for a STRING input (parses the CSS string into the entry
6
+ // array); an ARRAY input is returned UNCHANGED. symbiote forwarded a raw string, which
7
+ // Android native cast to ReadableArray and crashed with
8
+ // `java.lang.String cannot be cast to com.facebook.react.bridge.ReadableArray`. This
9
+ // restores the missing JS parse so native always receives the entry array.
10
+ //
11
+ // CRITICAL: the animated / sticky-header hot path produces transform ARRAYS and flows
12
+ // through commit's processValue on every flush, so the array branch MUST return the
13
+ // byte-identical reference (no decompose, no clone). RN's only array-path work is
14
+ // _validateTransforms, which is __DEV__-only and THROWS via invariant; we never throw
15
+ // into the commit path, so we run a non-throwing check that dlogs and still returns the
16
+ // array unchanged (matching the boxShadow/transformOrigin idiom).
17
+ import { dlog } from '../debug';
18
+ // RN processTransform.js:28: matches each `name(args)` in the CSS string.
19
+ const TRANSFORM_REGEX = /(\w+)\(([^)]+)\)/g;
20
+ // RN processTransform.js:59: splits one arg into [, number, , unit?].
21
+ const ARG_WITH_UNITS_REGEX = /([+-]?\d+(\.\d+)?)([a-zA-Z]+|%)?/g;
22
+ // RN processTransform.js:63: pulls every signed/decimal number out of a matrix arg list.
23
+ const MATRIX_NUMBER_REGEX = /[+-]?\d+(\.\d+)?/g;
24
+ // RN processTransform.js:52-139. Mirrors _getKeyAndValueFromCSSTransform: turns one
25
+ // `key(args)` pair into the entry value native expects.
26
+ function getKeyAndValueFromCSSTransform(key, args) {
27
+ switch (key) {
28
+ case 'matrix': {
29
+ // RN processTransform.js:62-63.
30
+ const numbers = args.match(MATRIX_NUMBER_REGEX);
31
+ return { key, value: numbers == null ? undefined : numbers.map(Number) };
32
+ }
33
+ case 'translate':
34
+ case 'translate3d': {
35
+ // RN processTransform.js:64-113.
36
+ const parsedArgs = [];
37
+ ARG_WITH_UNITS_REGEX.lastIndex = 0;
38
+ let matches;
39
+ while ((matches = ARG_WITH_UNITS_REGEX.exec(args))) {
40
+ const value = Number(matches[1]);
41
+ const unitOfMeasurement = matches[3];
42
+ if (value !== 0 && !unitOfMeasurement) {
43
+ dlog(`processTransform: ${key}(${args}) length must have a unit unless 0`);
44
+ }
45
+ if (unitOfMeasurement === '%') {
46
+ parsedArgs.push(`${value}%`);
47
+ }
48
+ else {
49
+ parsedArgs.push(value);
50
+ }
51
+ }
52
+ // RN processTransform.js:109-111: a single-axis translate gets an implicit y of 0.
53
+ if (parsedArgs.length === 1) {
54
+ parsedArgs.push(0);
55
+ }
56
+ // RN normalizes translate3d down to the `translate` key.
57
+ return { key: 'translate', value: parsedArgs };
58
+ }
59
+ case 'translateX':
60
+ case 'translateY':
61
+ case 'perspective': {
62
+ // RN processTransform.js:114-134.
63
+ ARG_WITH_UNITS_REGEX.lastIndex = 0;
64
+ const argMatches = ARG_WITH_UNITS_REGEX.exec(args);
65
+ if (argMatches == null || argMatches.length === 0) {
66
+ return { key, value: undefined };
67
+ }
68
+ const value = Number(argMatches[1]);
69
+ const unitOfMeasurement = argMatches[3];
70
+ if (value !== 0 && !unitOfMeasurement) {
71
+ dlog(`processTransform: ${key}(${args}) must have a unit unless 0`);
72
+ }
73
+ return { key, value };
74
+ }
75
+ default:
76
+ // RN processTransform.js:136-137: a numeric arg (scale, rotate '10' would be NaN)
77
+ // becomes a number; an angle string ('6deg', '1.16rad') stays a string.
78
+ return { key, value: isNaN(Number(args)) ? args : Number(args) };
79
+ }
80
+ }
81
+ // RN processTransform.js:24-50. STRING input is parsed into the entry array; ARRAY input
82
+ // is returned UNCHANGED (RN's only array work, _validateTransforms, is __DEV__-only and
83
+ // throws; we never throw, so we skip it and run a non-throwing dlog sanity check).
84
+ export function processTransform(transform) {
85
+ if (transform == null) {
86
+ return [];
87
+ }
88
+ if (typeof transform !== 'string') {
89
+ // Hot path: animated / sticky-header transforms arrive here as arrays. Return the
90
+ // same reference (no decompose, no clone) so the flush is a no-op.
91
+ warnInvalidTransforms(transform);
92
+ return transform;
93
+ }
94
+ TRANSFORM_REGEX.lastIndex = 0;
95
+ const transformArray = [];
96
+ let matches;
97
+ while ((matches = TRANSFORM_REGEX.exec(transform))) {
98
+ const { key, value } = getKeyAndValueFromCSSTransform(matches[1], matches[2]);
99
+ if (value !== undefined) {
100
+ transformArray.push({ [key]: value });
101
+ }
102
+ }
103
+ return transformArray;
104
+ }
105
+ // RN processTransform.js:141-159 (_validateTransforms) rewritten to NEVER throw: it only
106
+ // dlogs the same conditions RN's invariant would have flagged, then returns. The commit
107
+ // path keeps the array regardless: an invalid transform is the caller's bug, not ours to
108
+ // abort a frame over.
109
+ function warnInvalidTransforms(transform) {
110
+ for (const transformation of transform) {
111
+ const keys = Object.keys(transformation);
112
+ if (keys.length !== 1) {
113
+ dlog(`processTransform: each transform object must have exactly one key, got ${keys.length}`);
114
+ continue;
115
+ }
116
+ if (keys[0] === 'matrix' && transform.length > 1) {
117
+ dlog('processTransform: a matrix transform must be the only transform in the list');
118
+ }
119
+ }
120
+ }
@@ -0,0 +1,3 @@
1
+ type ITransformOriginValue = string | number;
2
+ export declare function processTransformOrigin(transformOrigin: Array<ITransformOriginValue> | string | undefined): Array<ITransformOriginValue>;
3
+ export {};