react-native-unistyles 3.2.5 → 3.4.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 +3 -3
- package/android/CMakeLists.txt +2 -1
- package/cxx/converters/BackgroundImageConverter.cpp +55 -0
- package/cxx/converters/BackgroundImageConverter.h +14 -0
- package/cxx/converters/TransformOriginConverter.cpp +33 -0
- package/cxx/converters/TransformOriginConverter.h +13 -0
- package/cxx/core/UnistylesRegistry.cpp +135 -47
- package/cxx/core/UnistylesRegistry.h +23 -3
- package/cxx/hybridObjects/HybridShadowRegistry.cpp +11 -9
- package/cxx/parser/Parser.cpp +47 -2
- package/cxx/shadowTree/NativeProps.h +44 -0
- package/cxx/shadowTree/ShadowLeafUpdate.h +8 -0
- package/cxx/shadowTree/ShadowTrafficController.h +16 -37
- package/cxx/shadowTree/ShadowTreeManager.cpp +8 -148
- package/cxx/shadowTree/ShadowTreeManager.h +1 -5
- package/lib/commonjs/components/native/Animated.js +2 -0
- package/lib/commonjs/components/native/Animated.js.map +1 -1
- package/lib/commonjs/components/native/Pressable.js +2 -0
- package/lib/commonjs/components/native/Pressable.js.map +1 -1
- package/lib/commonjs/components/native/Pressable.native.js +2 -0
- package/lib/commonjs/components/native/Pressable.native.js.map +1 -1
- package/lib/commonjs/index.js +0 -12
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/server/resetServerUnistyles.js +2 -0
- package/lib/commonjs/server/resetServerUnistyles.js.map +1 -1
- package/lib/commonjs/specs/ShadowRegistry/index.js +2 -1
- package/lib/commonjs/specs/ShadowRegistry/index.js.map +1 -1
- package/lib/commonjs/web/convert/object/boxShadow.js +2 -2
- package/lib/commonjs/web/convert/object/boxShadow.js.map +1 -1
- package/lib/commonjs/web/index.js +0 -17
- package/lib/commonjs/web/index.js.map +1 -1
- package/lib/commonjs/web/listener.js +5 -0
- package/lib/commonjs/web/listener.js.map +1 -1
- package/lib/commonjs/web/registry.js +2 -0
- package/lib/commonjs/web/registry.js.map +1 -1
- package/lib/commonjs/web/shadowRegistry.js +4 -0
- package/lib/commonjs/web/shadowRegistry.js.map +1 -1
- package/lib/module/components/native/Animated.js +3 -0
- package/lib/module/components/native/Animated.js.map +1 -1
- package/lib/module/components/native/Pressable.js +2 -0
- package/lib/module/components/native/Pressable.js.map +1 -1
- package/lib/module/components/native/Pressable.native.js +2 -0
- package/lib/module/components/native/Pressable.native.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/server/resetServerUnistyles.js +2 -0
- package/lib/module/server/resetServerUnistyles.js.map +1 -1
- package/lib/module/specs/ShadowRegistry/index.js +2 -1
- package/lib/module/specs/ShadowRegistry/index.js.map +1 -1
- package/lib/module/web/convert/object/boxShadow.js +2 -2
- package/lib/module/web/convert/object/boxShadow.js.map +1 -1
- package/lib/module/web/index.js +0 -1
- package/lib/module/web/index.js.map +1 -1
- package/lib/module/web/listener.js +5 -0
- package/lib/module/web/listener.js.map +1 -1
- package/lib/module/web/registry.js +2 -0
- package/lib/module/web/registry.js.map +1 -1
- package/lib/module/web/shadowRegistry.js +4 -0
- package/lib/module/web/shadowRegistry.js.map +1 -1
- package/lib/typescript/src/components/native/Animated.d.ts +16 -36
- package/lib/typescript/src/components/native/Animated.d.ts.map +1 -1
- package/lib/typescript/src/components/native/Pressable.d.ts +4 -2
- package/lib/typescript/src/components/native/Pressable.d.ts.map +1 -1
- package/lib/typescript/src/components/native/Pressable.native.d.ts +5 -2
- package/lib/typescript/src/components/native/Pressable.native.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/server/resetServerUnistyles.d.ts.map +1 -1
- package/lib/typescript/src/specs/ShadowRegistry/index.d.ts.map +1 -1
- package/lib/typescript/src/types/core.d.ts +7 -2
- package/lib/typescript/src/types/core.d.ts.map +1 -1
- package/lib/typescript/src/web/index.d.ts +0 -1
- package/lib/typescript/src/web/index.d.ts.map +1 -1
- package/lib/typescript/src/web/listener.d.ts +1 -0
- package/lib/typescript/src/web/listener.d.ts.map +1 -1
- package/lib/typescript/src/web/registry.d.ts.map +1 -1
- package/lib/typescript/src/web/shadowRegistry.d.ts +1 -0
- package/lib/typescript/src/web/shadowRegistry.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JColorScheme.hpp +1 -1
- package/nitrogen/generated/android/c++/JDimensions.hpp +1 -1
- package/nitrogen/generated/android/c++/JInsets.hpp +1 -1
- package/nitrogen/generated/android/c++/JOrientation.hpp +1 -1
- package/nitrogen/generated/android/c++/JUnistyleDependency.hpp +1 -1
- package/nitrogen/generated/android/c++/JUnistylesNativeMiniRuntime.hpp +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/unistyles/Func_void_UnistylesNativeMiniRuntime.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/unistyles/Func_void_std__vector_UnistyleDependency__UnistylesNativeMiniRuntime.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/unistyles/HybridNativePlatformSpec.kt +2 -0
- package/package.json +28 -26
- package/src/components/native/Animated.tsx +18 -1
- package/src/components/native/Pressable.native.tsx +10 -2
- package/src/components/native/Pressable.tsx +7 -1
- package/src/index.ts +1 -1
- package/src/server/resetServerUnistyles.ts +2 -0
- package/src/specs/ShadowRegistry/index.ts +2 -1
- package/src/types/core.ts +7 -28
- package/src/web/convert/object/boxShadow.ts +2 -2
- package/src/web/index.ts +2 -3
- package/src/web/listener.ts +6 -0
- package/src/web/registry.ts +2 -0
- package/src/web/shadowRegistry.ts +5 -0
package/README.md
CHANGED
|
@@ -58,9 +58,6 @@ Then follow [installation guides](https://unistyl.es/v3/start/getting-started) f
|
|
|
58
58
|
<a href="https://codemask.com">
|
|
59
59
|
<img src="https://avatars.githubusercontent.com/u/51229884?s=200&v=4" height="70px" width="70px" alt="codemask" />
|
|
60
60
|
</a>
|
|
61
|
-
<a href="https://galaxies.dev">
|
|
62
|
-
<img src="https://avatars.githubusercontent.com/u/118431096?s=200&v=4" height="70px" width="70px" alt="galaxies-dev" />
|
|
63
|
-
</a>
|
|
64
61
|
<a href="https://github.com/ryanlanciaux">
|
|
65
62
|
<img src="https://avatars.githubusercontent.com/u/85041?v=4" height="70px" width="70px" alt="ryanlanciaux" />
|
|
66
63
|
</a>
|
|
@@ -155,6 +152,9 @@ Then follow [installation guides](https://unistyl.es/v3/start/getting-started) f
|
|
|
155
152
|
<a href="https://github.com/AdiRishi">
|
|
156
153
|
<img src="https://avatars.githubusercontent.com/u/8351234?v=4" height="60px" width="60px" alt="AdiRishi" />
|
|
157
154
|
</a>
|
|
155
|
+
<a href="https://galaxies.dev">
|
|
156
|
+
<img src="https://avatars.githubusercontent.com/u/118431096?s=200&v=4" height="60px" width="60px" alt="galaxies-dev" />
|
|
157
|
+
</a>
|
|
158
158
|
|
|
159
159
|
## Sponsor my work
|
|
160
160
|
|
package/android/CMakeLists.txt
CHANGED
|
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.9.0)
|
|
|
2
2
|
|
|
3
3
|
project(unistyles)
|
|
4
4
|
|
|
5
|
-
file(GLOB_RECURSE CORE_SRC RELATIVE ${CMAKE_SOURCE_DIR} "../cxx
|
|
5
|
+
file(GLOB_RECURSE CORE_SRC CONFIGURE_DEPENDS RELATIVE ${CMAKE_SOURCE_DIR} "../cxx/*.cpp")
|
|
6
6
|
file(GLOB_RECURSE PLATFORM_SRC RELATIVE ${CMAKE_SOURCE_DIR} "./src/main/cxx/*.cpp")
|
|
7
7
|
|
|
8
8
|
if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 80)
|
|
@@ -21,6 +21,7 @@ include_directories(
|
|
|
21
21
|
./src/main/cxx
|
|
22
22
|
../cxx
|
|
23
23
|
../cxx/common
|
|
24
|
+
../cxx/converters
|
|
24
25
|
../cxx/core
|
|
25
26
|
../cxx/hybridObjects
|
|
26
27
|
../cxx/parser
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#include "BackgroundImageConverter.h"
|
|
2
|
+
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
|
|
5
|
+
#if defined(RN_SERIALIZABLE_STATE) && \
|
|
6
|
+
__has_include( \
|
|
7
|
+
<react/renderer/components/view/BackgroundImagePropsConversions.h>)
|
|
8
|
+
#include <react/renderer/components/view/BackgroundImagePropsConversions.h>
|
|
9
|
+
#include <react/renderer/core/propsConversions.h>
|
|
10
|
+
#define UNISTYLES_HAS_RN_BACKGROUND_IMAGE_PARSER 1
|
|
11
|
+
#endif
|
|
12
|
+
|
|
13
|
+
namespace margelo::nitro::unistyles::converters {
|
|
14
|
+
|
|
15
|
+
bool isBackgroundImagePropName(const std::string &propertyName) {
|
|
16
|
+
return propertyName == "backgroundImage" ||
|
|
17
|
+
propertyName == "experimental_backgroundImage";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
bool hasSafeBackgroundImageColorStops(const folly::dynamic &backgroundImages) {
|
|
21
|
+
if (!backgroundImages.isArray()) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return std::all_of(
|
|
26
|
+
backgroundImages.begin(), backgroundImages.end(), [](const auto &image) {
|
|
27
|
+
if (!image.isObject() || image.count("colorStops") == 0) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const auto &colorStops = image["colorStops"];
|
|
32
|
+
|
|
33
|
+
return colorStops.isArray() && colorStops.size() >= 2;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
std::optional<folly::dynamic>
|
|
38
|
+
parseBackgroundImageString(const std::string &backgroundImageString) {
|
|
39
|
+
#ifdef UNISTYLES_HAS_RN_BACKGROUND_IMAGE_PARSER
|
|
40
|
+
std::vector<facebook::react::BackgroundImage> backgroundImages;
|
|
41
|
+
|
|
42
|
+
facebook::react::parseUnprocessedBackgroundImageString(backgroundImageString,
|
|
43
|
+
backgroundImages);
|
|
44
|
+
|
|
45
|
+
auto parsedBackgroundImages = facebook::react::toDynamic(backgroundImages);
|
|
46
|
+
|
|
47
|
+
return hasSafeBackgroundImageColorStops(parsedBackgroundImages)
|
|
48
|
+
? std::move(parsedBackgroundImages)
|
|
49
|
+
: folly::dynamic::array();
|
|
50
|
+
#else
|
|
51
|
+
return std::nullopt;
|
|
52
|
+
#endif
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
} // namespace margelo::nitro::unistyles::converters
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <folly/dynamic.h>
|
|
4
|
+
#include <optional>
|
|
5
|
+
#include <string>
|
|
6
|
+
|
|
7
|
+
namespace margelo::nitro::unistyles::converters {
|
|
8
|
+
|
|
9
|
+
bool isBackgroundImagePropName(const std::string &propertyName);
|
|
10
|
+
bool hasSafeBackgroundImageColorStops(const folly::dynamic &backgroundImages);
|
|
11
|
+
std::optional<folly::dynamic>
|
|
12
|
+
parseBackgroundImageString(const std::string &backgroundImageString);
|
|
13
|
+
|
|
14
|
+
} // namespace margelo::nitro::unistyles::converters
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#include "TransformOriginConverter.h"
|
|
2
|
+
|
|
3
|
+
#if defined(RN_SERIALIZABLE_STATE) && \
|
|
4
|
+
__has_include(<react/renderer/components/view/conversions.h>)
|
|
5
|
+
#include <react/renderer/components/view/conversions.h>
|
|
6
|
+
#define UNISTYLES_HAS_RN_TRANSFORM_ORIGIN_PARSER 1
|
|
7
|
+
#endif
|
|
8
|
+
|
|
9
|
+
namespace margelo::nitro::unistyles::converters {
|
|
10
|
+
|
|
11
|
+
bool isTransformOriginPropName(const std::string &propertyName) {
|
|
12
|
+
return propertyName == "transformOrigin";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
std::optional<folly::dynamic>
|
|
16
|
+
parseTransformOriginString(const std::string &transformOriginString) {
|
|
17
|
+
#ifdef UNISTYLES_HAS_RN_TRANSFORM_ORIGIN_PARSER
|
|
18
|
+
facebook::react::TransformOrigin transformOrigin;
|
|
19
|
+
|
|
20
|
+
facebook::react::parseUnprocessedTransformOriginString(transformOriginString,
|
|
21
|
+
transformOrigin);
|
|
22
|
+
|
|
23
|
+
if (!transformOrigin.isSet()) {
|
|
24
|
+
return folly::dynamic(nullptr);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return static_cast<folly::dynamic>(transformOrigin);
|
|
28
|
+
#else
|
|
29
|
+
return std::nullopt;
|
|
30
|
+
#endif
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
} // namespace margelo::nitro::unistyles::converters
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <folly/dynamic.h>
|
|
4
|
+
#include <optional>
|
|
5
|
+
#include <string>
|
|
6
|
+
|
|
7
|
+
namespace margelo::nitro::unistyles::converters {
|
|
8
|
+
|
|
9
|
+
bool isTransformOriginPropName(const std::string &propertyName);
|
|
10
|
+
std::optional<folly::dynamic>
|
|
11
|
+
parseTransformOriginString(const std::string &transformOriginString);
|
|
12
|
+
|
|
13
|
+
} // namespace margelo::nitro::unistyles::converters
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#include "UnistylesRegistry.h"
|
|
2
2
|
#include "UnistylesState.h"
|
|
3
3
|
#include "Parser.h"
|
|
4
|
+
#include "NativeProps.h"
|
|
4
5
|
|
|
5
6
|
using namespace margelo::nitro::unistyles;
|
|
6
7
|
using namespace facebook;
|
|
@@ -60,37 +61,85 @@ void core::UnistylesRegistry::updateTheme(jsi::Runtime& rt, std::string& themeNa
|
|
|
60
61
|
|
|
61
62
|
void core::UnistylesRegistry::linkShadowNodeWithUnistyle(
|
|
62
63
|
jsi::Runtime& rt,
|
|
63
|
-
const ShadowNodeFamily
|
|
64
|
+
const std::shared_ptr<const ShadowNodeFamily>& shadowNodeFamily,
|
|
64
65
|
std::vector<std::shared_ptr<UnistyleData>>& unistylesData,
|
|
65
66
|
std::optional<folly::dynamic> initialScopedUpdate
|
|
66
67
|
) {
|
|
67
|
-
|
|
68
|
+
// released after unlocking, so a family destructor never runs within the lock
|
|
69
|
+
ReleasedFamilies releasedFamilies;
|
|
70
|
+
|
|
71
|
+
this->trafficController.withLock([this, &unistylesData, &shadowNodeFamily, &initialScopedUpdate, &releasedFamilies](){
|
|
72
|
+
auto family = shadowNodeFamily.get();
|
|
73
|
+
auto it = this->_shadowRegistry.find(family);
|
|
74
|
+
|
|
68
75
|
// Clear suspension state if this family was previously suspended
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
mutableFamily->nativeProps_DEPRECATED.reset();
|
|
76
|
+
if (it != this->_shadowRegistry.end() && it->second.isSuspended) {
|
|
77
|
+
shadow::resetNativeProps(family);
|
|
72
78
|
// Clear old registry entries to prevent stale UnistyleData accumulation
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
this->trafficController.removeShadowNode(shadowNodeFamily);
|
|
79
|
+
// and any stale traffic controller entry (e.g. from a theme change during suspension)
|
|
80
|
+
this->forgetFamilyUnsafe(family, releasedFamilies);
|
|
76
81
|
}
|
|
77
82
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
auto& linkedFamily = this->_shadowRegistry[family];
|
|
84
|
+
|
|
85
|
+
linkedFamily.family = shadowNodeFamily;
|
|
86
|
+
linkedFamily.unistyles.insert(linkedFamily.unistyles.end(), unistylesData.begin(), unistylesData.end());
|
|
81
87
|
|
|
82
88
|
// Required for scoped themes to apply on initial mount
|
|
83
89
|
if (initialScopedUpdate.has_value()) {
|
|
84
|
-
|
|
90
|
+
this->trafficController.setUpdate(shadowNodeFamily, std::move(*initialScopedUpdate));
|
|
91
|
+
}
|
|
85
92
|
|
|
86
|
-
|
|
87
|
-
|
|
93
|
+
// families unmounted without unlink (eg. while frozen) are pinned by us only
|
|
94
|
+
// sweep them once the registry doubles, so it's amortized O(1) per link
|
|
95
|
+
if (this->_shadowRegistry.size() >= this->_sweepThreshold) {
|
|
96
|
+
this->sweepUnownedFamiliesUnsafe(releasedFamilies);
|
|
97
|
+
this->_sweepThreshold = std::max(MIN_SWEEP_THRESHOLD, this->_shadowRegistry.size() * 2);
|
|
88
98
|
}
|
|
89
99
|
});
|
|
90
100
|
}
|
|
91
101
|
|
|
102
|
+
bool core::UnistylesRegistry::isOwnedOnlyByUnistyles(const LinkedFamily& linkedFamily) noexcept {
|
|
103
|
+
// shadow nodes own their family, so if nothing but our pin owns it, the family is not part of any
|
|
104
|
+
// shadow tree anymore. React Native can't get it back, as it only keeps weak references beside shadow nodes
|
|
105
|
+
return linkedFamily.family.use_count() == 1;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
void core::UnistylesRegistry::forgetFamilyUnsafe(const ShadowNodeFamily* shadowNodeFamily, ReleasedFamilies& releasedFamilies) {
|
|
109
|
+
auto it = this->_shadowRegistry.find(shadowNodeFamily);
|
|
110
|
+
|
|
111
|
+
if (it != this->_shadowRegistry.end()) {
|
|
112
|
+
releasedFamilies.emplace_back(std::move(it->second.family));
|
|
113
|
+
this->_shadowRegistry.erase(it);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (auto pendingFamily = this->trafficController.removeShadowNode(shadowNodeFamily)) {
|
|
117
|
+
releasedFamilies.emplace_back(std::move(pendingFamily));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
void core::UnistylesRegistry::sweepUnownedFamiliesUnsafe(ReleasedFamilies& releasedFamilies) {
|
|
122
|
+
std::vector<const ShadowNodeFamily*> unownedFamilies;
|
|
123
|
+
|
|
124
|
+
for (const auto& [family, linkedFamily] : this->_shadowRegistry) {
|
|
125
|
+
if (isOwnedOnlyByUnistyles(linkedFamily)) {
|
|
126
|
+
unownedFamilies.emplace_back(family);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
for (const auto* family : unownedFamilies) {
|
|
131
|
+
this->forgetFamilyUnsafe(family, releasedFamilies);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
92
135
|
void core::UnistylesRegistry::removeDuplicatedUnistyles(const ShadowNodeFamily *shadowNodeFamily, std::vector<core::Unistyle::Shared>& unistyles) {
|
|
93
|
-
auto
|
|
136
|
+
auto it = this->_shadowRegistry.find(shadowNodeFamily);
|
|
137
|
+
|
|
138
|
+
if (it == this->_shadowRegistry.end()) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
auto& targetFamilyUnistyles = it->second.unistyles;
|
|
94
143
|
|
|
95
144
|
unistyles.erase(
|
|
96
145
|
std::remove_if(
|
|
@@ -111,23 +160,27 @@ void core::UnistylesRegistry::removeDuplicatedUnistyles(const ShadowNodeFamily *
|
|
|
111
160
|
}
|
|
112
161
|
|
|
113
162
|
void core::UnistylesRegistry::unlinkShadowNodeWithUnistyles(const ShadowNodeFamily* shadowNodeFamily) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
this->
|
|
163
|
+
ReleasedFamilies releasedFamilies;
|
|
164
|
+
|
|
165
|
+
this->trafficController.withLock([this, shadowNodeFamily, &releasedFamilies](){
|
|
166
|
+
this->forgetFamilyUnsafe(shadowNodeFamily, releasedFamilies);
|
|
118
167
|
});
|
|
119
168
|
}
|
|
120
169
|
|
|
121
170
|
void core::UnistylesRegistry::suspendShadowNode(const ShadowNodeFamily* shadowNodeFamily) {
|
|
122
171
|
this->trafficController.withLock([this, shadowNodeFamily](){
|
|
123
|
-
|
|
124
|
-
|
|
172
|
+
auto it = this->_shadowRegistry.find(shadowNodeFamily);
|
|
173
|
+
|
|
174
|
+
if (it != this->_shadowRegistry.end()) {
|
|
175
|
+
it->second.isSuspended = true;
|
|
125
176
|
}
|
|
126
177
|
});
|
|
127
178
|
}
|
|
128
179
|
|
|
129
180
|
bool core::UnistylesRegistry::isSuspended(const ShadowNodeFamily* family) const noexcept {
|
|
130
|
-
|
|
181
|
+
auto it = this->_shadowRegistry.find(family);
|
|
182
|
+
|
|
183
|
+
return it != this->_shadowRegistry.end() && it->second.isSuspended;
|
|
131
184
|
}
|
|
132
185
|
|
|
133
186
|
std::shared_ptr<core::StyleSheet> core::UnistylesRegistry::addStyleSheet(jsi::Runtime& rt, core::StyleSheetType type, jsi::Object&& rawValue) {
|
|
@@ -143,36 +196,56 @@ core::DependencyMap core::UnistylesRegistry::buildDependencyMap(std::vector<Unis
|
|
|
143
196
|
core::DependencyMap dependencyMap;
|
|
144
197
|
|
|
145
198
|
std::unordered_set<UnistyleDependency> uniqueDependencies(deps.begin(), deps.end());
|
|
199
|
+
ReleasedFamilies releasedFamilies;
|
|
200
|
+
|
|
201
|
+
// families stay pinned by the registry until the shadow tree update is committed
|
|
202
|
+
this->trafficController.withLock([this, &dependencyMap, &uniqueDependencies, &releasedFamilies](){
|
|
203
|
+
// we iterate whole registry anyway, so it's a free moment to drop families unmounted without unlink
|
|
204
|
+
this->sweepUnownedFamiliesUnsafe(releasedFamilies);
|
|
205
|
+
|
|
206
|
+
for (const auto& [family, linkedFamily] : this->_shadowRegistry) {
|
|
207
|
+
bool hasAnyOfDependencies = false;
|
|
208
|
+
|
|
209
|
+
// Check if any dependency matches
|
|
210
|
+
for (const auto& unistyleData : linkedFamily.unistyles) {
|
|
211
|
+
for (const auto& dep : unistyleData->unistyle->dependencies) {
|
|
212
|
+
if (uniqueDependencies.count(dep)) {
|
|
213
|
+
hasAnyOfDependencies = true;
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
146
217
|
|
|
147
|
-
|
|
148
|
-
bool hasAnyOfDependencies = false;
|
|
149
|
-
|
|
150
|
-
// Check if any dependency matches
|
|
151
|
-
for (const auto& unistyleData : unistyles) {
|
|
152
|
-
for (const auto& dep : unistyleData->unistyle->dependencies) {
|
|
153
|
-
if (uniqueDependencies.count(dep)) {
|
|
154
|
-
hasAnyOfDependencies = true;
|
|
218
|
+
if (hasAnyOfDependencies) {
|
|
155
219
|
break;
|
|
156
|
-
}
|
|
220
|
+
};
|
|
157
221
|
}
|
|
158
222
|
|
|
159
|
-
if (hasAnyOfDependencies) {
|
|
160
|
-
|
|
161
|
-
}
|
|
223
|
+
if (!hasAnyOfDependencies) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
dependencyMap[family].insert(
|
|
228
|
+
dependencyMap[family].end(),
|
|
229
|
+
linkedFamily.unistyles.begin(),
|
|
230
|
+
linkedFamily.unistyles.end()
|
|
231
|
+
);
|
|
162
232
|
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
return dependencyMap;
|
|
236
|
+
}
|
|
163
237
|
|
|
164
|
-
|
|
238
|
+
void core::UnistylesRegistry::queueShadowLeafUpdatesUnsafe(shadow::ShadowLeafUpdates& updates) {
|
|
239
|
+
for (auto& [family, props] : updates) {
|
|
240
|
+
auto it = this->_shadowRegistry.find(family);
|
|
241
|
+
|
|
242
|
+
// family was unlinked in the meantime, there is nothing to update
|
|
243
|
+
if (it == this->_shadowRegistry.end()) {
|
|
165
244
|
continue;
|
|
166
245
|
}
|
|
167
246
|
|
|
168
|
-
|
|
169
|
-
dependencyMap[family].end(),
|
|
170
|
-
unistyles.begin(),
|
|
171
|
-
unistyles.end()
|
|
172
|
-
);
|
|
247
|
+
this->trafficController.setUpdate(it->second.family, std::move(props));
|
|
173
248
|
}
|
|
174
|
-
|
|
175
|
-
return dependencyMap;
|
|
176
249
|
}
|
|
177
250
|
|
|
178
251
|
// called from proxied function only, we don't know host
|
|
@@ -185,15 +258,15 @@ void core::UnistylesRegistry::shadowLeafUpdateFromUnistyle(jsi::Runtime& rt, Uni
|
|
|
185
258
|
? std::make_optional(maybePressableId.asString(rt).utf8(rt))
|
|
186
259
|
: std::nullopt;
|
|
187
260
|
|
|
188
|
-
for (const auto& [family,
|
|
189
|
-
for (const auto& unistyleData : unistyles) {
|
|
261
|
+
for (const auto& [family, linkedFamily] : this->_shadowRegistry) {
|
|
262
|
+
for (const auto& unistyleData : linkedFamily.unistyles) {
|
|
190
263
|
if (unistyleData->unistyle == unistyle) {
|
|
191
264
|
updates[family] = parser.parseStylesToShadowTreeStyles(rt, { unistyleData });
|
|
192
265
|
}
|
|
193
266
|
}
|
|
194
267
|
}
|
|
195
268
|
|
|
196
|
-
this->
|
|
269
|
+
this->queueShadowLeafUpdatesUnsafe(updates);
|
|
197
270
|
});
|
|
198
271
|
}
|
|
199
272
|
|
|
@@ -263,10 +336,25 @@ void core::UnistylesRegistry::setScopedTheme(std::optional<std::string> themeNam
|
|
|
263
336
|
}
|
|
264
337
|
|
|
265
338
|
void core::UnistylesRegistry::destroy() {
|
|
339
|
+
// called when module is invalidated, before React Native destroys the runtime
|
|
340
|
+
// pins must be released now, as families hold JS handles that can't outlive the runtime
|
|
341
|
+
ReleasedFamilies releasedFamilies;
|
|
342
|
+
|
|
343
|
+
this->trafficController.withLock([this, &releasedFamilies](){
|
|
344
|
+
for (auto& [_, linkedFamily] : this->_shadowRegistry) {
|
|
345
|
+
releasedFamilies.emplace_back(std::move(linkedFamily.family));
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
for (auto& [_, pendingUpdate] : this->trafficController.takeUpdates()) {
|
|
349
|
+
releasedFamilies.emplace_back(std::move(pendingUpdate.family));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
this->_shadowRegistry.clear();
|
|
353
|
+
this->_sweepThreshold = MIN_SWEEP_THRESHOLD;
|
|
354
|
+
});
|
|
355
|
+
|
|
266
356
|
this->_state.reset();
|
|
267
357
|
this->_styleSheetRegistry.clear();
|
|
268
|
-
this->_shadowRegistry.clear();
|
|
269
|
-
this->_suspendedFamilies.clear();
|
|
270
358
|
this->_scopedTheme = std::nullopt;
|
|
271
359
|
_nextStyleSheetTag.store(0);
|
|
272
360
|
}
|
|
@@ -23,6 +23,12 @@ using namespace facebook::react;
|
|
|
23
23
|
|
|
24
24
|
using DependencyMap = std::unordered_map<const ShadowNodeFamily*, std::vector<std::shared_ptr<UnistyleData>>>;
|
|
25
25
|
|
|
26
|
+
struct LinkedFamily {
|
|
27
|
+
std::shared_ptr<const ShadowNodeFamily> family;
|
|
28
|
+
std::vector<std::shared_ptr<UnistyleData>> unistyles;
|
|
29
|
+
bool isSuspended = false;
|
|
30
|
+
};
|
|
31
|
+
|
|
26
32
|
struct UnistylesRegistry: public StyleSheetRegistry {
|
|
27
33
|
static UnistylesRegistry& get();
|
|
28
34
|
|
|
@@ -40,13 +46,15 @@ struct UnistylesRegistry: public StyleSheetRegistry {
|
|
|
40
46
|
UnistylesState& getState();
|
|
41
47
|
void createState();
|
|
42
48
|
std::vector<std::shared_ptr<core::StyleSheet>> getStyleSheetsToRefresh(std::vector<UnistyleDependency>& unistylesDependencies);
|
|
43
|
-
void linkShadowNodeWithUnistyle(jsi::Runtime& rt, const ShadowNodeFamily
|
|
49
|
+
void linkShadowNodeWithUnistyle(jsi::Runtime& rt, const std::shared_ptr<const ShadowNodeFamily>& shadowNodeFamily, std::vector<std::shared_ptr<UnistyleData>>& unistylesData, std::optional<folly::dynamic> initialScopedUpdate = std::nullopt);
|
|
44
50
|
void unlinkShadowNodeWithUnistyles(const ShadowNodeFamily*);
|
|
45
51
|
void suspendShadowNode(const ShadowNodeFamily*);
|
|
46
52
|
bool isSuspended(const ShadowNodeFamily*) const noexcept;
|
|
47
53
|
std::shared_ptr<core::StyleSheet> addStyleSheet(jsi::Runtime& rt, core::StyleSheetType type, jsi::Object&& rawValue);
|
|
48
54
|
DependencyMap buildDependencyMap(std::vector<UnistyleDependency>& deps);
|
|
49
55
|
void shadowLeafUpdateFromUnistyle(jsi::Runtime& rt, Unistyle::Shared unistyle, jsi::Value& maybePressableId);
|
|
56
|
+
// call it only within trafficController.withLock!
|
|
57
|
+
void queueShadowLeafUpdatesUnsafe(shadow::ShadowLeafUpdates& updates);
|
|
50
58
|
shadow::ShadowTrafficController trafficController{};
|
|
51
59
|
const std::optional<std::string> getScopedTheme();
|
|
52
60
|
void removeDuplicatedUnistyles(const ShadowNodeFamily* shadowNodeFamily, std::vector<core::Unistyle::Shared>& unistyles);
|
|
@@ -55,14 +63,26 @@ struct UnistylesRegistry: public StyleSheetRegistry {
|
|
|
55
63
|
void destroy();
|
|
56
64
|
|
|
57
65
|
private:
|
|
66
|
+
using ReleasedFamilies = std::vector<std::shared_ptr<const ShadowNodeFamily>>;
|
|
67
|
+
|
|
58
68
|
UnistylesRegistry() = default;
|
|
59
69
|
|
|
70
|
+
// call it only within trafficController.withLock!
|
|
71
|
+
// pins are moved to releasedFamilies, so they can be released after unlocking
|
|
72
|
+
void forgetFamilyUnsafe(const ShadowNodeFamily* shadowNodeFamily, ReleasedFamilies& releasedFamilies);
|
|
73
|
+
void sweepUnownedFamiliesUnsafe(ReleasedFamilies& releasedFamilies);
|
|
74
|
+
|
|
75
|
+
// React Native doesn't own the family anymore, it was unmounted without unlink
|
|
76
|
+
static bool isOwnedOnlyByUnistyles(const LinkedFamily& linkedFamily) noexcept;
|
|
77
|
+
|
|
78
|
+
static constexpr size_t MIN_SWEEP_THRESHOLD = 64;
|
|
79
|
+
|
|
60
80
|
static std::atomic<int> _nextStyleSheetTag;
|
|
61
81
|
std::optional<std::string> _scopedTheme{};
|
|
62
82
|
std::unique_ptr<UnistylesState> _state{};
|
|
63
83
|
std::unordered_map<int, std::shared_ptr<core::StyleSheet>> _styleSheetRegistry{};
|
|
64
|
-
std::unordered_map<const ShadowNodeFamily*,
|
|
65
|
-
|
|
84
|
+
std::unordered_map<const ShadowNodeFamily*, LinkedFamily> _shadowRegistry{};
|
|
85
|
+
size_t _sweepThreshold = MIN_SWEEP_THRESHOLD;
|
|
66
86
|
};
|
|
67
87
|
|
|
68
88
|
inline UnistylesRegistry& UnistylesRegistry::get() {
|
|
@@ -11,10 +11,11 @@ jsi::Value HybridShadowRegistry::link(jsi::Runtime &rt, const jsi::Value &thisVa
|
|
|
11
11
|
std::vector<core::Unistyle::Shared> unistyleWrappers = core::unistyleFromValue(rt, args[1]);
|
|
12
12
|
std::vector<std::vector<folly::dynamic>> arguments;
|
|
13
13
|
auto& registry = core::UnistylesRegistry::get();
|
|
14
|
+
const bool wasSuspended = registry.isSuspended(&shadowNodeWrapper->getFamily());
|
|
14
15
|
|
|
15
16
|
// this is special case for Animated, and prevents appending same unistyles to node
|
|
16
17
|
// skip for suspended families - they need a full re-link with fresh UnistyleData
|
|
17
|
-
if (!
|
|
18
|
+
if (!wasSuspended) {
|
|
18
19
|
registry.removeDuplicatedUnistyles(&shadowNodeWrapper->getFamily(), unistyleWrappers);
|
|
19
20
|
|
|
20
21
|
if (unistyleWrappers.empty()) {
|
|
@@ -86,9 +87,10 @@ jsi::Value HybridShadowRegistry::link(jsi::Runtime &rt, const jsi::Value &thisVa
|
|
|
86
87
|
parser.rebuildUnistyleWithScopedTheme(rt, parsedStyleSheet, unistyleData);
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
//
|
|
90
|
+
// The shared cache can belong to another view by the time React attaches refs.
|
|
91
|
+
// Restore this view's arguments and variants before saving its native styles.
|
|
90
92
|
if (!unistyleData->parsedStyle.has_value() && unistyle->parsedStyle.has_value()) {
|
|
91
|
-
|
|
93
|
+
parser.rebuildUnistyleWithVariants(rt, unistyleData);
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
unistylesData.emplace_back(unistyleData);
|
|
@@ -96,17 +98,21 @@ jsi::Value HybridShadowRegistry::link(jsi::Runtime &rt, const jsi::Value &thisVa
|
|
|
96
98
|
|
|
97
99
|
std::optional<folly::dynamic> initialScopedUpdate;
|
|
98
100
|
|
|
99
|
-
if (scopedTheme.has_value()) {
|
|
101
|
+
if (scopedTheme.has_value() || wasSuspended) {
|
|
100
102
|
initialScopedUpdate = parser.parseStylesToShadowTreeStyles(rt, unistylesData);
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
registry.linkShadowNodeWithUnistyle(
|
|
104
106
|
rt,
|
|
105
|
-
|
|
107
|
+
shadowNodeWrapper->getFamilyShared(),
|
|
106
108
|
unistylesData,
|
|
107
109
|
std::move(initialScopedUpdate)
|
|
108
110
|
);
|
|
109
111
|
|
|
112
|
+
if (wasSuspended) {
|
|
113
|
+
shadow::ShadowTreeManager::updateShadowTree(rt);
|
|
114
|
+
}
|
|
115
|
+
|
|
110
116
|
return jsi::Value::undefined();
|
|
111
117
|
}
|
|
112
118
|
|
|
@@ -165,9 +171,5 @@ jsi::Value HybridShadowRegistry::getScopedTheme(jsi::Runtime &rt, const jsi::Val
|
|
|
165
171
|
}
|
|
166
172
|
|
|
167
173
|
std::shared_ptr<const core::ShadowNode> HybridShadowRegistry::getShadowNodeFromRef(jsi::Runtime& rt, const jsi::Value& maybeRef) {
|
|
168
|
-
#if REACT_NATIVE_VERSION_MINOR >= 81
|
|
169
174
|
return Bridging<std::shared_ptr<const ShadowNode>>::fromJs(rt, maybeRef);
|
|
170
|
-
#else
|
|
171
|
-
return shadowNodeFromValue(rt, maybeRef);
|
|
172
|
-
#endif
|
|
173
175
|
}
|
package/cxx/parser/Parser.cpp
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#include "Parser.h"
|
|
2
2
|
#include "UnistyleWrapper.h"
|
|
3
|
+
#include "BackgroundImageConverter.h"
|
|
4
|
+
#include "TransformOriginConverter.h"
|
|
3
5
|
#include <iomanip>
|
|
4
6
|
#include <sstream>
|
|
5
7
|
#include <react/renderer/css/CSSFilter.h>
|
|
@@ -442,8 +444,7 @@ void parser::Parser::rebuildShadowLeafUpdates(jsi::Runtime& rt, core::Dependency
|
|
|
442
444
|
updates.emplace(shadowNode, std::move(rawProps));
|
|
443
445
|
}
|
|
444
446
|
|
|
445
|
-
registry.
|
|
446
|
-
registry.trafficController.resumeUnistylesTraffic();
|
|
447
|
+
registry.queueShadowLeafUpdatesUnsafe(updates);
|
|
447
448
|
});
|
|
448
449
|
}
|
|
449
450
|
|
|
@@ -1072,6 +1073,50 @@ folly::dynamic parser::Parser::parseStylesToShadowTreeStyles(jsi::Runtime& rt, c
|
|
|
1072
1073
|
rt,
|
|
1073
1074
|
unistyleData->parsedStyle.value(),
|
|
1074
1075
|
[this, &rt, &state, &convertedStyles](const std::string& propertyName, jsi::Value& propertyValue) {
|
|
1076
|
+
if (converters::isBackgroundImagePropName(propertyName) && propertyValue.isString()) {
|
|
1077
|
+
auto maybeBackgroundImage = converters::parseBackgroundImageString(propertyValue.asString(rt).utf8(rt));
|
|
1078
|
+
|
|
1079
|
+
if (maybeBackgroundImage.has_value()) {
|
|
1080
|
+
convertedStyles.setProperty(
|
|
1081
|
+
rt,
|
|
1082
|
+
propertyName.c_str(),
|
|
1083
|
+
jsi::valueFromDynamic(rt, maybeBackgroundImage.value())
|
|
1084
|
+
);
|
|
1085
|
+
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
convertedStyles.setProperty(
|
|
1090
|
+
rt,
|
|
1091
|
+
propertyName.c_str(),
|
|
1092
|
+
propertyValue
|
|
1093
|
+
);
|
|
1094
|
+
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
if (converters::isTransformOriginPropName(propertyName) && propertyValue.isString()) {
|
|
1099
|
+
auto maybeTransformOrigin = converters::parseTransformOriginString(propertyValue.asString(rt).utf8(rt));
|
|
1100
|
+
|
|
1101
|
+
if (maybeTransformOrigin.has_value()) {
|
|
1102
|
+
convertedStyles.setProperty(
|
|
1103
|
+
rt,
|
|
1104
|
+
propertyName.c_str(),
|
|
1105
|
+
jsi::valueFromDynamic(rt, maybeTransformOrigin.value())
|
|
1106
|
+
);
|
|
1107
|
+
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
convertedStyles.setProperty(
|
|
1112
|
+
rt,
|
|
1113
|
+
propertyName.c_str(),
|
|
1114
|
+
propertyValue
|
|
1115
|
+
);
|
|
1116
|
+
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1075
1120
|
if (this->isColor(propertyName)) {
|
|
1076
1121
|
if (propertyValue.isString()) {
|
|
1077
1122
|
convertedStyles.setProperty(
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <folly/dynamic.h>
|
|
4
|
+
#include <react/renderer/core/ShadowNodeFamily.h>
|
|
5
|
+
#include <cxxreact/ReactNativeVersion.h>
|
|
6
|
+
#include <memory>
|
|
7
|
+
#include <mutex>
|
|
8
|
+
|
|
9
|
+
// React Native reads ShadowNodeFamily::nativeProps_DEPRECATED from any thread
|
|
10
|
+
// (eg. ShadowNode::clone during main thread layout) and since 0.86.1 guards every access with nativePropsMutex
|
|
11
|
+
// we need to hold the same lock, otherwise mutating / replacing the folly::dynamic races with RN copying it
|
|
12
|
+
#if REACT_NATIVE_VERSION_MINOR > 86 || (REACT_NATIVE_VERSION_MINOR == 86 && REACT_NATIVE_VERSION_PATCH >= 1)
|
|
13
|
+
#define UNISTYLES_HAS_NATIVE_PROPS_MUTEX 1
|
|
14
|
+
#else
|
|
15
|
+
#define UNISTYLES_HAS_NATIVE_PROPS_MUTEX 0
|
|
16
|
+
#endif
|
|
17
|
+
|
|
18
|
+
namespace margelo::nitro::unistyles::shadow {
|
|
19
|
+
|
|
20
|
+
using namespace facebook::react;
|
|
21
|
+
|
|
22
|
+
inline void mergeNativeProps(const ShadowNodeFamily* family, const folly::dynamic& props) {
|
|
23
|
+
#if UNISTYLES_HAS_NATIVE_PROPS_MUTEX
|
|
24
|
+
std::lock_guard<std::mutex> lock(family->nativePropsMutex);
|
|
25
|
+
#endif
|
|
26
|
+
|
|
27
|
+
if (family->nativeProps_DEPRECATED && family->nativeProps_DEPRECATED->isObject()) {
|
|
28
|
+
family->nativeProps_DEPRECATED->update(props);
|
|
29
|
+
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
family->nativeProps_DEPRECATED = std::make_unique<folly::dynamic>(props);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
inline void resetNativeProps(const ShadowNodeFamily* family) {
|
|
37
|
+
#if UNISTYLES_HAS_NATIVE_PROPS_MUTEX
|
|
38
|
+
std::lock_guard<std::mutex> lock(family->nativePropsMutex);
|
|
39
|
+
#endif
|
|
40
|
+
|
|
41
|
+
family->nativeProps_DEPRECATED.reset();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
}
|