react-native-unistyles 2.8.0-beta.1 → 2.8.0-beta.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. package/android/CMakeLists.txt +8 -1
  2. package/android/build.gradle +4 -1
  3. package/android/src/main/cxx/cpp-adapter.cpp +10 -100
  4. package/android/src/main/cxx/helpers.h +2 -0
  5. package/android/src/main/cxx/platform.cpp +126 -0
  6. package/android/src/main/cxx/platform.h +15 -0
  7. package/android/src/main/java/com/unistyles/Models.kt +14 -35
  8. package/android/src/main/java/com/unistyles/Platform.kt +91 -10
  9. package/android/src/main/java/com/unistyles/UnistylesModule.kt +84 -139
  10. package/cxx/Macros.h +11 -0
  11. package/cxx/UnistylesImpl.cpp +241 -0
  12. package/cxx/UnistylesModel.cpp +234 -0
  13. package/cxx/UnistylesModel.h +112 -0
  14. package/cxx/UnistylesRuntime.cpp +17 -388
  15. package/cxx/UnistylesRuntime.h +56 -95
  16. package/ios/UnistylesModule.h +8 -0
  17. package/ios/UnistylesModule.mm +12 -89
  18. package/ios/platform/Platform_Shared.h +5 -0
  19. package/ios/platform/Platform_Shared.mm +69 -0
  20. package/ios/platform/Platform_iOS.h +2 -9
  21. package/ios/platform/Platform_iOS.mm +47 -94
  22. package/ios/platform/Platform_macOS.h +1 -6
  23. package/ios/platform/Platform_macOS.mm +29 -29
  24. package/ios/platform/Platform_tvOS.h +2 -9
  25. package/ios/platform/Platform_tvOS.mm +30 -92
  26. package/ios/platform/Platform_visionOS.h +2 -8
  27. package/ios/platform/Platform_visionOS.mm +28 -83
  28. package/package.json +1 -1
  29. package/react-native-unistyles.podspec +3 -0
  30. package/android/src/main/java/com/unistyles/Config.kt +0 -116
  31. package/android/src/main/java/com/unistyles/Insets.kt +0 -141
  32. package/ios/UnistylesHelpers.h +0 -3
  33. package/ios/UnistylesHelpers.mm +0 -5
@@ -4,44 +4,45 @@ import android.content.BroadcastReceiver
4
4
  import android.content.Context
5
5
  import android.content.Intent
6
6
  import android.content.IntentFilter
7
+ import android.content.res.Configuration
7
8
  import android.graphics.Color
8
9
  import android.os.Handler
9
10
  import android.os.Looper
10
11
  import android.util.Log
11
- import android.view.ViewTreeObserver
12
- import com.facebook.react.bridge.Arguments
12
+ import android.view.View
13
+ import androidx.core.view.ViewCompat
14
+ import androidx.core.view.WindowCompat
13
15
  import com.facebook.react.bridge.LifecycleEventListener
14
16
  import com.facebook.react.bridge.ReactApplicationContext
15
17
  import com.facebook.react.bridge.ReactContextBaseJavaModule
16
18
  import com.facebook.react.bridge.ReactMethod
17
- import com.facebook.react.modules.core.DeviceEventManagerModule
19
+ import com.facebook.react.turbomodule.core.interfaces.CallInvokerHolder
18
20
 
19
21
  class UnistylesModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext), LifecycleEventListener {
20
- private val drawHandler = Handler(Looper.getMainLooper())
21
- private val debounceDuration = 250L
22
- private var runnable: Runnable? = null
23
-
24
22
  private var isCxxReady: Boolean = false
25
23
  private lateinit var platform: Platform
26
- private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
27
- if (this.isCxxReady) {
28
- runnable?.let { drawHandler.removeCallbacks(it) }
29
-
30
- runnable = Runnable {
31
- this@UnistylesModule.onLayoutConfigChange()
32
- }.also {
33
- drawHandler.postDelayed(it, debounceDuration)
34
- }
35
- }
36
- }
37
24
 
38
25
  private val configurationChangeReceiver = object : BroadcastReceiver() {
39
26
  override fun onReceive(context: Context, intent: Intent) {
40
- if (intent.action == Intent.ACTION_CONFIGURATION_CHANGED && this@UnistylesModule.isCxxReady) {
27
+ if (!this@UnistylesModule.isCxxReady) {
28
+ return
29
+ }
30
+
31
+ if (intent.action == Intent.ACTION_CONFIGURATION_CHANGED) {
41
32
  Handler(Looper.getMainLooper()).postDelayed({
42
33
  this@UnistylesModule.onConfigChange()
43
34
  }, 10)
44
35
  }
36
+
37
+ val newConfig = context.resources.configuration
38
+
39
+ if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
40
+ this@UnistylesModule.onLayoutConfigChange()
41
+ }
42
+
43
+ if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
44
+ this@UnistylesModule.onLayoutConfigChange()
45
+ }
45
46
  }
