@symbiote-native/engine 0.5.0 → 1.0.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.
- package/README.md +39 -14
- package/android/CMakeLists.txt +51 -0
- package/android/build.gradle +90 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/cpp/SymbioteEngineJni.cpp +72 -0
- package/android/src/main/java/dev/symbiotenative/engine/SymbioteEngineModule.kt +43 -0
- package/android/src/main/java/dev/symbiotenative/engine/SymbioteEnginePackage.kt +35 -0
- package/build/accessibility-info/shared.js +1 -1
- package/build/accessibility-props.d.ts +1 -8
- package/build/accessibility-props.js +13 -16
- package/build/animated/animations/composition.d.ts +1 -1
- package/build/animated/animations/composition.js +18 -4
- package/build/animated/easing.d.ts +3 -2
- package/build/animated/easing.js +17 -88
- package/build/animated/event.js +6 -1
- package/build/animated/host-binding.d.ts +1 -1
- package/build/animated/host-binding.js +19 -4
- package/build/animated/index.d.ts +1 -1
- package/build/animated/mock.d.ts +1 -19
- package/build/animated/props.js +1 -1
- package/build/animated/rgba.js +16 -50
- package/build/events/index.js +88 -40
- package/build/fabric-props.d.ts +1 -1
- package/build/fabric-props.js +116 -184
- package/build/fabric.d.ts +9 -0
- package/build/fabric.js +32 -0
- package/build/host-access.d.ts +125 -0
- package/build/host-access.js +280 -0
- package/build/host-behavior.d.ts +84 -21
- package/build/host-behavior.js +196 -30
- package/build/image-source-write.d.ts +16 -0
- package/build/image-source-write.js +65 -0
- package/build/imperative.d.ts +49 -0
- package/build/imperative.js +258 -0
- package/build/index.d.ts +14 -7
- package/build/index.js +53 -10
- package/build/mutation-buffer.d.ts +222 -0
- package/build/mutation-buffer.js +491 -0
- package/build/native-engine.d.ts +182 -0
- package/build/native-engine.js +178 -0
- package/build/native-tree-host.d.ts +25 -0
- package/build/native-tree-host.js +66 -0
- package/build/node.d.ts +172 -57
- package/build/node.js +839 -383
- package/build/pan-responder/index.js +27 -52
- package/build/platform-color/index.d.ts +1 -1
- package/build/platform-color/index.js +11 -4
- package/build/process-background-image/index.js +30 -566
- package/build/process-background-longhands.d.ts +4 -0
- package/build/process-background-longhands.js +44 -0
- package/build/process-box-shadow/index.js +23 -187
- package/build/process-filter.js +27 -300
- package/build/process-transform/index.d.ts +1 -1
- package/build/process-transform/index.js +25 -107
- package/build/process-transform-origin/index.d.ts +1 -1
- package/build/process-transform-origin/index.js +29 -102
- package/build/registry.d.ts +36 -0
- package/build/registry.js +73 -0
- package/build/sound-manager/index.d.ts +3 -0
- package/build/sound-manager/index.js +36 -0
- package/build/structured-style.d.ts +10 -0
- package/build/structured-style.js +180 -0
- package/build/style-registry/index.d.ts +14 -0
- package/build/style-registry/index.js +60 -11
- package/build/surface.d.ts +31 -2
- package/build/surface.js +138 -56
- package/build/text-input-state.d.ts +1 -0
- package/build/text-input-state.js +17 -3
- package/build/tree-host.d.ts +307 -0
- package/build/tree-host.js +211 -0
- package/build/view-config.js +4 -4
- package/codegen-specs/NativeSymbioteEngine.ts +27 -0
- package/cpp/SymbioteDebug.cpp +51 -0
- package/cpp/SymbioteDebug.h +54 -0
- package/cpp/SymbioteEngineBindings.cpp +232 -0
- package/cpp/SymbioteEngineBindings.h +59 -0
- package/cpp/SymbioteFabricProps.cpp +2619 -0
- package/cpp/SymbioteFabricProps.h +223 -0
- package/cpp/SymbioteTree.cpp +2478 -0
- package/cpp/SymbioteTree.h +257 -0
- package/ios/SymbioteEngineModule.h +25 -0
- package/ios/SymbioteEngineModule.mm +44 -0
- package/package.json +31 -3
- package/react-native.config.cjs +23 -0
- package/symbiote-engine.podspec +42 -0
- package/build/animated/bezier.d.ts +0 -1
- package/build/animated/bezier.js +0 -102
- package/build/commit.d.ts +0 -49
- package/build/commit.js +0 -1058
- package/build/tags.d.ts +0 -2
- package/build/tags.js +0 -40
|
@@ -1,189 +1,25 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
// A shadow color may already be a platform int (number) or undefined; processColor only
|
|
25
|
-
// types CSS strings / opaque PlatformColor objects. A number is passed through (already
|
|
26
|
-
// resolved), anything else (incl. undefined) is null, i.e. unprocessable, like RN.
|
|
27
|
-
function processShadowColor(color) {
|
|
28
|
-
if (typeof color === 'number')
|
|
29
|
-
return color;
|
|
30
|
-
if (typeof color === 'string' || isOpaqueColorValue(color))
|
|
31
|
-
return processColor(color);
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
// RN processBoxShadow.js:30-111. Returns [] on any invalid primitive (matches web: an
|
|
35
|
-
// invalid box-shadow paints nothing rather than a partial shadow).
|
|
1
|
+
// RN's own processBoxShadow, imported rather than ported.
|
|
2
|
+
//
|
|
3
|
+
// Why it is needed at all: RN registers `boxShadow` with `nativeCSSParsing ? true : {process:
|
|
4
|
+
// processBoxShadow}`, and enableNativeCSSParsing() defaults to false - so the stock path parses
|
|
5
|
+
// the CSS string / structured array in JS and sends native only the processed array. Symbiote
|
|
6
|
+
// forwarded the raw value, which native silently ignores with CSS parsing off: the shadow simply
|
|
7
|
+
// did not paint.
|
|
8
|
+
//
|
|
9
|
+
// Why no try/catch, unlike processTransform's wrapper: upstream has zero `invariant` and zero
|
|
10
|
+
// `throw` sites here. It answers `[]` for anything it cannot parse - web semantics, where an
|
|
11
|
+
// invalid box-shadow paints nothing rather than a partial shadow. A guard that can never fire is
|
|
12
|
+
// worse than none: it reads as verified and has never run.
|
|
13
|
+
//
|
|
14
|
+
// What changed by importing: colour now goes through RN's own `processColor` (upstream imports it
|
|
15
|
+
// directly at processBoxShadow.js:14) instead of our injected `setColorProcessor` seam. On a real
|
|
16
|
+
// host those are the same function - bootstrapHost injects exactly `processColor` and nothing in
|
|
17
|
+
// the repo passes a custom one. The seam still governs every OTHER colour prop; it simply no
|
|
18
|
+
// longer governs the colours inside a box-shadow.
|
|
19
|
+
//
|
|
20
|
+
// @ts-expect-error - untyped Flow source. Metro compiles it; vitest.config.ts strips the types.
|
|
21
|
+
import processBoxShadowUpstream from 'react-native/Libraries/StyleSheet/processBoxShadow';
|
|
36
22
|
export function processBoxShadow(rawBoxShadows) {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
return result;
|
|
40
|
-
}
|
|
41
|
-
const boxShadowList = typeof rawBoxShadows === 'string'
|
|
42
|
-
? parseBoxShadowString(rawBoxShadows.replace(NEWLINE_REGEX, ' '))
|
|
43
|
-
: rawBoxShadows;
|
|
44
|
-
for (const rawBoxShadow of boxShadowList) {
|
|
45
|
-
const parsedBoxShadow = { offsetX: 0, offsetY: 0 };
|
|
46
|
-
for (const arg of Object.keys(rawBoxShadow)) {
|
|
47
|
-
switch (arg) {
|
|
48
|
-
case 'offsetX': {
|
|
49
|
-
const value = resolveLength(rawBoxShadow.offsetX);
|
|
50
|
-
if (value == null) {
|
|
51
|
-
dlog(`processBoxShadow reject: invalid offsetX "${String(rawBoxShadow.offsetX)}"`);
|
|
52
|
-
return [];
|
|
53
|
-
}
|
|
54
|
-
parsedBoxShadow.offsetX = value;
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
case 'offsetY': {
|
|
58
|
-
const value = resolveLength(rawBoxShadow.offsetY);
|
|
59
|
-
if (value == null) {
|
|
60
|
-
dlog(`processBoxShadow reject: invalid offsetY "${String(rawBoxShadow.offsetY)}"`);
|
|
61
|
-
return [];
|
|
62
|
-
}
|
|
63
|
-
parsedBoxShadow.offsetY = value;
|
|
64
|
-
break;
|
|
65
|
-
}
|
|
66
|
-
case 'spreadDistance': {
|
|
67
|
-
const value = resolveLength(rawBoxShadow.spreadDistance);
|
|
68
|
-
if (value == null) {
|
|
69
|
-
dlog(`processBoxShadow reject: invalid spreadDistance`);
|
|
70
|
-
return [];
|
|
71
|
-
}
|
|
72
|
-
parsedBoxShadow.spreadDistance = value;
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
case 'blurRadius': {
|
|
76
|
-
const value = resolveLength(rawBoxShadow.blurRadius);
|
|
77
|
-
if (value == null || value < 0) {
|
|
78
|
-
dlog(`processBoxShadow reject: invalid blurRadius`);
|
|
79
|
-
return [];
|
|
80
|
-
}
|
|
81
|
-
parsedBoxShadow.blurRadius = value;
|
|
82
|
-
break;
|
|
83
|
-
}
|
|
84
|
-
case 'color': {
|
|
85
|
-
const color = processShadowColor(rawBoxShadow.color);
|
|
86
|
-
if (color == null) {
|
|
87
|
-
dlog(`processBoxShadow reject: unprocessable color`);
|
|
88
|
-
return [];
|
|
89
|
-
}
|
|
90
|
-
parsedBoxShadow.color = color;
|
|
91
|
-
break;
|
|
92
|
-
}
|
|
93
|
-
case 'inset':
|
|
94
|
-
if (typeof rawBoxShadow.inset === 'boolean')
|
|
95
|
-
parsedBoxShadow.inset = rawBoxShadow.inset;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
result.push(parsedBoxShadow);
|
|
99
|
-
}
|
|
100
|
-
return result;
|
|
101
|
-
}
|
|
102
|
-
// RN processBoxShadow.js:113-197. Walks each comma-separated shadow, classifying each
|
|
103
|
-
// whitespace-separated arg as a color, the `inset` keyword, or a length by position.
|
|
104
|
-
function parseBoxShadowString(rawBoxShadows) {
|
|
105
|
-
const result = [];
|
|
106
|
-
for (const rawBoxShadow of rawBoxShadows
|
|
107
|
-
.split(COMMA_SPLIT_REGEX)
|
|
108
|
-
.map(bS => bS.trim())
|
|
109
|
-
.filter(bS => bS !== '')) {
|
|
110
|
-
const boxShadow = { offsetX: 0, offsetY: 0 };
|
|
111
|
-
let offsetX;
|
|
112
|
-
let offsetY;
|
|
113
|
-
let keywordDetectedAfterLength = false;
|
|
114
|
-
let lengthCount = 0;
|
|
115
|
-
const args = rawBoxShadow.split(WHITESPACE_SPLIT_REGEX);
|
|
116
|
-
for (const arg of args) {
|
|
117
|
-
const processedColor = processColor(arg);
|
|
118
|
-
if (processedColor != null) {
|
|
119
|
-
if (boxShadow.color != null) {
|
|
120
|
-
return [];
|
|
121
|
-
}
|
|
122
|
-
if (offsetX != null) {
|
|
123
|
-
keywordDetectedAfterLength = true;
|
|
124
|
-
}
|
|
125
|
-
boxShadow.color = arg;
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
if (arg === 'inset') {
|
|
129
|
-
if (boxShadow.inset != null) {
|
|
130
|
-
return [];
|
|
131
|
-
}
|
|
132
|
-
if (offsetX != null) {
|
|
133
|
-
keywordDetectedAfterLength = true;
|
|
134
|
-
}
|
|
135
|
-
boxShadow.inset = true;
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
switch (lengthCount) {
|
|
139
|
-
case 0:
|
|
140
|
-
offsetX = arg;
|
|
141
|
-
lengthCount++;
|
|
142
|
-
break;
|
|
143
|
-
case 1:
|
|
144
|
-
if (keywordDetectedAfterLength) {
|
|
145
|
-
return [];
|
|
146
|
-
}
|
|
147
|
-
offsetY = arg;
|
|
148
|
-
lengthCount++;
|
|
149
|
-
break;
|
|
150
|
-
case 2:
|
|
151
|
-
if (keywordDetectedAfterLength) {
|
|
152
|
-
return [];
|
|
153
|
-
}
|
|
154
|
-
boxShadow.blurRadius = arg;
|
|
155
|
-
lengthCount++;
|
|
156
|
-
break;
|
|
157
|
-
case 3:
|
|
158
|
-
if (keywordDetectedAfterLength) {
|
|
159
|
-
return [];
|
|
160
|
-
}
|
|
161
|
-
boxShadow.spreadDistance = arg;
|
|
162
|
-
lengthCount++;
|
|
163
|
-
break;
|
|
164
|
-
default:
|
|
165
|
-
return [];
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
if (offsetX == null || offsetY == null) {
|
|
169
|
-
return [];
|
|
170
|
-
}
|
|
171
|
-
boxShadow.offsetX = offsetX;
|
|
172
|
-
boxShadow.offsetY = offsetY;
|
|
173
|
-
result.push(boxShadow);
|
|
174
|
-
}
|
|
175
|
-
return result;
|
|
176
|
-
}
|
|
177
|
-
// RN processBoxShadow.js:199-214. Accepts a unitless 0 or any `<n>px`; rejects a
|
|
178
|
-
// non-zero number with no unit (CSS requires a unit on lengths).
|
|
179
|
-
function parseLength(length) {
|
|
180
|
-
const match = LENGTH_PARSE_REGEX.exec(length);
|
|
181
|
-
if (!match) {
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
const value = parseFloat(match[1]);
|
|
185
|
-
if (match[2] == null && value !== 0) {
|
|
186
|
-
return null;
|
|
187
|
-
}
|
|
188
|
-
return value;
|
|
23
|
+
const parsed = processBoxShadowUpstream(rawBoxShadows);
|
|
24
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
189
25
|
}
|
package/build/process-filter.js
CHANGED
|
@@ -1,305 +1,32 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
|
|
1
|
+
// RN's own processFilter, imported rather than ported.
|
|
2
|
+
//
|
|
3
|
+
// Why it is needed at all: RN registers `filter` with `nativeCSSParsing ? true : {process:
|
|
4
|
+
// processFilter}`, and enableNativeCSSParsing() defaults to false - so the stock path parses the
|
|
5
|
+
// CSS string / structured array in JS and native never sees the raw string.
|
|
6
|
+
//
|
|
7
|
+
// Why the try/catch, unlike processBoxShadow's wrapper: upstream throws exactly once
|
|
8
|
+
// (processFilter.js:120, a TypeError for an input that is neither string nor array), and a commit
|
|
9
|
+
// path must never throw. fabric-props narrows before calling, but `processFilter` is on the public
|
|
10
|
+
// barrel, so the guard is reachable rather than ceremonial.
|
|
11
|
+
//
|
|
12
|
+
// What changed by importing: a drop-shadow's colour now goes through RN's own `processColor`
|
|
13
|
+
// (upstream imports it directly at processFilter.js:16) instead of our injected
|
|
14
|
+
// `setColorProcessor` seam. On a real host those are the same function - bootstrapHost injects
|
|
15
|
+
// exactly `processColor` - so the seam simply no longer governs the colour inside a filter.
|
|
16
|
+
//
|
|
17
|
+
// Platform caveat worth keeping in view when demoing this: on iOS RN paints only `brightness` and
|
|
18
|
+
// `opacity` unless enableSwiftUIBasedFilters is on, so grayscale/blur/saturate/contrast/hueRotate
|
|
19
|
+
// parse correctly here and still do nothing on screen.
|
|
8
20
|
import { dlog } from './debug.js';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const NEWLINE_REGEX = /\n/g;
|
|
12
|
-
const FILTER_FUNCTION_REGEX = /([\w-]+)\(([^()]*|\([^()]*\)|[^()]*\([^()]*\)[^()]*)\)/g;
|
|
13
|
-
const ARGS_WITH_UNITS_REGEX = /([+-]?\d*(\.\d+)?)([a-zA-Z%]+)?/g;
|
|
14
|
-
const WHITESPACE_SPLIT_REGEX = /\s+(?![^(]*\))/;
|
|
15
|
-
const LENGTH_PARSE_REGEX = /([+-]?\d*(\.\d+)?)([\w\W]+)?/g;
|
|
16
|
-
const RADIANS_TO_DEGREES = 180 / Math.PI;
|
|
17
|
-
// A length field may be a CSS string or a number; resolve to a number or null.
|
|
18
|
-
function resolveLength(value) {
|
|
19
|
-
if (typeof value === 'string')
|
|
20
|
-
return parseLength(value);
|
|
21
|
-
if (typeof value === 'number')
|
|
22
|
-
return value;
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
// A drop-shadow color may already be a platform int (number) or undefined; processColor
|
|
26
|
-
// only types CSS strings / opaque PlatformColor objects. Number passes through, anything
|
|
27
|
-
// else (incl. undefined) is null: unprocessable, like RN.
|
|
28
|
-
function processShadowColor(color) {
|
|
29
|
-
if (typeof color === 'number')
|
|
30
|
-
return color;
|
|
31
|
-
if (typeof color === 'string' || isOpaqueColorValue(color))
|
|
32
|
-
return processColor(color);
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
// RN processFilter.js:45-124. Returns [] on any invalid primitive (web semantics: an
|
|
36
|
-
// invalid filter applies none of them, so the missing effect is obvious).
|
|
21
|
+
// @ts-expect-error - untyped Flow source. Metro compiles it; vitest.config.ts strips the types.
|
|
22
|
+
import processFilterUpstream from 'react-native/Libraries/StyleSheet/processFilter';
|
|
37
23
|
export function processFilter(filter) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return
|
|
41
|
-
}
|
|
42
|
-
if (typeof filter === 'string') {
|
|
43
|
-
const text = filter.replace(NEWLINE_REGEX, ' ');
|
|
44
|
-
// Matches functions with args and nested functions like
|
|
45
|
-
// "drop-shadow(10 10 10 rgba(0, 0, 0, 1))".
|
|
46
|
-
FILTER_FUNCTION_REGEX.lastIndex = 0;
|
|
47
|
-
let matches;
|
|
48
|
-
while ((matches = FILTER_FUNCTION_REGEX.exec(text)) !== null) {
|
|
49
|
-
const filterName = matches[1].toLowerCase();
|
|
50
|
-
if (filterName === 'drop-shadow') {
|
|
51
|
-
const dropShadow = parseDropShadow(matches[2]);
|
|
52
|
-
if (dropShadow != null) {
|
|
53
|
-
result.push({ dropShadow });
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
dlog(`processFilter reject: invalid drop-shadow "${matches[2]}"`);
|
|
57
|
-
return [];
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
const camelizedName = filterName === 'hue-rotate' ? 'hueRotate' : filterName;
|
|
62
|
-
const amount = getFilterAmount(camelizedName, matches[2]);
|
|
63
|
-
if (amount != null) {
|
|
64
|
-
result.push(filterEntry(camelizedName, amount));
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
dlog(`processFilter reject: invalid ${camelizedName} "${matches[2]}"`);
|
|
68
|
-
return [];
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
else if (Array.isArray(filter)) {
|
|
74
|
-
for (const filterFunction of filter) {
|
|
75
|
-
const [filterName, filterValue] = Object.entries(filterFunction)[0];
|
|
76
|
-
if (filterName === 'dropShadow') {
|
|
77
|
-
const dropShadow = isDropShadowValue(filterValue)
|
|
78
|
-
? parseDropShadow(filterValue)
|
|
79
|
-
: null;
|
|
80
|
-
if (dropShadow == null) {
|
|
81
|
-
dlog(`processFilter reject: invalid dropShadow in array`);
|
|
82
|
-
return [];
|
|
83
|
-
}
|
|
84
|
-
result.push({ dropShadow });
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
const amount = getFilterAmount(filterName, filterValue);
|
|
88
|
-
if (amount != null) {
|
|
89
|
-
result.push(filterEntry(filterName, amount));
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
dlog(`processFilter reject: invalid ${filterName} in array`);
|
|
93
|
-
return [];
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
throw new TypeError(`${typeof filter} filter is not a string or array`);
|
|
100
|
-
}
|
|
101
|
-
return result;
|
|
102
|
-
}
|
|
103
|
-
// RN sets `filterFunction[camelizedName] = amount` and pushes the loose object. We
|
|
104
|
-
// build the discriminated union explicitly to keep it typed without an `as` cast.
|
|
105
|
-
function filterEntry(name, amount) {
|
|
106
|
-
switch (name) {
|
|
107
|
-
case 'brightness':
|
|
108
|
-
return { brightness: amount };
|
|
109
|
-
case 'blur':
|
|
110
|
-
return { blur: amount };
|
|
111
|
-
case 'contrast':
|
|
112
|
-
return { contrast: amount };
|
|
113
|
-
case 'grayscale':
|
|
114
|
-
return { grayscale: amount };
|
|
115
|
-
case 'hueRotate':
|
|
116
|
-
return { hueRotate: amount };
|
|
117
|
-
case 'invert':
|
|
118
|
-
return { invert: amount };
|
|
119
|
-
case 'opacity':
|
|
120
|
-
return { opacity: amount };
|
|
121
|
-
case 'saturate':
|
|
122
|
-
return { saturate: amount };
|
|
123
|
-
default:
|
|
124
|
-
return { sepia: amount };
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
function isDropShadowValue(value) {
|
|
128
|
-
return typeof value === 'string' || (isRecord(value) && 'offsetX' in value);
|
|
129
|
-
}
|
|
130
|
-
// RN processFilter.js:126-186.
|
|
131
|
-
function getFilterAmount(filterName, filterArgs) {
|
|
132
|
-
let filterArgAsNumber;
|
|
133
|
-
let unit;
|
|
134
|
-
if (typeof filterArgs === 'string') {
|
|
135
|
-
// Matches args with units like "1.5 5% -80deg".
|
|
136
|
-
ARGS_WITH_UNITS_REGEX.lastIndex = 0;
|
|
137
|
-
const match = ARGS_WITH_UNITS_REGEX.exec(filterArgs);
|
|
138
|
-
if (!match || isNaN(Number(match[1]))) {
|
|
139
|
-
return undefined;
|
|
140
|
-
}
|
|
141
|
-
filterArgAsNumber = Number(match[1]);
|
|
142
|
-
unit = match[3];
|
|
143
|
-
}
|
|
144
|
-
else if (typeof filterArgs === 'number') {
|
|
145
|
-
filterArgAsNumber = filterArgs;
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
return undefined;
|
|
149
|
-
}
|
|
150
|
-
switch (filterName) {
|
|
151
|
-
// hueRotate takes an angle that can carry a unit and be negative; bare 0 is allowed.
|
|
152
|
-
case 'hueRotate':
|
|
153
|
-
if (filterArgAsNumber === 0) {
|
|
154
|
-
return 0;
|
|
155
|
-
}
|
|
156
|
-
if (unit !== 'deg' && unit !== 'rad') {
|
|
157
|
-
return undefined;
|
|
158
|
-
}
|
|
159
|
-
return unit === 'rad'
|
|
160
|
-
? RADIANS_TO_DEGREES * filterArgAsNumber
|
|
161
|
-
: filterArgAsNumber;
|
|
162
|
-
// blur takes any non-negative CSS length that is not a percent; RN only has DIPs.
|
|
163
|
-
case 'blur':
|
|
164
|
-
if ((unit != null && unit !== 'px') || filterArgAsNumber < 0) {
|
|
165
|
-
return undefined;
|
|
166
|
-
}
|
|
167
|
-
return filterArgAsNumber;
|
|
168
|
-
// The rest take a non-negative number or percentage (50% == 0.5).
|
|
169
|
-
case 'brightness':
|
|
170
|
-
case 'contrast':
|
|
171
|
-
case 'grayscale':
|
|
172
|
-
case 'invert':
|
|
173
|
-
case 'opacity':
|
|
174
|
-
case 'saturate':
|
|
175
|
-
case 'sepia':
|
|
176
|
-
if ((unit != null && unit !== '%' && unit !== 'px') ||
|
|
177
|
-
filterArgAsNumber < 0) {
|
|
178
|
-
return undefined;
|
|
179
|
-
}
|
|
180
|
-
return unit === '%' ? filterArgAsNumber / 100 : filterArgAsNumber;
|
|
181
|
-
default:
|
|
182
|
-
return undefined;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
// RN processFilter.js:188-256.
|
|
186
|
-
function parseDropShadow(rawDropShadow) {
|
|
187
|
-
const dropShadow = typeof rawDropShadow === 'string'
|
|
188
|
-
? parseDropShadowString(rawDropShadow)
|
|
189
|
-
: rawDropShadow;
|
|
190
|
-
if (dropShadow == null) {
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
const parsedDropShadow = { offsetX: 0, offsetY: 0 };
|
|
194
|
-
let offsetX;
|
|
195
|
-
let offsetY;
|
|
196
|
-
for (const arg of Object.keys(dropShadow)) {
|
|
197
|
-
switch (arg) {
|
|
198
|
-
case 'offsetX': {
|
|
199
|
-
const value = resolveLength(dropShadow.offsetX);
|
|
200
|
-
if (value == null) {
|
|
201
|
-
return null;
|
|
202
|
-
}
|
|
203
|
-
offsetX = value;
|
|
204
|
-
break;
|
|
205
|
-
}
|
|
206
|
-
case 'offsetY': {
|
|
207
|
-
const value = resolveLength(dropShadow.offsetY);
|
|
208
|
-
if (value == null) {
|
|
209
|
-
return null;
|
|
210
|
-
}
|
|
211
|
-
offsetY = value;
|
|
212
|
-
break;
|
|
213
|
-
}
|
|
214
|
-
case 'standardDeviation': {
|
|
215
|
-
const value = resolveLength(dropShadow.standardDeviation);
|
|
216
|
-
if (value == null || value < 0) {
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
parsedDropShadow.standardDeviation = value;
|
|
220
|
-
break;
|
|
221
|
-
}
|
|
222
|
-
case 'color': {
|
|
223
|
-
const color = processShadowColor(dropShadow.color);
|
|
224
|
-
if (color == null) {
|
|
225
|
-
return null;
|
|
226
|
-
}
|
|
227
|
-
parsedDropShadow.color = color;
|
|
228
|
-
break;
|
|
229
|
-
}
|
|
230
|
-
default:
|
|
231
|
-
return null;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
if (offsetX == null || offsetY == null) {
|
|
235
|
-
return null;
|
|
236
|
-
}
|
|
237
|
-
parsedDropShadow.offsetX = offsetX;
|
|
238
|
-
parsedDropShadow.offsetY = offsetY;
|
|
239
|
-
return parsedDropShadow;
|
|
240
|
-
}
|
|
241
|
-
// RN processFilter.js:258-312.
|
|
242
|
-
function parseDropShadowString(rawDropShadow) {
|
|
243
|
-
const dropShadow = { offsetX: 0, offsetY: 0 };
|
|
244
|
-
let offsetX;
|
|
245
|
-
let offsetY;
|
|
246
|
-
let lengthCount = 0;
|
|
247
|
-
let keywordDetectedAfterLength = false;
|
|
248
|
-
for (const arg of rawDropShadow.split(WHITESPACE_SPLIT_REGEX)) {
|
|
249
|
-
const processedColor = processColor(arg);
|
|
250
|
-
if (processedColor != null) {
|
|
251
|
-
if (dropShadow.color != null) {
|
|
252
|
-
return null;
|
|
253
|
-
}
|
|
254
|
-
if (offsetX != null) {
|
|
255
|
-
keywordDetectedAfterLength = true;
|
|
256
|
-
}
|
|
257
|
-
dropShadow.color = arg;
|
|
258
|
-
continue;
|
|
259
|
-
}
|
|
260
|
-
switch (lengthCount) {
|
|
261
|
-
case 0:
|
|
262
|
-
offsetX = arg;
|
|
263
|
-
lengthCount++;
|
|
264
|
-
break;
|
|
265
|
-
case 1:
|
|
266
|
-
if (keywordDetectedAfterLength) {
|
|
267
|
-
return null;
|
|
268
|
-
}
|
|
269
|
-
offsetY = arg;
|
|
270
|
-
lengthCount++;
|
|
271
|
-
break;
|
|
272
|
-
case 2:
|
|
273
|
-
if (keywordDetectedAfterLength) {
|
|
274
|
-
return null;
|
|
275
|
-
}
|
|
276
|
-
dropShadow.standardDeviation = arg;
|
|
277
|
-
lengthCount++;
|
|
278
|
-
break;
|
|
279
|
-
default:
|
|
280
|
-
return null;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
if (offsetX == null || offsetY == null) {
|
|
284
|
-
return null;
|
|
285
|
-
}
|
|
286
|
-
dropShadow.offsetX = offsetX;
|
|
287
|
-
dropShadow.offsetY = offsetY;
|
|
288
|
-
return dropShadow;
|
|
289
|
-
}
|
|
290
|
-
// RN processFilter.js:314-332. Accepts a unitless 0 or any `<n>px`; rejects a non-zero
|
|
291
|
-
// unitless length or a non-px unit.
|
|
292
|
-
function parseLength(length) {
|
|
293
|
-
LENGTH_PARSE_REGEX.lastIndex = 0;
|
|
294
|
-
const match = LENGTH_PARSE_REGEX.exec(length);
|
|
295
|
-
if (!match || isNaN(Number(match[1]))) {
|
|
296
|
-
return null;
|
|
297
|
-
}
|
|
298
|
-
if (match[3] != null && match[3] !== 'px') {
|
|
299
|
-
return null;
|
|
24
|
+
try {
|
|
25
|
+
const parsed = processFilterUpstream(filter);
|
|
26
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
300
27
|
}
|
|
301
|
-
|
|
302
|
-
|
|
28
|
+
catch (error) {
|
|
29
|
+
dlog(`processFilter: dropped an invalid filter - ${error instanceof Error ? error.message : String(error)}`);
|
|
30
|
+
return [];
|
|
303
31
|
}
|
|
304
|
-
return Number(match[1]);
|
|
305
32
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
type ITransformEntry = Record<string, ITransformValue>;
|
|
2
1
|
type ITransformValue = number | string | Array<number | string> | undefined;
|
|
2
|
+
type ITransformEntry = Record<string, ITransformValue>;
|
|
3
3
|
type IRawTransform = Record<string, unknown>;
|
|
4
4
|
export declare function processTransform(transform: ReadonlyArray<IRawTransform> | string | undefined): ReadonlyArray<IRawTransform>;
|
|
5
5
|
export type { ITransformEntry, IRawTransform };
|