46
47
  }
47
48
 
@@ -56,21 +57,8 @@ class UnistylesModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
56
57
  reactApplicationContext.addLifecycleEventListener(this)
57
58
  }
58
59
 
59
- private fun setupLayoutListener() {
60
- val activity = currentActivity ?: return
61
- activity.window.decorView.rootView.viewTreeObserver.addOnGlobalLayoutListener(layoutListener)
62
- }
63
-
64
- private fun stopLayoutListener() {
65
- val activity = currentActivity ?: return
66
- activity.window.decorView.rootView.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener)
67
- }
68
-
69
- @Deprecated("Deprecated in Java")
70
- override fun onCatalystInstanceDestroy() {
71
- this.stopLayoutListener()
60
+ override fun invalidate() {
72
61
  reactApplicationContext.unregisterReceiver(configurationChangeReceiver)
73
- runnable?.let { drawHandler.removeCallbacks(it) }
74
62
  reactApplicationContext.removeLifecycleEventListener(this)
75
63
 
76
64
  if (this.isCxxReady) {
@@ -81,36 +69,27 @@ class UnistylesModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
81
69
  //endregion
82
70
  //region Event handlers
83
71
  private fun onConfigChange() {
84
- if (!platform.hasNewConfig()) {
85
- return
86
- }
87
-
88
- val config = platform.getConfig()
72
+ val colorScheme = this.platform.getColorScheme()
73
+ val contentSizeCategory = this.platform.getContentSizeCategory()
89
74
 
90
75
  reactApplicationContext.runOnJSQueueThread {
91
- if (config.hasNewColorScheme) {
92
- this.nativeOnAppearanceChange(config.colorScheme)
93
- }
94
-
95
- if (config.hasNewContentSizeCategory) {
96
- this.nativeOnContentSizeCategoryChange(config.contentSizeCategory)
97
- }
76
+ this.nativeOnAppearanceChange(colorScheme)
77
+ this.nativeOnContentSizeCategoryChange(contentSizeCategory)
98
78
  }
99
79
  }
100
80
 
101
81
  private fun onLayoutConfigChange() {
102
- if (!platform.hasNewLayoutConfig()) {
103
- return
104
- }
105
-
106
- val config = platform.getLayoutConfig()
82
+ val screen = this.getScreenDimensions()
83
+ val insets = this.getInsets()
84
+ val statusBar = this.getStatusBarDimensions()
85
+ val navigationBar = this.getNavigationBarDimensions()
107
86
 
108
87
  reactApplicationContext.runOnJSQueueThread {
109
88
  this.nativeOnOrientationChange(
110
- config.screen,
111
- config.insets,
112
- config.statusBar,
113
- config.navigationBar
89
+ screen,
90
+ insets,
91
+ statusBar,
92
+ navigationBar
114
93
  )
115
94
  }
116
95
  }
@@ -123,25 +102,17 @@ class UnistylesModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
123
102
  System.loadLibrary("unistyles")
124
103
 
125
104
  this.platform = Platform(reactApplicationContext)
105
+ this.enableEdgeToEdge()
126
106
 
127
- val config = platform.getConfig()
128
- val layoutConfig = platform.getLayoutConfig()
129
-
130
- this.reactApplicationContext.javaScriptContextHolder?.let {
131
- this.nativeInstall(
132
- it.get(),
133
- layoutConfig.screen,
134
- config.colorScheme,
135
- config.contentSizeCategory,
136
- layoutConfig.insets,
137
- layoutConfig.statusBar,
138
- layoutConfig.navigationBar
139
- )
140
- this.isCxxReady = true
107
+ this.reactApplicationContext.javaScriptContextHolder?.let { contextHolder ->
108
+ this.reactApplicationContext.catalystInstance.jsCallInvokerHolder?.let { callInvokerHolder: CallInvokerHolder ->
109
+ this.nativeInstall(contextHolder.get(), callInvokerHolder)
110
+ this.isCxxReady = true
141
111
 
142
- Log.i(NAME, "Installed Unistyles \uD83E\uDD84!")
112
+ Log.i(NAME, "Installed Unistyles \uD83E\uDD84!")
143
113
 
144
- return true
114
+ return true
115
+ }
145
116
  }
146
117
 
147
118
  false
@@ -152,88 +123,36 @@ class UnistylesModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
152
123
  }
153
124
  }
154
125
 
155
- private external fun nativeInstall(
156
- jsi: Long,
157
- screen: Dimensions,
158
- colorScheme: String,
159
- contentSizeCategory: String,
160
- insets: Insets,
161
- statusBar: Dimensions,
162
- navigationBar: Dimensions
163
- )
126
+ private external fun nativeInstall(jsi: Long, callInvoker: CallInvokerHolder)
164
127
  private external fun nativeDestroy()
165
128
  private external fun nativeOnOrientationChange(screen: Dimensions, insets: Insets, statusBar: Dimensions, navigationBar: Dimensions)
166
129
  private external fun nativeOnAppearanceChange(colorScheme: String)
167
130
  private external fun nativeOnContentSizeCategoryChange(contentSizeCategory: String)
168
131
 
169
132
  //endregion
170
- //region Event emitter
171
- private fun onLayoutChange(breakpoint: String, orientation: String, screen: Dimensions, statusBar: Dimensions, insets: Insets, navigationBar: Dimensions) {
172
- val body = Arguments.createMap().apply {
173
- putString("type", "layout")
174
- putMap("payload", Arguments.createMap().apply {
175
- putString("breakpoint", breakpoint)
176
- putString("orientation", orientation)
177
- putMap("screen", Arguments.createMap().apply {
178
- putInt("width", screen.width)
179
- putInt("height", screen.height)
180
- })
181
- putMap("statusBar", Arguments.createMap().apply {
182
- putInt("width", statusBar.width)
183
- putInt("height", statusBar.height)
184
- })
185
- putMap("insets", Arguments.createMap().apply {
186
- putInt("top", insets.top)
187
- putInt("bottom", insets.bottom)
188
- putInt("left", insets.left)
189
- putInt("right", insets.right)
190
- })
191
- putMap("navigationBar", Arguments.createMap().apply {
192
- putInt("width", navigationBar.width)
193
- putInt("height", navigationBar.height)
194
- })
195
- })
196
- }
197
133
 
198
- reactApplicationContext
199
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
200
- .emit("__unistylesOnChange", body)
134
+ private fun getScreenDimensions(): Dimensions {
135
+ return platform.getScreenDimensions()
201
136
  }
202
137
 
203
- private fun onThemeChange(themeName: String) {
204
- val body = Arguments.createMap().apply {
205
- putString("type", "theme")
206
- putMap("payload", Arguments.createMap().apply {
207
- putString("themeName", themeName)
208
- })
209
- }
210
-
211
- reactApplicationContext
212
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
213
- .emit("__unistylesOnChange", body)
138
+ private fun getColorScheme(): String {
139
+ return platform.getColorScheme()
214
140
  }
215
141
 
216
- private fun onPluginChange() {
217
- val body = Arguments.createMap().apply {
218
- putString("type", "plugin")
219
- }
142
+ private fun getStatusBarDimensions(): Dimensions {
143
+ return platform.getStatusBarDimensions()
144
+ }
220
145
 
221
- reactApplicationContext
222
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
223
- .emit("__unistylesOnChange", body)
146
+ private fun getNavigationBarDimensions(): Dimensions {
147
+ return platform.getNavigationBarDimensions()
224
148
  }
225
149
 
226
- private fun onContentSizeCategoryChange(contentSizeCategory: String) {
227
- val body = Arguments.createMap().apply {
228
- putString("type", "dynamicTypeSize")
229
- putMap("payload", Arguments.createMap().apply {
230
- putString("contentSizeCategory", contentSizeCategory)
231
- })
232
- }
150
+ private fun getContentSizeCategory(): String {
151
+ return platform.getContentSizeCategory()
152
+ }
233
153
 
234
- reactApplicationContext
235
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
236
- .emit("__unistylesOnChange", body)
154
+ private fun getInsets(): Insets {
155
+ return platform.getInsets()
237
156
  }
238
157
 
239
158
  private fun onSetNavigationBarColor(color: String) {
@@ -268,6 +187,14 @@ class UnistylesModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
268
187
  }
269
188
  }
270
189
 
190
+ private fun enableEdgeToEdge() {
191
+ this.reactApplicationContext.currentActivity?.let { activity ->
192
+ activity.runOnUiThread {
193
+ WindowCompat.setDecorFitsSystemWindows(activity.window, false)
194
+ }
195
+ }
196
+ }
197
+
271
198
  @ReactMethod
272
199
  fun addListener(eventName: String?) = Unit
273
200
 
@@ -278,11 +205,29 @@ class UnistylesModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
278
205
  this.onConfigChange()
279
206
  }
280
207
 
281
- this.setupLayoutListener()
208
+ this.reactApplicationContext.currentActivity?.let { activity ->
209
+ activity.findViewById<View>(android.R.id.content)?.let { mainView ->
210
+ activity.window?.decorView?.let { decorView ->
211
+ ViewCompat.setOnApplyWindowInsetsListener(mainView) { _, insets ->
212
+ this.platform.setInsetsCompat(insets, decorView)
213
+
214
+ if (this.isCxxReady) {
215
+ this.onLayoutConfigChange()
216
+ }
217
+
218
+ insets
219
+ }
220
+ }
221
+ }
222
+ }
282
223
  }
283
224
 
284
225
  override fun onHostPause() {
285
- this.stopLayoutListener()
226
+ this.reactApplicationContext.currentActivity?.let { activity ->
227
+ activity.window?.decorView?.let { view ->
228
+ ViewCompat.setOnApplyWindowInsetsListener(view, null)
229
+ }
230
+ }
286
231
  }
287
232
 
288
233
  override fun onHostDestroy() {}
package/cxx/Macros.h ADDED
@@ -0,0 +1,11 @@
1
+ #pragma once
2
+
3
+ #define HOST_FN(name, args, fn_body) \
4
+ jsi::Function::createFromHostFunction(rt, \
5
+ jsi::PropNameID::forAscii(rt, name), \
6
+ args, \
7
+ [this, &fnName](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *arguments, size_t count) -> jsi::Value \
8
+ fn_body \
9
+ ); \
10
+
11
+ #define BIND_FN(fn) std::bind(&UnistylesRuntime::fn, this, std::placeholders::_1, std::placeholders::_2)
@@ -0,0 +1,241 @@
1
+ #include "UnistylesRuntime.h"
2
+ #include <jsi/jsi.h>
3
+
4
+ using namespace facebook;
5
+
6
+ jsi::Value UnistylesRuntime::getScreenWidth(jsi::Runtime& rt, std::string fnName) {
7
+ return jsi::Value(this->screen.width);
8
+ }
9
+
10
+ jsi::Value UnistylesRuntime::getScreenHeight(jsi::Runtime& rt, std::string fnName) {
11
+ return jsi::Value(this->screen.height);
12
+ }
13
+
14
+ jsi::Value UnistylesRuntime::getContentSizeCategory(jsi::Runtime & rt, std::string fnName) {
15
+ return jsi::Value(jsi::String::createFromUtf8(rt, this->contentSizeCategory));
16
+ }
17
+
18
+ jsi::Value UnistylesRuntime::hasEnabledAdaptiveThemes(jsi::Runtime& rt, std::string fnName) {
19
+ return jsi::Value(this->hasAdaptiveThemes);
20
+ }
21
+
22
+ jsi::Value UnistylesRuntime::getThemeName(jsi::Runtime& rt, std::string fnName) {
23
+ return !this->themeName.empty()
24
+ ? jsi::Value(jsi::String::createFromUtf8(rt, this->themeName))
25
+ : this->getThemeOrFail(rt);
26
+ }
27
+
28
+ jsi::Value UnistylesRuntime::getCurrentBreakpoint(jsi::Runtime& rt, std::string fnName) {
29
+ return !this->breakpoint.empty()
30
+ ? jsi::Value(jsi::String::createFromUtf8(rt, this->breakpoint))
31
+ : jsi::Value::undefined();
32
+ }
33
+
34
+ jsi::Value UnistylesRuntime::getColorScheme(jsi::Runtime& rt, std::string fnName) {
35
+ return jsi::Value(jsi::String::createFromUtf8(rt, this->colorScheme));
36
+ }
37
+
38
+ jsi::Value UnistylesRuntime::getSortedBreakpointPairs(jsi::Runtime& rt, std::string fnName) {
39
+ std::unique_ptr<jsi::Array> sortedBreakpointEntriesArray = std::make_unique<jsi::Array>(rt, this->sortedBreakpointPairs.size());
40
+
41
+ for (size_t i = 0; i < this->sortedBreakpointPairs.size(); ++i) {
42
+ std::unique_ptr<jsi::Array> pairArray = std::make_unique<jsi::Array>(rt, 2);
43
+ jsi::String nameValue = jsi::String::createFromUtf8(rt, this->sortedBreakpointPairs[i].first);
44
+
45
+ pairArray->setValueAtIndex(rt, 0, nameValue);
46
+ pairArray->setValueAtIndex(rt, 1, jsi::Value(this->sortedBreakpointPairs[i].second));
47
+ sortedBreakpointEntriesArray->setValueAtIndex(rt, i, *pairArray);
48
+ }
49
+
50
+ return jsi::Value(rt, *sortedBreakpointEntriesArray);
51
+ }
52
+
53
+ jsi::Value UnistylesRuntime::setBreakpoints(jsi::Runtime& rt, std::string fnName) {
54
+ return HOST_FN(fnName, 1, {
55
+ jsi::Object breakpointsObj = arguments[0].asObject(rt);
56
+ auto sortedBreakpoints = this->toSortedBreakpointPairs(rt, breakpointsObj);
57
+
58
+ if (sortedBreakpoints.size() == 0) {
59
+ throw jsi::JSError(rt, UnistylesErrorBreakpointsCannotBeEmpty);
60
+ }
61
+
62
+ if (sortedBreakpoints.at(0).second != 0) {
63
+ throw jsi::JSError(rt, UnistylesErrorBreakpointsMustStartFromZero);
64
+ }
65
+
66
+ this->sortedBreakpointPairs = sortedBreakpoints;
67
+
68
+ std::string breakpoint = this->getBreakpointFromScreenWidth(this->screen.width, sortedBreakpoints);
69
+
70
+ this->breakpoint = breakpoint;
71
+
72
+ return jsi::Value::undefined();
73
+ });
74
+ }
75
+
76
+ jsi::Value UnistylesRuntime::setActiveTheme(jsi::Runtime& rt, std::string fnName) {
77
+ return HOST_FN(fnName, 1, {
78
+ std::string themeName = arguments[0].asString(rt).utf8(rt);
79
+
80
+ if (this->themeName != themeName) {
81
+ this->themeName = themeName;
82
+ this->onThemeChange(themeName);
83
+ }
84
+
85
+ return jsi::Value::undefined();
86
+ });
87
+ }
88
+
89
+ jsi::Value UnistylesRuntime::updateTheme(jsi::Runtime& rt, std::string fnName) {
90
+ return HOST_FN(fnName, 1, {
91
+ std::string themeName = arguments[0].asString(rt).utf8(rt);
92
+
93
+ if (this->themeName == themeName) {
94
+ this->onThemeChange(themeName);
95
+ }
96
+
97
+ return jsi::Value::undefined();
98
+ });
99
+ }
100
+
101
+ jsi::Value UnistylesRuntime::useAdaptiveThemes(jsi::Runtime& rt, std::string fnName) {
102
+ return HOST_FN(fnName, 1, {
103
+ bool enableAdaptiveThemes = arguments[0].asBool();
104
+
105
+ if (enableAdaptiveThemes && this->colorScheme == UnistylesUnspecifiedScheme) {
106
+ throw jsi::JSError(rt, UnistylesErrorAdaptiveThemesNotSupported);
107
+ }
108
+
109
+ this->hasAdaptiveThemes = enableAdaptiveThemes;
110
+
111
+ if (!enableAdaptiveThemes || !this->supportsAutomaticColorScheme) {
112
+ return jsi::Value::undefined();
113
+ }
114
+
115
+ if (this->themeName != this->colorScheme) {
116
+ this->themeName = this->colorScheme;
117
+ this->onThemeChange(this->themeName);
118
+ }
119
+
120
+ return jsi::Value::undefined();
121
+ });
122
+ }
123
+
124
+ jsi::Value UnistylesRuntime::addPlugin(jsi::Runtime& rt, std::string fnName) {
125
+ return HOST_FN(fnName, 1, {
126
+ std::string pluginName = arguments[0].asString(rt).utf8(rt);
127
+ bool notify = arguments[1].asBool();
128
+
129
+ this->pluginNames.push_back(pluginName);
130
+
131
+ // registry enabled plugins won't notify listeners
132
+ if (notify) {
133
+ this->onPluginChange();
134
+ }
135
+
136
+ return jsi::Value::undefined();
137
+ });
138
+ }
139
+
140
+ jsi::Value UnistylesRuntime::removePlugin(jsi::Runtime& rt, std::string fnName) {
141
+ return HOST_FN(fnName, 1, {
142
+ std::string pluginName = arguments[0].asString(rt).utf8(rt);
143
+
144
+ auto it = std::find(this->pluginNames.begin(), this->pluginNames.end(), pluginName);
145
+
146
+ if (it != this->pluginNames.end()) {
147
+ this->pluginNames.erase(it);
148
+ this->onPluginChange();
149
+ }
150
+
151
+ return jsi::Value::undefined();
152
+ });
153
+ }
154
+
155
+ jsi::Value UnistylesRuntime::getEnabledPlugins(jsi::Runtime& rt, std::string fnName) {
156
+ auto jsiArray = facebook::jsi::Array(rt, this->pluginNames.size());
157
+
158
+ for (size_t i = 0; i < this->pluginNames.size(); i++) {
159
+ jsiArray.setValueAtIndex(rt, i, facebook::jsi::String::createFromUtf8(rt, this->pluginNames[i]));
160
+ }
161
+
162
+ return jsiArray;
163
+ }
164
+
165
+ jsi::Value UnistylesRuntime::getInsets(jsi::Runtime& rt, std::string fnName) {
166
+ auto insets = jsi::Object(rt);
167
+
168
+ insets.setProperty(rt, "top", this->insets.top);
169
+ insets.setProperty(rt, "bottom", this->insets.bottom);
170
+ insets.setProperty(rt, "left", this->insets.left);
171
+ insets.setProperty(rt, "right", this->insets.right);
172
+
173
+ return insets;
174
+ }
175
+
176
+ jsi::Value UnistylesRuntime::getStatusBar(jsi::Runtime& rt, std::string fnName) {
177
+ auto statusBar = jsi::Object(rt);
178
+ auto setStatusBarColorFunction = HOST_FN("setColor", 1, {
179
+ std::string color = arguments[0].asString(rt).utf8(rt);
180
+
181
+ if (this->setStatusBarColor.has_value()) {
182
+ this->setStatusBarColor.value()(color);
183
+ }
184
+
185
+ return jsi::Value::undefined();
186
+ });
187
+
188
+ statusBar.setProperty(rt, "width", this->statusBar.width);
189
+ statusBar.setProperty(rt, "height", this->statusBar.height);
190
+ statusBar.setProperty(rt, "setColor", setStatusBarColorFunction);
191
+
192
+ return statusBar;
193
+ }
194
+
195
+ jsi::Value UnistylesRuntime::getNavigationBar(jsi::Runtime& rt, std::string fnName) {
196
+ auto navigationBarValue = jsi::Object(rt);
197
+ auto setNavigationBarColorFunction = HOST_FN("setColor", 1, {
198
+ std::string color = arguments[0].asString(rt).utf8(rt);
199
+
200
+ if (this->setNavigationBarColor.has_value()) {
201
+ this->setNavigationBarColor.value()(color);
202
+ }
203
+
204
+ return jsi::Value::undefined();
205
+ });
206
+
207
+ navigationBarValue.setProperty(rt, "width", this->navigationBar.width);
208
+ navigationBarValue.setProperty(rt, "height", this->navigationBar.height);
209
+ navigationBarValue.setProperty(rt, "setColor", setNavigationBarColorFunction);
210
+
211
+ return navigationBarValue;
212
+ }
213
+
214
+ std::optional<jsi::Value> UnistylesRuntime::setThemes(jsi::Runtime& rt, const jsi::Value& value) {
215
+ jsi::Array themes = value.asObject(rt).asArray(rt);
216
+ std::vector<std::string> themesVector;
217
+ size_t length = themes.size(rt);
218
+
219
+ for (size_t i = 0; i < length; ++i) {
220
+ jsi::Value element = themes.getValueAtIndex(rt, i);
221
+
222
+ if (element.isString()) {
223
+ std::string theme = element.asString(rt).utf8(rt);
224
+ themesVector.push_back(theme);
225
+ }
226
+ }
227
+
228
+ if (themesVector.size() == 0) {
229
+ throw jsi::JSError(rt, UnistylesErrorThemesCannotBeEmpty);
230
+ }
231
+
232
+ this->themes = themesVector;
233
+ this->themeName = "";
234
+
235
+ bool hasLightTheme = std::find(themesVector.begin(), themesVector.end(), "light") != themesVector.end();
236
+ bool hasDarkTheme = std::find(themesVector.begin(), themesVector.end(), "dark") != themesVector.end();
237
+
238
+ this->supportsAutomaticColorScheme = hasLightTheme && hasDarkTheme;
239
+
240
+ return std::nullopt;
241
+ }