react-native-unistyles 3.0.0-alpha.36 → 3.0.0-alpha.37
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/Unistyles.podspec +1 -1
- package/android/CMakeLists.txt +3 -15
- package/android/src/main/cxx/NativeUnistylesModule.cpp +1 -1
- package/android/src/main/java/com/unistyles/Equatable.kt +61 -0
- package/android/src/main/java/com/unistyles/NativePlatform+android.kt +302 -0
- package/android/src/main/java/com/unistyles/NativePlatform+insets.kt +148 -0
- package/android/src/main/java/com/unistyles/NativePlatform+listener.kt +54 -0
- package/android/src/main/java/com/unistyles/UnistylesModule.kt +5 -1
- package/cxx/core/UnistyleData.h +1 -1
- package/cxx/core/UnistyleWrapper.h +1 -2
- package/cxx/core/UnistylesCommitHook.cpp +1 -1
- package/cxx/core/UnistylesMountHook.cpp +1 -1
- package/cxx/core/UnistylesRegistry.cpp +4 -13
- package/cxx/core/UnistylesRegistry.h +1 -2
- package/cxx/core/UnistylesState.cpp +10 -7
- package/cxx/hybridObjects/HybridStyleSheet.cpp +21 -18
- package/cxx/hybridObjects/HybridUnistylesRuntime.cpp +0 -4
- package/cxx/hybridObjects/HybridUnistylesRuntime.h +0 -1
- package/cxx/parser/Parser.cpp +3 -27
- package/cxx/parser/Parser.h +2 -3
- package/cxx/shadowTree/ShadowTrafficController.h +9 -5
- package/cxx/shadowTree/ShadowTreeManager.cpp +10 -5
- package/cxx/shadowTree/ShadowTreeManager.h +1 -1
- package/package.json +3 -3
- package/android/src/main/java/com/unistyles/NativePlatform.kt +0 -184
- package/android/src/main/java/com/unistyles/UnistylesModule+insets.kt +0 -8
    
        package/Unistyles.podspec
    CHANGED
    
    | @@ -19,7 +19,7 @@ Pod::Spec.new do |s| | |
| 19 19 | 
             
              ]
         | 
| 20 20 | 
             
              s.pod_target_xcconfig = {
         | 
| 21 21 | 
             
                "CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
         | 
| 22 | 
            -
                "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES"
         | 
| 22 | 
            +
                "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES FOLLY_MOBILE"
         | 
| 23 23 | 
             
              }
         | 
| 24 24 |  | 
| 25 25 | 
             
              s.public_header_files = [
         | 
    
        package/android/CMakeLists.txt
    CHANGED
    
    | @@ -23,7 +23,7 @@ include_directories( | |
| 23 23 | 
             
                ../cxx/shadowTree
         | 
| 24 24 | 
             
            )
         | 
| 25 25 |  | 
| 26 | 
            -
            string(APPEND CMAKE_CXX_FLAGS "-DFOLLY_NO_CONFIG")
         | 
| 26 | 
            +
            string(APPEND CMAKE_CXX_FLAGS "-DFOLLY_NO_CONFIG=1 -DFOLLY_CFG_NO_COROUTINES=1 -DFOLLY_MOBILE=1")
         | 
| 27 27 |  | 
| 28 28 | 
             
            set_target_properties(unistyles PROPERTIES
         | 
| 29 29 | 
             
                CXX_STANDARD 20
         | 
| @@ -32,17 +32,5 @@ set_target_properties(unistyles PROPERTIES | |
| 32 32 | 
             
                POSITION_INDEPENDENT_CODE ON
         | 
| 33 33 | 
             
            )
         | 
| 34 34 |  | 
| 35 | 
            -
            #  | 
| 36 | 
            -
            #  | 
| 37 | 
            -
            # find_package(fbjni REQUIRED CONFIG)
         | 
| 38 | 
            -
             | 
| 39 | 
            -
            #if (ReactAndroid_VERSION_MINOR GREATER_EQUAL 76)
         | 
| 40 | 
            -
            #     target_link_libraries(unistyles ReactAndroid::reactnative)
         | 
| 41 | 
            -
            #else()
         | 
| 42 | 
            -
            #    target_link_libraries(unistyles
         | 
| 43 | 
            -
            #        ReactAndroid::turbomodulejsijni
         | 
| 44 | 
            -
            #        ReactAndroid::react_nativemodule_core
         | 
| 45 | 
            -
            #         android
         | 
| 46 | 
            -
            #        fbjni::fbjni
         | 
| 47 | 
            -
            #    )
         | 
| 48 | 
            -
            #endif()
         | 
| 35 | 
            +
            # For React Native 0.76 and above, we don't need to link anything
         | 
| 36 | 
            +
            # as NitroModules will automatically add ReactAndroid::reactnative prefab
         | 
| @@ -49,7 +49,7 @@ jni::local_ref<BindingsInstallerHolder::javaobject> UnistylesModule::getBindings | |
| 49 49 | 
             
                    }
         | 
| 50 50 |  | 
| 51 51 | 
             
                    auto runOnJSThread = [&runtimeExecutor](std::function<void(jsi::Runtime&)>&& callback) {
         | 
| 52 | 
            -
                        runtimeExecutor([ | 
| 52 | 
            +
                        runtimeExecutor([callback = std::move(callback)](jsi::Runtime &rt) {
         | 
| 53 53 | 
             
                            callback(rt);
         | 
| 54 54 | 
             
                        });
         | 
| 55 55 | 
             
                    };
         | 
| @@ -0,0 +1,61 @@ | |
| 1 | 
            +
            package com.unistyles
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            import com.margelo.nitro.unistyles.Dimensions
         | 
| 4 | 
            +
            import com.margelo.nitro.unistyles.Insets
         | 
| 5 | 
            +
            import com.margelo.nitro.unistyles.UnistyleDependency
         | 
| 6 | 
            +
            import com.margelo.nitro.unistyles.UnistylesNativeMiniRuntime
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            fun Dimensions.isEqualTo(other: Dimensions): Boolean {
         | 
| 9 | 
            +
                return this.width == other.width && this.height == other.height
         | 
| 10 | 
            +
            }
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            fun Insets.isEqualTo(other: Insets): Boolean {
         | 
| 13 | 
            +
                return this.top == other.top && this.bottom == other.bottom &&
         | 
| 14 | 
            +
                    this.left == other.left && this.right == other.right &&
         | 
| 15 | 
            +
                    this.ime == other.ime
         | 
| 16 | 
            +
            }
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            fun NativePlatformAndroid.diffMiniRuntimes(lhs: UnistylesNativeMiniRuntime, rhs: UnistylesNativeMiniRuntime): Array<UnistyleDependency> {
         | 
| 19 | 
            +
                val dependencies: MutableList<UnistyleDependency> = mutableListOf()
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                if (lhs.colorScheme != rhs.colorScheme) {
         | 
| 22 | 
            +
                    dependencies.add(UnistyleDependency.COLORSCHEME)
         | 
| 23 | 
            +
                }
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                if (!lhs.screen.isEqualTo(rhs.screen)) {
         | 
| 26 | 
            +
                    dependencies.add(UnistyleDependency.DIMENSIONS)
         | 
| 27 | 
            +
                }
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                if (lhs.screen.width != rhs.screen.width) {
         | 
| 30 | 
            +
                    dependencies.add(UnistyleDependency.BREAKPOINTS)
         | 
| 31 | 
            +
                }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                // no need to check isLandscape, as it's always opposite
         | 
| 34 | 
            +
                if (lhs.isPortrait != rhs.isPortrait) {
         | 
| 35 | 
            +
                    dependencies.add(UnistyleDependency.ORIENTATION)
         | 
| 36 | 
            +
                }
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                if (lhs.contentSizeCategory != rhs.contentSizeCategory) {
         | 
| 39 | 
            +
                    dependencies.add(UnistyleDependency.CONTENTSIZECATEGORY)
         | 
| 40 | 
            +
                }
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                if (!lhs.insets.isEqualTo(rhs.insets)) {
         | 
| 43 | 
            +
                    dependencies.add(UnistyleDependency.INSETS)
         | 
| 44 | 
            +
                }
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                if (lhs.fontScale != rhs.fontScale) {
         | 
| 47 | 
            +
                    dependencies.add(UnistyleDependency.FONTSCALE)
         | 
| 48 | 
            +
                }
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                if (!lhs.statusBar.isEqualTo(rhs.statusBar)) {
         | 
| 51 | 
            +
                    dependencies.add(UnistyleDependency.STATUSBAR)
         | 
| 52 | 
            +
                }
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                if (!lhs.navigationBar.isEqualTo(rhs.navigationBar)) {
         | 
| 55 | 
            +
                    dependencies.add(UnistyleDependency.NAVIGATIONBAR)
         | 
| 56 | 
            +
                }
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                // rtl and pixel ratio are not dynamic
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                return dependencies.toTypedArray()
         | 
| 61 | 
            +
            }
         | 
| @@ -0,0 +1,302 @@ | |
| 1 | 
            +
            package com.unistyles
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            import android.content.Context
         | 
| 4 | 
            +
            import android.content.res.Configuration
         | 
| 5 | 
            +
            import android.os.Build
         | 
| 6 | 
            +
            import android.util.DisplayMetrics
         | 
| 7 | 
            +
            import android.view.View
         | 
| 8 | 
            +
            import android.view.WindowManager
         | 
| 9 | 
            +
            import androidx.core.text.TextUtilsCompat
         | 
| 10 | 
            +
            import androidx.core.view.ViewCompat
         | 
| 11 | 
            +
            import androidx.core.view.WindowCompat
         | 
| 12 | 
            +
            import androidx.core.view.WindowInsetsCompat
         | 
| 13 | 
            +
            import androidx.core.view.WindowInsetsControllerCompat
         | 
| 14 | 
            +
            import com.facebook.react.bridge.LifecycleEventListener
         | 
| 15 | 
            +
            import com.facebook.react.bridge.ReactApplicationContext
         | 
| 16 | 
            +
            import com.margelo.nitro.unistyles.ColorScheme
         | 
| 17 | 
            +
            import com.margelo.nitro.unistyles.Dimensions
         | 
| 18 | 
            +
            import com.margelo.nitro.unistyles.HybridNativePlatformSpec
         | 
| 19 | 
            +
            import com.margelo.nitro.unistyles.Insets
         | 
| 20 | 
            +
            import com.margelo.nitro.unistyles.Orientation
         | 
| 21 | 
            +
            import com.margelo.nitro.unistyles.UnistyleDependency
         | 
| 22 | 
            +
            import com.margelo.nitro.unistyles.UnistylesNativeMiniRuntime
         | 
| 23 | 
            +
            import java.util.Locale
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            class NativePlatformAndroid(private val reactContext: ReactApplicationContext): HybridNativePlatformSpec(), LifecycleEventListener {
         | 
| 26 | 
            +
                private val _insets = NativePlatformInsets(reactContext) { this.diffMiniRuntime() }
         | 
| 27 | 
            +
                private var _miniRuntime: UnistylesNativeMiniRuntime = buildMiniRuntime()
         | 
| 28 | 
            +
                private val _listener = NativePlatformListener(reactContext) { this.diffMiniRuntime() }
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                init {
         | 
| 31 | 
            +
                    reactContext.addLifecycleEventListener(this)
         | 
| 32 | 
            +
                }
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                fun invalidate() {
         | 
| 35 | 
            +
                    reactContext.removeLifecycleEventListener(this)
         | 
| 36 | 
            +
                    _listener.invalidate()
         | 
| 37 | 
            +
                }
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                override fun onHostResume() {
         | 
| 40 | 
            +
                    enableEdgeToEdge()
         | 
| 41 | 
            +
                    _insets.startInsetsListener()
         | 
| 42 | 
            +
                }
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                override fun onHostPause() {
         | 
| 45 | 
            +
                    _insets.stopInsetsListener()
         | 
| 46 | 
            +
                }
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                override fun onHostDestroy() {}
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                override val memorySize: Long
         | 
| 51 | 
            +
                    get() = 0
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                override fun getInsets(): Insets {
         | 
| 54 | 
            +
                    return _insets.getInsets()
         | 
| 55 | 
            +
                }
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                override fun getColorScheme(): ColorScheme {
         | 
| 58 | 
            +
                    val uiMode = reactContext.resources.configuration.uiMode
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    val colorScheme = when (uiMode.and(Configuration.UI_MODE_NIGHT_MASK)) {
         | 
| 61 | 
            +
                        Configuration.UI_MODE_NIGHT_YES -> ColorScheme.DARK
         | 
| 62 | 
            +
                        Configuration.UI_MODE_NIGHT_NO -> ColorScheme.LIGHT
         | 
| 63 | 
            +
                        else -> ColorScheme.UNSPECIFIED
         | 
| 64 | 
            +
                    }
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                    return colorScheme
         | 
| 67 | 
            +
                }
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                override fun getFontScale(): Double {
         | 
| 70 | 
            +
                    return reactContext.resources.configuration.fontScale.toDouble()
         | 
| 71 | 
            +
                }
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                override fun getPixelRatio(): Double {
         | 
| 74 | 
            +
                    return reactContext.resources.displayMetrics.density.toDouble()
         | 
| 75 | 
            +
                }
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                override fun getOrientation(): Orientation {
         | 
| 78 | 
            +
                    val orientation = when (reactContext.resources.configuration.orientation) {
         | 
| 79 | 
            +
                        Configuration.ORIENTATION_PORTRAIT -> Orientation.PORTRAIT
         | 
| 80 | 
            +
                        Configuration.ORIENTATION_LANDSCAPE -> Orientation.LANDSCAPE
         | 
| 81 | 
            +
                        else -> Orientation.PORTRAIT
         | 
| 82 | 
            +
                    }
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                    return orientation
         | 
| 85 | 
            +
                }
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                override fun getContentSizeCategory(): String {
         | 
| 88 | 
            +
                    val fontScale = getFontScale()
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                    val contentSizeCategory = when {
         | 
| 91 | 
            +
                        fontScale <= 0.85f -> "Small"
         | 
| 92 | 
            +
                        fontScale <= 1.0f -> "Default"
         | 
| 93 | 
            +
                        fontScale <= 1.15f -> "Large"
         | 
| 94 | 
            +
                        fontScale <= 1.3f -> "ExtraLarge"
         | 
| 95 | 
            +
                        fontScale <= 1.5f -> "Huge"
         | 
| 96 | 
            +
                        fontScale <= 1.8 -> "ExtraHuge"
         | 
| 97 | 
            +
                        else -> "ExtraExtraHuge"
         | 
| 98 | 
            +
                    }
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                    return contentSizeCategory
         | 
| 101 | 
            +
                }
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                override fun getScreenDimensions(): Dimensions {
         | 
| 104 | 
            +
                    // function takes in count edge-to-edge layout
         | 
| 105 | 
            +
                    when {
         | 
| 106 | 
            +
                        Build.VERSION.SDK_INT < Build.VERSION_CODES.R -> {
         | 
| 107 | 
            +
                            val windowManager = reactContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager
         | 
| 108 | 
            +
                            val metrics = DisplayMetrics()
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                            @Suppress("DEPRECATION")
         | 
| 111 | 
            +
                            windowManager.defaultDisplay.getRealMetrics(metrics)
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                            val screenWidth = (metrics.widthPixels / metrics.density).toDouble()
         | 
| 114 | 
            +
                            val screenHeight = (metrics.heightPixels / metrics.density).toDouble()
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                            return Dimensions(screenWidth, screenHeight)
         | 
| 117 | 
            +
                        }
         | 
| 118 | 
            +
                        else -> {
         | 
| 119 | 
            +
                            val displayMetrics = reactContext.resources.displayMetrics
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                            reactContext.currentActivity?.windowManager?.currentWindowMetrics?.bounds?.let {
         | 
| 122 | 
            +
                                val boundsWidth = (it.width() / displayMetrics.density).toDouble()
         | 
| 123 | 
            +
                                val boundsHeight = (it.height() / displayMetrics.density).toDouble()
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                                return Dimensions(boundsWidth, boundsHeight)
         | 
| 126 | 
            +
                            } ?: run {
         | 
| 127 | 
            +
                                val screenWidth = (displayMetrics.widthPixels / displayMetrics.density).toDouble()
         | 
| 128 | 
            +
                                val screenHeight = (displayMetrics.heightPixels / displayMetrics.density).toDouble()
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                                return Dimensions(screenWidth, screenHeight)
         | 
| 131 | 
            +
                            }
         | 
| 132 | 
            +
                        }
         | 
| 133 | 
            +
                    }
         | 
| 134 | 
            +
                }
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                override fun getStatusBarDimensions(): Dimensions {
         | 
| 137 | 
            +
                    val screenWidth = getScreenDimensions().width
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                    return Dimensions(screenWidth, _insets.getInsets().top)
         | 
| 140 | 
            +
                }
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                override fun getNavigationBarDimensions(): Dimensions {
         | 
| 143 | 
            +
                    val screenWidth = getScreenDimensions().width
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                    return Dimensions(screenWidth, _insets.getInsets().bottom)
         | 
| 146 | 
            +
                }
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                override fun getPrefersRtlDirection(): Boolean {
         | 
| 149 | 
            +
                    // forced by React Native
         | 
| 150 | 
            +
                    val sharedPrefs = reactContext.getSharedPreferences(
         | 
| 151 | 
            +
                        "com.facebook.react.modules.i18nmanager.I18nUtil",
         | 
| 152 | 
            +
                        Context.MODE_PRIVATE
         | 
| 153 | 
            +
                    )
         | 
| 154 | 
            +
                    val hasForcedRtl = sharedPrefs.getBoolean("RCTI18nUtil_forceRTL", false)
         | 
| 155 | 
            +
                    // user preferences
         | 
| 156 | 
            +
                    val isRtl = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                    return hasForcedRtl || isRtl
         | 
| 159 | 
            +
                }
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                override fun setRootViewBackgroundColor(color: Double) {
         | 
| 162 | 
            +
                    reactContext.currentActivity?.let { activity ->
         | 
| 163 | 
            +
                        activity.window?.decorView?.let { decorView ->
         | 
| 164 | 
            +
                            activity.runOnUiThread {
         | 
| 165 | 
            +
                                decorView.rootView.setBackgroundColor(color.toInt())
         | 
| 166 | 
            +
                            }
         | 
| 167 | 
            +
                        }
         | 
| 168 | 
            +
                    }
         | 
| 169 | 
            +
                }
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                @Suppress("DEPRECATION")
         | 
| 172 | 
            +
                override fun setNavigationBarBackgroundColor(color: Double) {
         | 
| 173 | 
            +
                    reactContext.currentActivity?.let { activity ->
         | 
| 174 | 
            +
                        activity.runOnUiThread {
         | 
| 175 | 
            +
                            activity.window.navigationBarColor = color.toInt()
         | 
| 176 | 
            +
                        }
         | 
| 177 | 
            +
                    }
         | 
| 178 | 
            +
                }
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                override fun setNavigationBarHidden(isHidden: Boolean) {
         | 
| 181 | 
            +
                    reactContext.currentActivity?.let { activity ->
         | 
| 182 | 
            +
                        WindowInsetsControllerCompat(activity.window, activity.window.decorView).apply {
         | 
| 183 | 
            +
                            activity.window?.decorView?.let { decorView ->
         | 
| 184 | 
            +
                                @Suppress("DEPRECATION")
         | 
| 185 | 
            +
                                activity.runOnUiThread {
         | 
| 186 | 
            +
                                    if (isHidden) {
         | 
| 187 | 
            +
                                        // below Android 11, we need to use window flags to hide the navigation bar
         | 
| 188 | 
            +
                                        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
         | 
| 189 | 
            +
                                            decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
         | 
| 190 | 
            +
                                                or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
         | 
| 191 | 
            +
                                        } else {
         | 
| 192 | 
            +
                                            hide(WindowInsetsCompat.Type.navigationBars())
         | 
| 193 | 
            +
                                            systemBarsBehavior =
         | 
| 194 | 
            +
                                                WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
         | 
| 195 | 
            +
                                        }
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                                        // dispatch new insets to invoke the insets listener
         | 
| 198 | 
            +
                                        val newInsets = WindowInsetsCompat.Builder()
         | 
| 199 | 
            +
                                            .setInsets(WindowInsetsCompat.Type.navigationBars(), androidx.core.graphics.Insets.of(0, 0, 0, 0))
         | 
| 200 | 
            +
                                            .build()
         | 
| 201 | 
            +
             | 
| 202 | 
            +
                                        ViewCompat.dispatchApplyWindowInsets(activity.findViewById(android.R.id.content), newInsets)
         | 
| 203 | 
            +
                                    } else {
         | 
| 204 | 
            +
                                        show(WindowInsetsCompat.Type.navigationBars())
         | 
| 205 | 
            +
                                    }
         | 
| 206 | 
            +
                                }
         | 
| 207 | 
            +
                            }
         | 
| 208 | 
            +
                        }
         | 
| 209 | 
            +
                    }
         | 
| 210 | 
            +
                }
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                override fun setStatusBarHidden(isHidden: Boolean) {
         | 
| 213 | 
            +
                    reactContext.currentActivity?.let { activity ->
         | 
| 214 | 
            +
                        WindowInsetsControllerCompat(activity.window, activity.window.decorView).apply {
         | 
| 215 | 
            +
                            activity.window?.let { window ->
         | 
| 216 | 
            +
                                @Suppress("DEPRECATION")
         | 
| 217 | 
            +
                                activity.runOnUiThread {
         | 
| 218 | 
            +
                                    if (isHidden) {
         | 
| 219 | 
            +
                                        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
         | 
| 220 | 
            +
                                            window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
         | 
| 221 | 
            +
                                            window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
         | 
| 222 | 
            +
                                        } else {
         | 
| 223 | 
            +
                                            hide(WindowInsetsCompat.Type.statusBars())
         | 
| 224 | 
            +
                                        }
         | 
| 225 | 
            +
                                    } else {
         | 
| 226 | 
            +
                                        show(WindowInsetsCompat.Type.statusBars())
         | 
| 227 | 
            +
                                    }
         | 
| 228 | 
            +
                                }
         | 
| 229 | 
            +
                            }
         | 
| 230 | 
            +
                        }
         | 
| 231 | 
            +
                    }
         | 
| 232 | 
            +
                }
         | 
| 233 | 
            +
             | 
| 234 | 
            +
                @Suppress("DEPRECATION")
         | 
| 235 | 
            +
                override fun setStatusBarBackgroundColor(color: Double) {
         | 
| 236 | 
            +
                    reactContext.currentActivity?.let { activity ->
         | 
| 237 | 
            +
                        activity.runOnUiThread {
         | 
| 238 | 
            +
                            activity.window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
         | 
| 239 | 
            +
                            activity.window.statusBarColor = color.toInt()
         | 
| 240 | 
            +
                        }
         | 
| 241 | 
            +
                    }
         | 
| 242 | 
            +
                }
         | 
| 243 | 
            +
             | 
| 244 | 
            +
                override fun setImmersiveMode(isEnabled: Boolean) {
         | 
| 245 | 
            +
                    this.setStatusBarHidden(isEnabled)
         | 
| 246 | 
            +
                    this.setNavigationBarHidden(isEnabled)
         | 
| 247 | 
            +
                }
         | 
| 248 | 
            +
             | 
| 249 | 
            +
                override fun getMiniRuntime(): UnistylesNativeMiniRuntime {
         | 
| 250 | 
            +
                    return _miniRuntime
         | 
| 251 | 
            +
                }
         | 
| 252 | 
            +
             | 
| 253 | 
            +
                private fun buildMiniRuntime(): UnistylesNativeMiniRuntime {
         | 
| 254 | 
            +
                    val orientation = this.getOrientation()
         | 
| 255 | 
            +
             | 
| 256 | 
            +
                    return UnistylesNativeMiniRuntime(
         | 
| 257 | 
            +
                        colorScheme = this.getColorScheme(),
         | 
| 258 | 
            +
                        screen = this.getScreenDimensions(),
         | 
| 259 | 
            +
                        contentSizeCategory = this.getContentSizeCategory(),
         | 
| 260 | 
            +
                        insets = this.getInsets(),
         | 
| 261 | 
            +
                        pixelRatio = this.getPixelRatio(),
         | 
| 262 | 
            +
                        fontScale = this.getFontScale(),
         | 
| 263 | 
            +
                        rtl = this.getPrefersRtlDirection(),
         | 
| 264 | 
            +
                        statusBar = this.getStatusBarDimensions(),
         | 
| 265 | 
            +
                        navigationBar = this.getNavigationBarDimensions(),
         | 
| 266 | 
            +
                        isPortrait = orientation == Orientation.PORTRAIT,
         | 
| 267 | 
            +
                        isLandscape = orientation == Orientation.LANDSCAPE
         | 
| 268 | 
            +
                    )
         | 
| 269 | 
            +
                }
         | 
| 270 | 
            +
             | 
| 271 | 
            +
                private fun diffMiniRuntime(): Array<UnistyleDependency> {
         | 
| 272 | 
            +
                    val newMiniRuntime = this.buildMiniRuntime()
         | 
| 273 | 
            +
                    val changedDependencies = diffMiniRuntimes(this._miniRuntime, newMiniRuntime)
         | 
| 274 | 
            +
             | 
| 275 | 
            +
                    if (changedDependencies.isNotEmpty()) {
         | 
| 276 | 
            +
                        this._miniRuntime = newMiniRuntime
         | 
| 277 | 
            +
                    }
         | 
| 278 | 
            +
             | 
| 279 | 
            +
                    return changedDependencies
         | 
| 280 | 
            +
                }
         | 
| 281 | 
            +
             | 
| 282 | 
            +
                override fun registerPlatformListener(callback: (dependencies: Array<UnistyleDependency>) -> Unit) {
         | 
| 283 | 
            +
                    this._listener.addPlatformListener(callback)
         | 
| 284 | 
            +
                }
         | 
| 285 | 
            +
             | 
| 286 | 
            +
                override fun registerImeListener(callback: () -> Unit) {
         | 
| 287 | 
            +
                    this._insets.addImeListener(callback)
         | 
| 288 | 
            +
                }
         | 
| 289 | 
            +
             | 
| 290 | 
            +
                override fun unregisterPlatformListeners() {
         | 
| 291 | 
            +
                    this._listener.removePlatformListeners()
         | 
| 292 | 
            +
                    this._insets.removeImeListeners()
         | 
| 293 | 
            +
                }
         | 
| 294 | 
            +
             | 
| 295 | 
            +
                private fun enableEdgeToEdge() {
         | 
| 296 | 
            +
                    reactContext.currentActivity?.let { activity ->
         | 
| 297 | 
            +
                        activity.runOnUiThread {
         | 
| 298 | 
            +
                            WindowCompat.setDecorFitsSystemWindows(activity.window, false)
         | 
| 299 | 
            +
                        }
         | 
| 300 | 
            +
                    }
         | 
| 301 | 
            +
                }
         | 
| 302 | 
            +
            }
         | 
| @@ -0,0 +1,148 @@ | |
| 1 | 
            +
            package com.unistyles
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            import android.graphics.Rect
         | 
| 4 | 
            +
            import android.os.Build
         | 
| 5 | 
            +
            import android.view.View
         | 
| 6 | 
            +
            import android.view.Window
         | 
| 7 | 
            +
            import android.view.WindowManager
         | 
| 8 | 
            +
            import androidx.core.view.ViewCompat
         | 
| 9 | 
            +
            import androidx.core.view.WindowInsetsAnimationCompat
         | 
| 10 | 
            +
            import androidx.core.view.WindowInsetsCompat
         | 
| 11 | 
            +
            import com.facebook.react.bridge.ReactApplicationContext
         | 
| 12 | 
            +
            import com.margelo.nitro.unistyles.Insets
         | 
| 13 | 
            +
            import com.margelo.nitro.unistyles.UnistyleDependency
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            typealias CxxImeListener = () -> Unit
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            class NativePlatformInsets(private val reactContext: ReactApplicationContext, private val diffMiniRuntime: () -> Array<UnistyleDependency>) {
         | 
| 18 | 
            +
                private val _imeListeners: MutableList<CxxImeListener> = mutableListOf()
         | 
| 19 | 
            +
                private var _insets: Insets = Insets(0.0, 0.0, 0.0, 0.0, 0.0)
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                fun getInsets(): Insets {
         | 
| 22 | 
            +
                    val density = reactContext.resources.displayMetrics.density
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    return Insets(
         | 
| 25 | 
            +
                        this._insets.top / density,
         | 
| 26 | 
            +
                        this._insets.bottom / density,
         | 
| 27 | 
            +
                        this._insets.left / density,
         | 
| 28 | 
            +
                        this._insets.right / density,
         | 
| 29 | 
            +
                        this._insets.ime / density
         | 
| 30 | 
            +
                    )
         | 
| 31 | 
            +
                }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                fun setInsets(insetsCompat: WindowInsetsCompat, window: Window, animatedBottomInsets: Double?) {
         | 
| 34 | 
            +
                    // below Android 11, we need to use window flags to detect status bar visibility
         | 
| 35 | 
            +
                    val isStatusBarVisible = when(Build.VERSION.SDK_INT) {
         | 
| 36 | 
            +
                        in 30..Int.MAX_VALUE -> {
         | 
| 37 | 
            +
                            insetsCompat.isVisible(WindowInsetsCompat.Type.statusBars())
         | 
| 38 | 
            +
                        }
         | 
| 39 | 
            +
                        else -> {
         | 
| 40 | 
            +
                            @Suppress("DEPRECATION")
         | 
| 41 | 
            +
                            window.attributes.flags and WindowManager.LayoutParams.FLAG_FULLSCREEN != WindowManager.LayoutParams.FLAG_FULLSCREEN
         | 
| 42 | 
            +
                        }
         | 
| 43 | 
            +
                    }
         | 
| 44 | 
            +
                    // React Native is forcing insets to make status bar translucent
         | 
| 45 | 
            +
                    // so we need to calculate top inset manually, as WindowInsetCompat will always return 0
         | 
| 46 | 
            +
                    val statusBarTopInset = when(isStatusBarVisible) {
         | 
| 47 | 
            +
                        true -> {
         | 
| 48 | 
            +
                            val visibleRect = Rect()
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                            window.decorView.getWindowVisibleDisplayFrame(visibleRect)
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                            visibleRect.top
         | 
| 53 | 
            +
                        }
         | 
| 54 | 
            +
                        false -> 0
         | 
| 55 | 
            +
                    }
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    val insets = insetsCompat.getInsets(WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout())
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                    // Android 10 and below - set bottom insets to 0 while keyboard is visible and use default bottom insets otherwise
         | 
| 60 | 
            +
                    // Android 11 and above - animate bottom insets while keyboard is appearing and disappearing
         | 
| 61 | 
            +
                    val imeInsets = if (Build.VERSION.SDK_INT >= 30) {
         | 
| 62 | 
            +
                        animatedBottomInsets ?: this._insets.ime
         | 
| 63 | 
            +
                    } else {
         | 
| 64 | 
            +
                        val nextBottomInset = insetsCompat.getInsets(WindowInsetsCompat.Type.ime()).bottom - insets.bottom
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                        // call new IME event here, as for SDK >= 30 it's called in AnimationCallback
         | 
| 67 | 
            +
                        this@NativePlatformInsets.emitImeEvent()
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                        if (nextBottomInset < 0) {
         | 
| 70 | 
            +
                            0
         | 
| 71 | 
            +
                        } else {
         | 
| 72 | 
            +
                            nextBottomInset
         | 
| 73 | 
            +
                        }
         | 
| 74 | 
            +
                    }
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                    this._insets = Insets(
         | 
| 77 | 
            +
                        statusBarTopInset.toDouble(),
         | 
| 78 | 
            +
                        insets.bottom.toDouble(),
         | 
| 79 | 
            +
                        insets.left.toDouble(),
         | 
| 80 | 
            +
                        insets.right.toDouble(),
         | 
| 81 | 
            +
                        imeInsets.toDouble()
         | 
| 82 | 
            +
                    )
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                    diffMiniRuntime()
         | 
| 85 | 
            +
                }
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                fun startInsetsListener() {
         | 
| 88 | 
            +
                    reactContext.currentActivity?.let { activity ->
         | 
| 89 | 
            +
                        activity.findViewById<View>(android.R.id.content)?.let { mainView ->
         | 
| 90 | 
            +
                            ViewCompat.setOnApplyWindowInsetsListener(mainView) { _, insets ->
         | 
| 91 | 
            +
                                setInsets(insets, activity.window, null)
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                                insets
         | 
| 94 | 
            +
                            }
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                            // IME insets are available from Android 11
         | 
| 97 | 
            +
                            if (Build.VERSION.SDK_INT >= 30) {
         | 
| 98 | 
            +
                                ViewCompat.setWindowInsetsAnimationCallback(
         | 
| 99 | 
            +
                                    mainView,
         | 
| 100 | 
            +
                                    object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) {
         | 
| 101 | 
            +
                                        override fun onProgress(
         | 
| 102 | 
            +
                                            insets: WindowInsetsCompat,
         | 
| 103 | 
            +
                                            runningAnimations: List<WindowInsetsAnimationCompat>
         | 
| 104 | 
            +
                                        ): WindowInsetsCompat {
         | 
| 105 | 
            +
                                            runningAnimations.firstOrNull()?.let {
         | 
| 106 | 
            +
                                                val bottomInset = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom.toDouble() - this@NativePlatformInsets._insets.bottom
         | 
| 107 | 
            +
                                                val nextBottomInset = if (bottomInset < 0) {
         | 
| 108 | 
            +
                                                    0.0
         | 
| 109 | 
            +
                                                } else {
         | 
| 110 | 
            +
                                                    bottomInset
         | 
| 111 | 
            +
                                                }
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                                                this@NativePlatformInsets.setInsets(insets, activity.window, nextBottomInset)
         | 
| 114 | 
            +
                                                this@NativePlatformInsets.emitImeEvent()
         | 
| 115 | 
            +
                                            }
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                                            return insets
         | 
| 118 | 
            +
                                        }
         | 
| 119 | 
            +
                                    }
         | 
| 120 | 
            +
                                )
         | 
| 121 | 
            +
                            }
         | 
| 122 | 
            +
                        }
         | 
| 123 | 
            +
                    }
         | 
| 124 | 
            +
                }
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                fun emitImeEvent() {
         | 
| 127 | 
            +
                    _imeListeners.forEach { listener ->
         | 
| 128 | 
            +
                        listener()
         | 
| 129 | 
            +
                    }
         | 
| 130 | 
            +
                }
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                fun stopInsetsListener() {
         | 
| 133 | 
            +
                    reactContext.currentActivity?.let { activity ->
         | 
| 134 | 
            +
                        activity.window?.decorView?.let { view ->
         | 
| 135 | 
            +
                            ViewCompat.setOnApplyWindowInsetsListener(view, null)
         | 
| 136 | 
            +
                            ViewCompat.setWindowInsetsAnimationCallback(view, null)
         | 
| 137 | 
            +
                        }
         | 
| 138 | 
            +
                    }
         | 
| 139 | 
            +
                }
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                fun addImeListener(listener: CxxImeListener) {
         | 
| 142 | 
            +
                    this._imeListeners.add(listener)
         | 
| 143 | 
            +
                }
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                fun removeImeListeners() {
         | 
| 146 | 
            +
                    this._imeListeners.clear()
         | 
| 147 | 
            +
                }
         | 
| 148 | 
            +
            }
         | 
| @@ -0,0 +1,54 @@ | |
| 1 | 
            +
            package com.unistyles
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            import android.content.BroadcastReceiver
         | 
| 4 | 
            +
            import android.content.Context
         | 
| 5 | 
            +
            import android.content.Intent
         | 
| 6 | 
            +
            import android.content.IntentFilter
         | 
| 7 | 
            +
            import android.os.Handler
         | 
| 8 | 
            +
            import android.os.Looper
         | 
| 9 | 
            +
            import com.facebook.react.bridge.ReactApplicationContext
         | 
| 10 | 
            +
            import com.margelo.nitro.unistyles.UnistyleDependency
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            typealias CxxDependencyListener = (dependencies: Array<UnistyleDependency>) -> Unit
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            class NativePlatformListener(private val reactContext: ReactApplicationContext, private val diffMiniRuntime: () -> Array<UnistyleDependency>) {
         | 
| 15 | 
            +
                private val _dependencyListeners: MutableList<CxxDependencyListener> = mutableListOf()
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                private val configurationChangeReceiver = object : BroadcastReceiver() {
         | 
| 18 | 
            +
                    override fun onReceive(context: Context, intent: Intent) {
         | 
| 19 | 
            +
                        Handler(Looper.getMainLooper()).postDelayed({
         | 
| 20 | 
            +
                            this@NativePlatformListener.onConfigChange()
         | 
| 21 | 
            +
                        }, 10)
         | 
| 22 | 
            +
                    }
         | 
| 23 | 
            +
                }
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                init {
         | 
| 26 | 
            +
                    reactContext.registerReceiver(configurationChangeReceiver, IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED))
         | 
| 27 | 
            +
                }
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                fun invalidate() {
         | 
| 30 | 
            +
                    reactContext.unregisterReceiver(configurationChangeReceiver)
         | 
| 31 | 
            +
                }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                fun addPlatformListener(listener: CxxDependencyListener) {
         | 
| 34 | 
            +
                    this._dependencyListeners.add(listener)
         | 
| 35 | 
            +
                }
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                fun removePlatformListeners() {
         | 
| 38 | 
            +
                    this._dependencyListeners.clear()
         | 
| 39 | 
            +
                }
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                private fun emitCxxEvent(dependencies: Array<UnistyleDependency>) {
         | 
| 42 | 
            +
                    this._dependencyListeners.forEach { listener ->
         | 
| 43 | 
            +
                        listener(dependencies)
         | 
| 44 | 
            +
                    }
         | 
| 45 | 
            +
                }
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                fun onConfigChange() {
         | 
| 48 | 
            +
                    val changedDependencies = diffMiniRuntime()
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    if (changedDependencies.isNotEmpty()) {
         | 
| 51 | 
            +
                        emitCxxEvent(changedDependencies)
         | 
| 52 | 
            +
                    }
         | 
| 53 | 
            +
                }
         | 
| 54 | 
            +
            }
         | 
| @@ -16,7 +16,7 @@ import com.margelo.nitro.unistyles.HybridNativePlatformSpec | |
| 16 16 | 
             
            class UnistylesModule(reactContext: ReactApplicationContext): NativeTurboUnistylesSpec(reactContext), TurboModuleWithJSIBindings {
         | 
| 17 17 | 
             
                @DoNotStrip
         | 
| 18 18 | 
             
                private var mHybridData: HybridData?
         | 
| 19 | 
            -
                private val _nativePlatform =  | 
| 19 | 
            +
                private val _nativePlatform = NativePlatformAndroid(reactContext)
         | 
| 20 20 |  | 
| 21 21 | 
             
                companion object {
         | 
| 22 22 | 
             
                    const val NAME = NativeTurboUnistylesSpec.NAME
         | 
| @@ -26,6 +26,10 @@ class UnistylesModule(reactContext: ReactApplicationContext): NativeTurboUnistyl | |
| 26 26 | 
             
                    mHybridData = initializeHybridData(reactContext)
         | 
| 27 27 | 
             
                }
         | 
| 28 28 |  | 
| 29 | 
            +
                override fun invalidate() {
         | 
| 30 | 
            +
                    _nativePlatform.invalidate()
         | 
| 31 | 
            +
                }
         | 
| 32 | 
            +
             | 
| 29 33 | 
             
                private fun initializeHybridData(reactContext: ReactApplicationContext): HybridData {
         | 
| 30 34 | 
             
                    val runtimeExecutor = reactContext.catalystInstance?.runtimeExecutor
         | 
| 31 35 | 
             
                        ?: throw IllegalStateException("Unistyles: React Native runtime executor is not available. Please follow installation guides.")
         | 
    
        package/cxx/core/UnistyleData.h
    CHANGED
    
    | @@ -12,7 +12,7 @@ struct UnistyleData { | |
| 12 12 | 
             
                    : unistyle{unistyle}, variants(std::move(variants)), dynamicFunctionMetadata{std::move(arguments)} {}
         | 
| 13 13 |  | 
| 14 14 | 
             
                UnistyleData(const UnistyleData&) = delete;
         | 
| 15 | 
            -
                UnistyleData(UnistyleData&& other) | 
| 15 | 
            +
                UnistyleData(UnistyleData&& other) = delete;
         | 
| 16 16 |  | 
| 17 17 | 
             
                core::Unistyle::Shared unistyle;
         | 
| 18 18 | 
             
                core::Variants variants;
         | 
| @@ -32,7 +32,6 @@ inline static Unistyle::Shared unistyleFromStaticStyleSheet(jsi::Runtime& rt, js | |
| 32 32 | 
             
                return exoticUnistyle;
         | 
| 33 33 | 
             
            }
         | 
| 34 34 |  | 
| 35 | 
            -
             | 
| 36 35 | 
             
            inline static std::vector<Unistyle::Shared> unistylesFromNonExistentNativeState(jsi::Runtime& rt, jsi::Object& value) {
         | 
| 37 36 | 
             
                auto hasUnistyleName = value.hasProperty(rt, helpers::NAME_STYLE_KEY.c_str());
         | 
| 38 37 |  | 
| @@ -71,7 +70,7 @@ inline static jsi::Object generateUnistylesPrototype( | |
| 71 70 | 
             
                auto hostFn = jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forUtf8(rt, "getStyle"), 0, [unistyle, unistylesRuntime](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count){
         | 
| 72 71 | 
             
                    auto variants = helpers::variantsToPairs(rt, thisValue.asObject(rt).getProperty(rt, "variants").asObject(rt));
         | 
| 73 72 | 
             
                    auto arguments = helpers::parseDynamicFunctionArguments(rt, thisValue.asObject(rt).getProperty(rt, "arguments").asObject(rt).asArray(rt));
         | 
| 74 | 
            -
             | 
| 73 | 
            +
             | 
| 75 74 | 
             
                    parser::Parser(unistylesRuntime).rebuildUnistyle(rt, unistyle->parent, unistyle, variants, std::make_optional<std::vector<folly::dynamic>>(arguments));
         | 
| 76 75 |  | 
| 77 76 | 
             
                    return jsi::Value(rt, unistyle->parsedStyle.value()).asObject(rt);
         | 
| @@ -31,7 +31,7 @@ RootShadowNode::Unshared core::UnistylesCommitHook::shadowTreeWillCommit( | |
| 31 31 | 
             
                auto& registry = core::UnistylesRegistry::get();
         | 
| 32 32 | 
             
                auto& shadowLeafUpdates = registry.trafficController.getUpdates();
         | 
| 33 33 |  | 
| 34 | 
            -
                // oops,  | 
| 34 | 
            +
                // oops, no updates from Unistyles yet, skip it!
         | 
| 35 35 | 
             
                if (shadowLeafUpdates.size() == 0) {
         | 
| 36 36 | 
             
                    return newRootShadowNode;
         | 
| 37 37 | 
             
                }
         | 
| @@ -23,6 +23,6 @@ void core::UnistylesMountHook::shadowTreeDidMount(RootShadowNode::Shared const & | |
| 23 23 | 
             
                auto& registry = core::UnistylesRegistry::get();
         | 
| 24 24 |  | 
| 25 25 | 
             
                if (!registry.trafficController.shouldStop()) {
         | 
| 26 | 
            -
                    shadow::ShadowTreeManager::updateShadowTree(this-> | 
| 26 | 
            +
                    shadow::ShadowTreeManager::updateShadowTree(this->_uiManager->getShadowTreeRegistry());
         | 
| 27 27 | 
             
                }
         | 
| 28 28 | 
             
            }
         | 
| @@ -78,10 +78,6 @@ void core::UnistylesRegistry::linkShadowNodeWithUnistyle( | |
| 78 78 | 
             
                auto parser = parser::Parser(nullptr);
         | 
| 79 79 | 
             
                shadow::ShadowLeafUpdates updates;
         | 
| 80 80 |  | 
| 81 | 
            -
                if (!this->_shadowRegistry[&rt].contains(shadowNodeFamily)) {
         | 
| 82 | 
            -
                    this->_shadowRegistry[&rt][shadowNodeFamily] = {};
         | 
| 83 | 
            -
                }
         | 
| 84 | 
            -
             | 
| 85 81 | 
             
                std::for_each(unistyles.begin(), unistyles.end(), [&, this](Unistyle::Shared unistyle){
         | 
| 86 82 | 
             
                    this->_shadowRegistry[&rt][shadowNodeFamily].emplace_back(std::make_shared<UnistyleData>(unistyle, variants, arguments));
         | 
| 87 83 |  | 
| @@ -104,16 +100,11 @@ void core::UnistylesRegistry::linkShadowNodeWithUnistyle( | |
| 104 100 |  | 
| 105 101 | 
             
            void core::UnistylesRegistry::unlinkShadowNodeWithUnistyles(jsi::Runtime& rt, const ShadowNodeFamily* shadowNodeFamily) {
         | 
| 106 102 | 
             
                this->_shadowRegistry[&rt].erase(shadowNodeFamily);
         | 
| 107 | 
            -
             | 
| 103 | 
            +
                this->trafficController.removeShadowNode(shadowNodeFamily);
         | 
| 108 104 |  | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 112 | 
            -
                if (targetStyleSheet == nullptr) {
         | 
| 113 | 
            -
                    return nullptr;
         | 
| 105 | 
            +
                if (this->_shadowRegistry[&rt].empty()) {
         | 
| 106 | 
            +
                    this->_shadowRegistry.erase(&rt);
         | 
| 114 107 | 
             
                }
         | 
| 115 | 
            -
             | 
| 116 | 
            -
                return targetStyleSheet.get()->unistyles[styleKey];
         | 
| 117 108 | 
             
            }
         | 
| 118 109 |  | 
| 119 110 | 
             
            std::shared_ptr<core::StyleSheet> core::UnistylesRegistry::addStyleSheet(jsi::Runtime& rt, int unid, core::StyleSheetType type, jsi::Object&& rawValue) {
         | 
| @@ -162,7 +153,7 @@ void core::UnistylesRegistry::shadowLeafUpdateFromUnistyle(jsi::Runtime& rt, Uni | |
| 162 153 | 
             
                for (const auto& [family, unistyles] : this->_shadowRegistry[&rt]) {
         | 
| 163 154 | 
             
                    for (const auto& unistyleData : unistyles) {
         | 
| 164 155 | 
             
                        if (unistyleData->unistyle == unistyle) {
         | 
| 165 | 
            -
                            updates[family] = parser.parseStylesToShadowTreeStyles(rt, {unistyleData});
         | 
| 156 | 
            +
                            updates[family] = parser.parseStylesToShadowTreeStyles(rt, { unistyleData });
         | 
| 166 157 | 
             
                        }
         | 
| 167 158 | 
             
                    }
         | 
| 168 159 | 
             
                }
         | 
| @@ -41,7 +41,6 @@ struct UnistylesRegistry: public StyleSheetRegistry { | |
| 41 41 | 
             
                void unlinkShadowNodeWithUnistyles(jsi::Runtime& rt, const ShadowNodeFamily*);
         | 
| 42 42 | 
             
                std::shared_ptr<core::StyleSheet> addStyleSheet(jsi::Runtime& rt, int tag, core::StyleSheetType type, jsi::Object&& rawValue);
         | 
| 43 43 | 
             
                DependencyMap buildDependencyMap(jsi::Runtime& rt, std::vector<UnistyleDependency>& deps);
         | 
| 44 | 
            -
                Unistyle::Shared findUnistyleFromKey(jsi::Runtime& rt, std::string styleKey, int tag);
         | 
| 45 44 | 
             
                void shadowLeafUpdateFromUnistyle(jsi::Runtime& rt, Unistyle::Shared unistyle);
         | 
| 46 45 | 
             
                shadow::ShadowTrafficController trafficController{};
         | 
| 47 46 |  | 
| @@ -50,7 +49,7 @@ private: | |
| 50 49 |  | 
| 51 50 | 
             
                std::unordered_map<jsi::Runtime*, UnistylesState> _states{};
         | 
| 52 51 | 
             
                std::unordered_map<jsi::Runtime*, std::unordered_map<int, std::shared_ptr<core::StyleSheet>>> _styleSheetRegistry{};
         | 
| 53 | 
            -
                std::unordered_map<jsi::Runtime*, std::unordered_map<const ShadowNodeFamily*, std::vector<std::shared_ptr<UnistyleData>>>> _shadowRegistry{};
         | 
| 52 | 
            +
                std::unordered_map<jsi::Runtime*, std::unordered_map<const ShadowNodeFamily*, std::vector<const std::shared_ptr<UnistyleData>>>> _shadowRegistry{};
         | 
| 54 53 | 
             
            };
         | 
| 55 54 |  | 
| 56 55 | 
             
            inline UnistylesRegistry& UnistylesRegistry::get() {
         | 
| @@ -25,7 +25,7 @@ std::optional<std::string>& core::UnistylesState::getCurrentThemeName() { | |
| 25 25 |  | 
| 26 26 | 
             
            jsi::Object core::UnistylesState::getCurrentJSTheme() {
         | 
| 27 27 | 
             
                auto hasSomeThemes = _registeredThemeNames.size() > 0;
         | 
| 28 | 
            -
             | 
| 28 | 
            +
             | 
| 29 29 | 
             
                if (!hasSomeThemes && !this->hasUserConfig) {
         | 
| 30 30 | 
             
                    helpers::assertThat(*_rt, false, "Unistyles: One of your stylesheets is trying to get the theme, but no theme has been selected yet. Did you forget to call StyleSheet.configure? If you called it, make sure you did so before any StyleSheet.create.");
         | 
| 31 31 | 
             
                }
         | 
| @@ -99,15 +99,18 @@ int core::UnistylesState::parseColor(jsi::Value& maybeColor) { | |
| 99 99 | 
             
                if (!maybeColor.isString()) {
         | 
| 100 100 | 
             
                    return 0;
         | 
| 101 101 | 
             
                }
         | 
| 102 | 
            -
             | 
| 102 | 
            +
             | 
| 103 103 | 
             
                auto colorString = maybeColor.asString(*_rt);
         | 
| 104 | 
            -
             | 
| 104 | 
            +
             | 
| 105 105 | 
             
                if (!this->_colorCache.contains(colorString.utf8(*_rt).c_str())) {
         | 
| 106 | 
            -
                     | 
| 107 | 
            -
             | 
| 108 | 
            -
                    
         | 
| 106 | 
            +
                    #ifdef ANDROID
         | 
| 107 | 
            +
                        int color = this->_processColorFn.get()->call(*_rt, colorString).asNumber();
         | 
| 108 | 
            +
                    #else
         | 
| 109 | 
            +
                        uint32_t color = this->_processColorFn.get()->call(*_rt, colorString).asNumber();
         | 
| 110 | 
            +
                    #endif
         | 
| 111 | 
            +
             | 
| 109 112 | 
             
                    this->_colorCache[colorString.utf8(*_rt).c_str()] = color ? color : 0;
         | 
| 110 113 | 
             
                }
         | 
| 111 | 
            -
             | 
| 114 | 
            +
             | 
| 112 115 | 
             
                return this->_colorCache[colorString.utf8(*_rt).c_str()];
         | 
| 113 116 | 
             
            }
         | 
| @@ -15,7 +15,7 @@ double HybridStyleSheet::getUnid() { | |
| 15 15 |  | 
| 16 16 | 
             
            jsi::Value HybridStyleSheet::create(jsi::Runtime& rt, const jsi::Value &thisVal, const jsi::Value *arguments, size_t count) {
         | 
| 17 17 | 
             
                if (count == 1) {
         | 
| 18 | 
            -
                    helpers::assertThat(rt,  | 
| 18 | 
            +
                    helpers::assertThat(rt, false, "Unistyles is not initialized correctly. Please add babel plugin to your babel config.");
         | 
| 19 19 | 
             
                }
         | 
| 20 20 |  | 
| 21 21 | 
             
                // second argument is hidden, so validation is perfectly fine
         | 
| @@ -128,7 +128,7 @@ void HybridStyleSheet::parseSettings(jsi::Runtime &rt, jsi::Object settings) { | |
| 128 128 | 
             
            void HybridStyleSheet::parseBreakpoints(jsi::Runtime &rt, jsi::Object breakpoints){
         | 
| 129 129 | 
             
                helpers::Breakpoints sortedBreakpoints = helpers::jsiBreakpointsToVecPairs(rt, std::move(breakpoints));
         | 
| 130 130 |  | 
| 131 | 
            -
                helpers::assertThat(rt, sortedBreakpoints. | 
| 131 | 
            +
                helpers::assertThat(rt, !sortedBreakpoints.empty(), "StyleSheet.configure's breakpoints can't be empty.");
         | 
| 132 132 | 
             
                helpers::assertThat(rt, sortedBreakpoints.front().second == 0, "StyleSheet.configure's first breakpoint must start from 0.");
         | 
| 133 133 |  | 
| 134 134 | 
             
                auto& registry = core::UnistylesRegistry::get();
         | 
| @@ -197,7 +197,7 @@ void HybridStyleSheet::verifyAndSelectTheme(jsi::Runtime &rt) { | |
| 197 197 |  | 
| 198 198 | 
             
            void HybridStyleSheet::setThemeFromColorScheme(jsi::Runtime& rt) {
         | 
| 199 199 | 
             
                auto& state = core::UnistylesRegistry::get().getState(rt);
         | 
| 200 | 
            -
                 | 
| 200 | 
            +
                auto colorScheme = static_cast<ColorScheme>(this->_unistylesRuntime->getColorScheme());
         | 
| 201 201 |  | 
| 202 202 | 
             
                switch (colorScheme) {
         | 
| 203 203 | 
             
                    case ColorScheme::LIGHT:
         | 
| @@ -232,7 +232,7 @@ void HybridStyleSheet::loadExternalMethods(const jsi::Value& thisValue, jsi::Run | |
| 232 232 | 
             
            void HybridStyleSheet::registerHooks(jsi::Runtime& rt) {
         | 
| 233 233 | 
             
                // cleanup Shadow updates
         | 
| 234 234 | 
             
                core::UnistylesRegistry::get().trafficController.restore();
         | 
| 235 | 
            -
             | 
| 235 | 
            +
             | 
| 236 236 | 
             
                this->_unistylesCommitHook = std::make_shared<core::UnistylesCommitHook>(this->_uiManager);
         | 
| 237 237 | 
             
                this->_unistylesMountHook = std::make_shared<core::UnistylesMountHook>(this->_uiManager, this->_unistylesRuntime);
         | 
| 238 238 | 
             
            }
         | 
| @@ -248,20 +248,20 @@ void HybridStyleSheet::onPlatformDependenciesChange(std::vector<UnistyleDependen | |
| 248 248 | 
             
                    auto dependencies = std::move(unistylesDependencies);
         | 
| 249 249 |  | 
| 250 250 | 
             
                    // re-compute new breakpoint
         | 
| 251 | 
            -
                    auto dimensionsIt = std::find( | 
| 251 | 
            +
                    auto dimensionsIt = std::find(unistylesDependencies.begin(), unistylesDependencies.end(), UnistyleDependency::DIMENSIONS);
         | 
| 252 252 |  | 
| 253 | 
            -
                    if (dimensionsIt !=  | 
| 253 | 
            +
                    if (dimensionsIt != unistylesDependencies.end()) {
         | 
| 254 254 | 
             
                        registry.getState(rt).computeCurrentBreakpoint(this->_unistylesRuntime->getScreen().width);
         | 
| 255 255 | 
             
                    }
         | 
| 256 256 |  | 
| 257 257 | 
             
                    // check if color scheme changed and then if Unistyles state depend on it (adaptive themes)
         | 
| 258 | 
            -
                    auto colorSchemeIt = std::find( | 
| 259 | 
            -
                    auto hasNewColorScheme = colorSchemeIt !=  | 
| 258 | 
            +
                    auto colorSchemeIt = std::find(unistylesDependencies.begin(), unistylesDependencies.end(), UnistyleDependency::COLORSCHEME);
         | 
| 259 | 
            +
                    auto hasNewColorScheme = colorSchemeIt != unistylesDependencies.end();
         | 
| 260 260 |  | 
| 261 261 | 
             
                    // in a later step, we will rebuild only Unistyles with mounted StyleSheets
         | 
| 262 262 | 
             
                    // however, user may have StyleSheets with components that haven't mounted yet
         | 
| 263 263 | 
             
                    // we need to rebuild all dependent StyleSheets as well
         | 
| 264 | 
            -
                    auto dependentStyleSheets = registry.getStyleSheetsToRefresh(rt, hasNewColorScheme,  | 
| 264 | 
            +
                    auto dependentStyleSheets = registry.getStyleSheetsToRefresh(rt, hasNewColorScheme, unistylesDependencies.size() > 1);
         | 
| 265 265 |  | 
| 266 266 | 
             
                    if (hasNewColorScheme) {
         | 
| 267 267 | 
             
                        this->_unistylesRuntime->includeDependenciesForColorSchemeChange(dependencies);
         | 
| @@ -269,17 +269,17 @@ void HybridStyleSheet::onPlatformDependenciesChange(std::vector<UnistyleDependen | |
| 269 269 |  | 
| 270 270 | 
             
                    auto dependencyMap = registry.buildDependencyMap(rt, dependencies);
         | 
| 271 271 |  | 
| 272 | 
            -
                    if (dependencyMap. | 
| 272 | 
            +
                    if (dependencyMap.empty()) {
         | 
| 273 273 | 
             
                        this->notifyJSListeners(dependencies);
         | 
| 274 274 |  | 
| 275 275 | 
             
                        return;
         | 
| 276 276 | 
             
                    }
         | 
| 277 277 |  | 
| 278 278 | 
             
                    parser.rebuildUnistylesInDependencyMap(rt, dependencyMap, dependentStyleSheets);
         | 
| 279 | 
            -
                    parser.rebuildShadowLeafUpdates(dependencyMap);
         | 
| 280 | 
            -
             | 
| 279 | 
            +
                    parser.rebuildShadowLeafUpdates(rt, dependencyMap);
         | 
| 280 | 
            +
             | 
| 281 281 | 
             
                    this->notifyJSListeners(dependencies);
         | 
| 282 | 
            -
                    shadow::ShadowTreeManager::updateShadowTree(rt);
         | 
| 282 | 
            +
                    shadow::ShadowTreeManager::updateShadowTree(UIManagerBinding::getBinding(rt)->getUIManager().getShadowTreeRegistry());
         | 
| 283 283 | 
             
                });
         | 
| 284 284 | 
             
            }
         | 
| 285 285 |  | 
| @@ -298,18 +298,21 @@ void HybridStyleSheet::onImeChange() { | |
| 298 298 |  | 
| 299 299 | 
             
                    auto dependencyMap = registry.buildDependencyMap(rt, dependencies);
         | 
| 300 300 |  | 
| 301 | 
            -
                    if (dependencyMap. | 
| 301 | 
            +
                    if (dependencyMap.empty()) {
         | 
| 302 302 | 
             
                        return;
         | 
| 303 303 | 
             
                    }
         | 
| 304 | 
            +
                    
         | 
| 305 | 
            +
                    std::vector<std::shared_ptr<core::StyleSheet>> styleSheet;
         | 
| 306 | 
            +
             | 
| 307 | 
            +
                    parser.rebuildUnistylesInDependencyMap(rt, dependencyMap, styleSheet);
         | 
| 308 | 
            +
                    parser.rebuildShadowLeafUpdates(rt, dependencyMap);
         | 
| 304 309 |  | 
| 305 | 
            -
                     | 
| 306 | 
            -
                    parser.rebuildShadowLeafUpdates(dependencyMap);
         | 
| 307 | 
            -
                    shadow::ShadowTreeManager::updateShadowTree(rt);
         | 
| 310 | 
            +
                    shadow::ShadowTreeManager::updateShadowTree(UIManagerBinding::getBinding(rt)->getUIManager().getShadowTreeRegistry());
         | 
| 308 311 | 
             
                });
         | 
| 309 312 | 
             
            }
         | 
| 310 313 |  | 
| 311 314 | 
             
            void HybridStyleSheet::notifyJSListeners(std::vector<UnistyleDependency>& dependencies) {
         | 
| 312 | 
            -
                if (dependencies. | 
| 315 | 
            +
                if (!dependencies.empty()) {
         | 
| 313 316 | 
             
                    std::for_each(this->_changeListeners.begin(), this->_changeListeners.end(), [&](auto& listener){
         | 
| 314 317 | 
             
                        (*listener)(dependencies);
         | 
| 315 318 | 
             
                    });
         | 
| @@ -228,10 +228,6 @@ jsi::Value HybridUnistylesRuntime::getMiniRuntimeAsValue(jsi::Runtime& rt) { | |
| 228 228 | 
             
                return obj;
         | 
| 229 229 | 
             
            }
         | 
| 230 230 |  | 
| 231 | 
            -
            jsi::Runtime& HybridUnistylesRuntime::getRuntime() {
         | 
| 232 | 
            -
                return *this->_rt;
         | 
| 233 | 
            -
            }
         | 
| 234 | 
            -
             | 
| 235 231 | 
             
            void HybridUnistylesRuntime::registerPlatformListener(const std::function<void(std::vector<UnistyleDependency>)>& listener) {
         | 
| 236 232 | 
             
                this->_nativePlatform.registerPlatformListener(listener);
         | 
| 237 233 | 
             
                this->_onDependenciesChange = listener;
         | 
| @@ -67,7 +67,6 @@ struct HybridUnistylesRuntime: public HybridUnistylesRuntimeSpec { | |
| 67 67 | 
             
                std::unordered_map<std::string, double> getBreakpoints() override;
         | 
| 68 68 |  | 
| 69 69 | 
             
                jsi::Value getMiniRuntimeAsValue(jsi::Runtime& rt);
         | 
| 70 | 
            -
                jsi::Runtime& getRuntime();
         | 
| 71 70 | 
             
                void includeDependenciesForColorSchemeChange(std::vector<UnistyleDependency>& deps);
         | 
| 72 71 | 
             
                void calculateNewThemeAndDependencies(std::vector<UnistyleDependency>& deps);
         | 
| 73 72 | 
             
                std::function<void(std::function<void(jsi::Runtime&)>&&)> runOnJSThread;
         | 
    
        package/cxx/parser/Parser.cpp
    CHANGED
    
    | @@ -93,13 +93,12 @@ void parser::Parser::rebuildUnistylesWithVariants(jsi::Runtime& rt, std::shared_ | |
| 93 93 | 
             
                        continue;
         | 
| 94 94 | 
             
                    }
         | 
| 95 95 |  | 
| 96 | 
            -
                    // todo skip dynamic functions
         | 
| 97 96 | 
             
                    this->rebuildUnistyle(rt, styleSheet, unistyle, variants, std::nullopt);
         | 
| 98 97 | 
             
                }
         | 
| 99 98 | 
             
            }
         | 
| 100 99 |  | 
| 101 100 | 
             
            // rebuild all unistyles that are affected by platform event
         | 
| 102 | 
            -
            void parser::Parser::rebuildUnistylesInDependencyMap(jsi::Runtime& rt, DependencyMap& dependencyMap, std::vector<std::shared_ptr<core::StyleSheet | 
| 101 | 
            +
            void parser::Parser::rebuildUnistylesInDependencyMap(jsi::Runtime& rt, DependencyMap& dependencyMap, std::vector<std::shared_ptr<core::StyleSheet>>& styleSheets) {
         | 
| 103 102 | 
             
                std::unordered_map<std::shared_ptr<StyleSheet>, jsi::Value> parsedStyleSheets{};
         | 
| 104 103 | 
             
                std::unordered_map<std::shared_ptr<core::Unistyle>, bool> parsedUnistyles{};
         | 
| 105 104 |  | 
| @@ -121,7 +120,7 @@ void parser::Parser::rebuildUnistylesInDependencyMap(jsi::Runtime& rt, Dependenc | |
| 121 120 | 
             
                        auto& unistyle = unistyleData->unistyle;
         | 
| 122 121 |  | 
| 123 122 | 
             
                        // for RN styles or inline styles, compute styles only once
         | 
| 124 | 
            -
                        if (unistyle->styleKey == helpers::EXOTIC_STYLE_KEY | 
| 123 | 
            +
                        if (unistyle->styleKey == helpers::EXOTIC_STYLE_KEY) {
         | 
| 125 124 | 
             
                            if (!unistyleData->parsedStyle.has_value()) {
         | 
| 126 125 | 
             
                                unistyleData->parsedStyle = jsi::Value(rt, unistyle->rawValue).asObject(rt);
         | 
| 127 126 |  | 
| @@ -206,10 +205,9 @@ void parser::Parser::rebuildUnistyle(jsi::Runtime& rt, std::shared_ptr<StyleShee | |
| 206 205 | 
             
            }
         | 
| 207 206 |  | 
| 208 207 | 
             
            // convert dependency map to shadow tree updates
         | 
| 209 | 
            -
            void parser::Parser::rebuildShadowLeafUpdates(core::DependencyMap& dependencyMap) {
         | 
| 208 | 
            +
            void parser::Parser::rebuildShadowLeafUpdates(jsi::Runtime& rt, core::DependencyMap& dependencyMap) {
         | 
| 210 209 | 
             
                shadow::ShadowLeafUpdates updates;
         | 
| 211 210 | 
             
                auto& registry = core::UnistylesRegistry::get();
         | 
| 212 | 
            -
                auto& rt = this->_unistylesRuntime->getRuntime();
         | 
| 213 211 |  | 
| 214 212 | 
             
                for (const auto& [shadowNode, unistyles] : dependencyMap) {
         | 
| 215 213 | 
             
                    auto rawProps = this->parseStylesToShadowTreeStyles(rt, unistyles);
         | 
| @@ -218,8 +216,6 @@ void parser::Parser::rebuildShadowLeafUpdates(core::DependencyMap& dependencyMap | |
| 218 216 | 
             
                }
         | 
| 219 217 |  | 
| 220 218 | 
             
                registry.trafficController.setUpdates(updates);
         | 
| 221 | 
            -
             | 
| 222 | 
            -
                // this is required, we need to indicate that there are new changes
         | 
| 223 219 | 
             
                registry.trafficController.resumeUnistylesTraffic();
         | 
| 224 220 | 
             
            }
         | 
| 225 221 |  | 
| @@ -764,26 +760,6 @@ folly::dynamic parser::Parser::parseStylesToShadowTreeStyles(jsi::Runtime& rt, c | |
| 764 760 | 
             
                return jsi::dynamicFromValue(rt, std::move(convertedStyles));
         | 
| 765 761 | 
             
            }
         | 
| 766 762 |  | 
| 767 | 
            -
            folly::dynamic parser::Parser::parseUnistyleToShadowTreeStyles(jsi::Runtime& rt, const Unistyle::Shared unistyle) {
         | 
| 768 | 
            -
                jsi::Object convertedStyles = jsi::Object(rt);
         | 
| 769 | 
            -
                auto& state = core::UnistylesRegistry::get().getState(rt);
         | 
| 770 | 
            -
             | 
| 771 | 
            -
                // can happen for exotic styles
         | 
| 772 | 
            -
                if (!unistyle->parsedStyle.has_value()) {
         | 
| 773 | 
            -
                    return nullptr;
         | 
| 774 | 
            -
                }
         | 
| 775 | 
            -
             | 
| 776 | 
            -
                helpers::enumerateJSIObject(rt, unistyle->parsedStyle.value(), [&](const std::string& propertyName, jsi::Value& propertyValue){
         | 
| 777 | 
            -
                    if (this->isColor(propertyName)) {
         | 
| 778 | 
            -
                        return convertedStyles.setProperty(rt, propertyName.c_str(), jsi::Value(state.parseColor(propertyValue)));
         | 
| 779 | 
            -
                    }
         | 
| 780 | 
            -
             | 
| 781 | 
            -
                    convertedStyles.setProperty(rt, propertyName.c_str(), propertyValue);
         | 
| 782 | 
            -
                });
         | 
| 783 | 
            -
             | 
| 784 | 
            -
                return jsi::dynamicFromValue(rt, std::move(convertedStyles));
         | 
| 785 | 
            -
            }
         | 
| 786 | 
            -
             | 
| 787 763 | 
             
            // check is styleKey contains color
         | 
| 788 764 | 
             
            bool parser::Parser::isColor(const std::string& propertyName) {
         | 
| 789 765 | 
             
                std::string str = propertyName;
         | 
    
        package/cxx/parser/Parser.h
    CHANGED
    
    | @@ -24,9 +24,8 @@ struct Parser { | |
| 24 24 | 
             
                void buildUnistyles(jsi::Runtime& rt, std::shared_ptr<StyleSheet> styleSheet);
         | 
| 25 25 | 
             
                void parseUnistyles(jsi::Runtime& rt, std::shared_ptr<StyleSheet> styleSheet);
         | 
| 26 26 | 
             
                void rebuildUnistylesWithVariants(jsi::Runtime& rt, std::shared_ptr<StyleSheet> styleSheet, Variants& variants);
         | 
| 27 | 
            -
                void rebuildUnistylesInDependencyMap(jsi::Runtime& rt, core::DependencyMap& dependencyMap, std::vector<std::shared_ptr<core::StyleSheet | 
| 28 | 
            -
                void rebuildShadowLeafUpdates(core::DependencyMap& dependencyMap);
         | 
| 29 | 
            -
                folly::dynamic parseUnistyleToShadowTreeStyles(jsi::Runtime& rt, const Unistyle::Shared unistyle);
         | 
| 27 | 
            +
                void rebuildUnistylesInDependencyMap(jsi::Runtime& rt, core::DependencyMap& dependencyMap, std::vector<std::shared_ptr<core::StyleSheet>>& styleSheets);
         | 
| 28 | 
            +
                void rebuildShadowLeafUpdates(jsi::Runtime& rt, core::DependencyMap& dependencyMap);
         | 
| 30 29 | 
             
                folly::dynamic parseStylesToShadowTreeStyles(jsi::Runtime& rt, const std::vector<std::shared_ptr<UnistyleData>>& unistyles);
         | 
| 31 30 | 
             
                void rebuildUnistyle(jsi::Runtime& rt, std::shared_ptr<StyleSheet> styleSheet, Unistyle::Shared unistyle, const Variants& variants, std::optional<std::vector<folly::dynamic>>);
         | 
| 32 31 |  | 
| @@ -13,20 +13,16 @@ struct ShadowTrafficController { | |
| 13 13 | 
             
                }
         | 
| 14 14 |  | 
| 15 15 | 
             
                inline void stopUnistylesTraffic() {
         | 
| 16 | 
            -
                    std::lock_guard<std::mutex> lock(_mutex);
         | 
| 17 | 
            -
             | 
| 18 16 | 
             
                    this->_canCommit = false;
         | 
| 19 17 | 
             
                }
         | 
| 20 18 |  | 
| 21 19 | 
             
                inline void resumeUnistylesTraffic() {
         | 
| 22 | 
            -
                    std::lock_guard<std::mutex> lock(_mutex);
         | 
| 23 | 
            -
             | 
| 24 20 | 
             
                    this->_canCommit = true;
         | 
| 25 21 | 
             
                }
         | 
| 26 22 |  | 
| 27 23 | 
             
                inline shadow::ShadowLeafUpdates& getUpdates() {
         | 
| 28 24 | 
             
                    std::lock_guard<std::mutex> lock(_mutex);
         | 
| 29 | 
            -
             | 
| 25 | 
            +
             
         | 
| 30 26 | 
             
                    return _unistylesUpdates;
         | 
| 31 27 | 
             
                }
         | 
| 32 28 |  | 
| @@ -47,6 +43,14 @@ struct ShadowTrafficController { | |
| 47 43 | 
             
                        targetUpdates.emplace(pair.first, std::move(pair.second));
         | 
| 48 44 | 
             
                    });
         | 
| 49 45 | 
             
                }
         | 
| 46 | 
            +
                
         | 
| 47 | 
            +
                inline void removeShadowNode(const ShadowNodeFamily* shadowNodeFamily) {
         | 
| 48 | 
            +
                    std::lock_guard<std::mutex> lock(_mutex);
         | 
| 49 | 
            +
                    
         | 
| 50 | 
            +
                    if (_unistylesUpdates.contains(shadowNodeFamily)) {
         | 
| 51 | 
            +
                        _unistylesUpdates.erase(shadowNodeFamily);
         | 
| 52 | 
            +
                    }
         | 
| 53 | 
            +
                }
         | 
| 50 54 |  | 
| 51 55 | 
             
                inline void restore() {
         | 
| 52 56 | 
             
                    std::lock_guard<std::mutex> lock(_mutex);
         | 
| @@ -6,13 +6,11 @@ using namespace facebook; | |
| 6 6 |  | 
| 7 7 | 
             
            using AffectedNodes = std::unordered_map<const ShadowNodeFamily*, std::unordered_set<int>>;
         | 
| 8 8 |  | 
| 9 | 
            -
            void shadow::ShadowTreeManager::updateShadowTree( | 
| 9 | 
            +
            void shadow::ShadowTreeManager::updateShadowTree(const ShadowTreeRegistry& shadowTreeRegistry) {
         | 
| 10 10 | 
             
                auto& registry = core::UnistylesRegistry::get();
         | 
| 11 | 
            -
                auto& uiManager = UIManagerBinding::getBinding(rt)->getUIManager();
         | 
| 12 | 
            -
                const auto &shadowTreeRegistry = uiManager.getShadowTreeRegistry();
         | 
| 13 11 | 
             
                auto updates = registry.trafficController.getUpdates();
         | 
| 14 12 |  | 
| 15 | 
            -
                if (updates. | 
| 13 | 
            +
                if (updates.empty()) {
         | 
| 16 14 | 
             
                    return;
         | 
| 17 15 | 
             
                }
         | 
| 18 16 |  | 
| @@ -111,9 +109,16 @@ ShadowNode::Unshared shadow::ShadowTreeManager::cloneShadowTree(const ShadowNode | |
| 111 109 | 
             
                        *shadowNode.getContextContainer()
         | 
| 112 110 | 
             
                    };
         | 
| 113 111 |  | 
| 112 | 
            +
                    #ifdef ANDROID
         | 
| 113 | 
            +
                        auto safeProps = rawPropsIt->second == nullptr ? folly::dynamic::object() : rawPropsIt->second;
         | 
| 114 | 
            +
                        auto newProps = folly::dynamic::merge(shadowNode.getProps()->rawProps, safeProps);
         | 
| 115 | 
            +
                    #else
         | 
| 116 | 
            +
                        auto newProps = rawPropsIt->second;
         | 
| 117 | 
            +
                    #endif
         | 
| 118 | 
            +
             | 
| 114 119 | 
             
                    updatedProps = shadowNode
         | 
| 115 120 | 
             
                        .getComponentDescriptor()
         | 
| 116 | 
            -
                        .cloneProps(propsParserContext, shadowNode.getProps(), RawProps( | 
| 121 | 
            +
                        .cloneProps(propsParserContext, shadowNode.getProps(), RawProps(newProps));
         | 
| 117 122 | 
             
                }
         | 
| 118 123 |  | 
| 119 124 | 
             
                return shadowNode.clone({
         | 
| @@ -16,7 +16,7 @@ using namespace facebook; | |
| 16 16 | 
             
            using AffectedNodes = std::unordered_map<const ShadowNodeFamily *, std::unordered_set<int>>;
         | 
| 17 17 |  | 
| 18 18 | 
             
            struct ShadowTreeManager {
         | 
| 19 | 
            -
                static void updateShadowTree( | 
| 19 | 
            +
                static void updateShadowTree(const ShadowTreeRegistry& shadowTreeRegistry);
         | 
| 20 20 | 
             
                static AffectedNodes findAffectedNodes(const RootShadowNode& rootNode, ShadowLeafUpdates& updates);
         | 
| 21 21 | 
             
                static ShadowNode::Unshared cloneShadowTree(const ShadowNode &shadowNode, ShadowLeafUpdates& updates, AffectedNodes& affectedNodes);
         | 
| 22 22 | 
             
            };
         | 
    
        package/package.json
    CHANGED
    
    | @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            {
         | 
| 2 2 | 
             
              "name": "react-native-unistyles",
         | 
| 3 | 
            -
              "version": "3.0.0-alpha. | 
| 3 | 
            +
              "version": "3.0.0-alpha.37",
         | 
| 4 4 | 
             
              "description": "Level up your React Native StyleSheet",
         | 
| 5 5 | 
             
              "scripts": {
         | 
| 6 6 | 
             
                "test": "jest",
         | 
| @@ -74,11 +74,11 @@ | |
| 74 74 | 
             
                "husky": "9.1.6",
         | 
| 75 75 | 
             
                "jest": "29.7.0",
         | 
| 76 76 | 
             
                "metro-react-native-babel-preset": "0.77.0",
         | 
| 77 | 
            -
                "nitro-codegen": "0. | 
| 77 | 
            +
                "nitro-codegen": "0.16.2",
         | 
| 78 78 | 
             
                "react": "18.3.1",
         | 
| 79 79 | 
             
                "react-native": "0.76.0",
         | 
| 80 80 | 
             
                "react-native-builder-bob": "0.30.2",
         | 
| 81 | 
            -
                "react-native-nitro-modules": "0. | 
| 81 | 
            +
                "react-native-nitro-modules": "0.16.2",
         | 
| 82 82 | 
             
                "react-test-renderer": "18.3.1",
         | 
| 83 83 | 
             
                "release-it": "17.10.0",
         | 
| 84 84 | 
             
                "typescript": "5.6.3"
         | 
| @@ -1,184 +0,0 @@ | |
| 1 | 
            -
            package com.unistyles
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            import UnistylesModuleInsets
         | 
| 4 | 
            -
            import android.content.Context
         | 
| 5 | 
            -
            import android.content.res.Configuration
         | 
| 6 | 
            -
            import android.os.Build
         | 
| 7 | 
            -
            import android.util.DisplayMetrics
         | 
| 8 | 
            -
            import android.view.WindowManager
         | 
| 9 | 
            -
            import androidx.core.text.TextUtilsCompat
         | 
| 10 | 
            -
            import androidx.core.view.ViewCompat
         | 
| 11 | 
            -
            import com.facebook.react.bridge.ReactApplicationContext
         | 
| 12 | 
            -
            import com.margelo.nitro.unistyles.ColorScheme
         | 
| 13 | 
            -
            import com.margelo.nitro.unistyles.Dimensions
         | 
| 14 | 
            -
            import com.margelo.nitro.unistyles.HybridNativePlatformSpec
         | 
| 15 | 
            -
            import com.margelo.nitro.unistyles.Insets
         | 
| 16 | 
            -
            import com.margelo.nitro.unistyles.Orientation
         | 
| 17 | 
            -
            import com.margelo.nitro.unistyles.UnistyleDependency
         | 
| 18 | 
            -
            import com.margelo.nitro.unistyles.UnistylesNativeMiniRuntime
         | 
| 19 | 
            -
            import java.util.Locale
         | 
| 20 | 
            -
             | 
| 21 | 
            -
            class NativePlatform(private val reactContext: ReactApplicationContext): HybridNativePlatformSpec() {
         | 
| 22 | 
            -
                private val _insets = UnistylesModuleInsets(reactContext)
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                override fun getInsets(): Insets {
         | 
| 25 | 
            -
                    return _insets.getInsets()
         | 
| 26 | 
            -
                }
         | 
| 27 | 
            -
             | 
| 28 | 
            -
                override fun getColorScheme(): ColorScheme {
         | 
| 29 | 
            -
                    val uiMode = reactContext.resources.configuration.uiMode
         | 
| 30 | 
            -
             | 
| 31 | 
            -
                    val colorScheme = when (uiMode.and(Configuration.UI_MODE_NIGHT_MASK)) {
         | 
| 32 | 
            -
                        Configuration.UI_MODE_NIGHT_YES -> ColorScheme.DARK
         | 
| 33 | 
            -
                        Configuration.UI_MODE_NIGHT_NO -> ColorScheme.LIGHT
         | 
| 34 | 
            -
                        else -> ColorScheme.UNSPECIFIED
         | 
| 35 | 
            -
                    }
         | 
| 36 | 
            -
             | 
| 37 | 
            -
                    return colorScheme
         | 
| 38 | 
            -
                }
         | 
| 39 | 
            -
             | 
| 40 | 
            -
                override fun getFontScale(): Double {
         | 
| 41 | 
            -
                    return reactContext.resources.configuration.fontScale.toDouble()
         | 
| 42 | 
            -
                }
         | 
| 43 | 
            -
             | 
| 44 | 
            -
                override fun getPixelRatio(): Double {
         | 
| 45 | 
            -
                    return reactContext.resources.displayMetrics.density.toDouble()
         | 
| 46 | 
            -
                }
         | 
| 47 | 
            -
             | 
| 48 | 
            -
                override fun getOrientation(): Orientation {
         | 
| 49 | 
            -
                    val orientation = when (reactContext.resources.configuration.orientation) {
         | 
| 50 | 
            -
                        Configuration.ORIENTATION_PORTRAIT -> Orientation.PORTRAIT
         | 
| 51 | 
            -
                        Configuration.ORIENTATION_LANDSCAPE -> Orientation.LANDSCAPE
         | 
| 52 | 
            -
                        else -> Orientation.PORTRAIT
         | 
| 53 | 
            -
                    }
         | 
| 54 | 
            -
             | 
| 55 | 
            -
                    return orientation
         | 
| 56 | 
            -
                }
         | 
| 57 | 
            -
             | 
| 58 | 
            -
                override fun getContentSizeCategory(): String {
         | 
| 59 | 
            -
                    val fontScale = reactContext.resources.configuration.fontScale
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                    val contentSizeCategory = when {
         | 
| 62 | 
            -
                        fontScale <= 0.85f -> "Small"
         | 
| 63 | 
            -
                        fontScale <= 1.0f -> "Default"
         | 
| 64 | 
            -
                        fontScale <= 1.15f -> "Large"
         | 
| 65 | 
            -
                        fontScale <= 1.3f -> "ExtraLarge"
         | 
| 66 | 
            -
                        fontScale <= 1.5f -> "Huge"
         | 
| 67 | 
            -
                        fontScale <= 1.8 -> "ExtraHuge"
         | 
| 68 | 
            -
                        else -> "ExtraExtraHuge"
         | 
| 69 | 
            -
                    }
         | 
| 70 | 
            -
             | 
| 71 | 
            -
                    return contentSizeCategory
         | 
| 72 | 
            -
                }
         | 
| 73 | 
            -
             | 
| 74 | 
            -
                override fun getScreenDimensions(): Dimensions {
         | 
| 75 | 
            -
                    // function takes in count edge-to-edge layout
         | 
| 76 | 
            -
                    when {
         | 
| 77 | 
            -
                        Build.VERSION.SDK_INT < Build.VERSION_CODES.R -> {
         | 
| 78 | 
            -
                            val windowManager = reactContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager
         | 
| 79 | 
            -
                            val metrics = DisplayMetrics()
         | 
| 80 | 
            -
             | 
| 81 | 
            -
                            windowManager.defaultDisplay.getRealMetrics(metrics)
         | 
| 82 | 
            -
             | 
| 83 | 
            -
                            val screenWidth = (metrics.widthPixels / metrics.density).toDouble()
         | 
| 84 | 
            -
                            val screenHeight = (metrics.heightPixels / metrics.density).toDouble()
         | 
| 85 | 
            -
             | 
| 86 | 
            -
                            return Dimensions(screenWidth, screenHeight)
         | 
| 87 | 
            -
                        }
         | 
| 88 | 
            -
                        else -> {
         | 
| 89 | 
            -
                            val displayMetrics = reactContext.resources.displayMetrics
         | 
| 90 | 
            -
             | 
| 91 | 
            -
                            reactContext.currentActivity?.windowManager?.currentWindowMetrics?.bounds?.let {
         | 
| 92 | 
            -
                                val boundsWidth = (it.width() / displayMetrics.density).toDouble()
         | 
| 93 | 
            -
                                val boundsHeight = (it.height() / displayMetrics.density).toDouble()
         | 
| 94 | 
            -
             | 
| 95 | 
            -
                                return Dimensions(boundsWidth, boundsHeight)
         | 
| 96 | 
            -
                            } ?: run {
         | 
| 97 | 
            -
                                val screenWidth = (displayMetrics.widthPixels / displayMetrics.density).toDouble()
         | 
| 98 | 
            -
                                val screenHeight = (displayMetrics.heightPixels / displayMetrics.density).toDouble()
         | 
| 99 | 
            -
             | 
| 100 | 
            -
                                return Dimensions(screenWidth, screenHeight)
         | 
| 101 | 
            -
                            }
         | 
| 102 | 
            -
                        }
         | 
| 103 | 
            -
                    }
         | 
| 104 | 
            -
                }
         | 
| 105 | 
            -
             | 
| 106 | 
            -
                override fun getStatusBarDimensions(): Dimensions {
         | 
| 107 | 
            -
                    // todo
         | 
| 108 | 
            -
                    return Dimensions(0.0, 0.0)
         | 
| 109 | 
            -
                }
         | 
| 110 | 
            -
             | 
| 111 | 
            -
                override fun getNavigationBarDimensions(): Dimensions {
         | 
| 112 | 
            -
                    // todo
         | 
| 113 | 
            -
                    return Dimensions(0.0, 0.0)
         | 
| 114 | 
            -
                }
         | 
| 115 | 
            -
             | 
| 116 | 
            -
                override fun getPrefersRtlDirection(): Boolean {
         | 
| 117 | 
            -
                    // forced by React Native
         | 
| 118 | 
            -
                    val sharedPrefs = reactContext.getSharedPreferences(
         | 
| 119 | 
            -
                        "com.facebook.react.modules.i18nmanager.I18nUtil",
         | 
| 120 | 
            -
                        Context.MODE_PRIVATE
         | 
| 121 | 
            -
                    )
         | 
| 122 | 
            -
                    val hasForcedRtl = sharedPrefs.getBoolean("RCTI18nUtil_forceRTL", false)
         | 
| 123 | 
            -
                    // user preferences
         | 
| 124 | 
            -
                    val isRtl = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL
         | 
| 125 | 
            -
             | 
| 126 | 
            -
                    return hasForcedRtl || isRtl
         | 
| 127 | 
            -
                }
         | 
| 128 | 
            -
             | 
| 129 | 
            -
                override fun setRootViewBackgroundColor(color: Double) {
         | 
| 130 | 
            -
                    // todo
         | 
| 131 | 
            -
                }
         | 
| 132 | 
            -
             | 
| 133 | 
            -
                override fun setNavigationBarBackgroundColor(color: Double) {
         | 
| 134 | 
            -
                    // todo
         | 
| 135 | 
            -
                }
         | 
| 136 | 
            -
             | 
| 137 | 
            -
                override fun setNavigationBarHidden(isHidden: Boolean) {
         | 
| 138 | 
            -
                    // todo
         | 
| 139 | 
            -
                }
         | 
| 140 | 
            -
             | 
| 141 | 
            -
                override fun setStatusBarHidden(isHidden: Boolean) {
         | 
| 142 | 
            -
                    // todo
         | 
| 143 | 
            -
                }
         | 
| 144 | 
            -
             | 
| 145 | 
            -
                override fun setStatusBarBackgroundColor(color: Double) {
         | 
| 146 | 
            -
                    // todo
         | 
| 147 | 
            -
                }
         | 
| 148 | 
            -
             | 
| 149 | 
            -
                override fun setImmersiveMode(isEnabled: Boolean) {
         | 
| 150 | 
            -
                    this.setStatusBarHidden(isEnabled)
         | 
| 151 | 
            -
                    this.setNavigationBarHidden(isEnabled)
         | 
| 152 | 
            -
                }
         | 
| 153 | 
            -
             | 
| 154 | 
            -
                override fun getMiniRuntime(): UnistylesNativeMiniRuntime {
         | 
| 155 | 
            -
                    return UnistylesNativeMiniRuntime(
         | 
| 156 | 
            -
                        colorScheme = this.getColorScheme(),
         | 
| 157 | 
            -
                        screen = this.getScreenDimensions(),
         | 
| 158 | 
            -
                        contentSizeCategory = this.getContentSizeCategory(),
         | 
| 159 | 
            -
                        insets = this.getInsets(),
         | 
| 160 | 
            -
                        pixelRatio = this.getPixelRatio(),
         | 
| 161 | 
            -
                        fontScale = this.getFontScale(),
         | 
| 162 | 
            -
                        rtl = this.getPrefersRtlDirection(),
         | 
| 163 | 
            -
                        statusBar = this.getStatusBarDimensions(),
         | 
| 164 | 
            -
                        navigationBar = this.getNavigationBarDimensions(),
         | 
| 165 | 
            -
                        isPortrait = this.getOrientation() == Orientation.PORTRAIT,
         | 
| 166 | 
            -
                        isLandscape = this.getOrientation() == Orientation.LANDSCAPE
         | 
| 167 | 
            -
                    )
         | 
| 168 | 
            -
                }
         | 
| 169 | 
            -
             | 
| 170 | 
            -
                override fun registerPlatformListener(callback: (dependencies: Array<UnistyleDependency>) -> Unit) {
         | 
| 171 | 
            -
                    // todo
         | 
| 172 | 
            -
                }
         | 
| 173 | 
            -
             | 
| 174 | 
            -
                override fun registerImeListener(callback: () -> Unit) {
         | 
| 175 | 
            -
                    // todo
         | 
| 176 | 
            -
                }
         | 
| 177 | 
            -
             | 
| 178 | 
            -
                override fun unregisterPlatformListeners() {
         | 
| 179 | 
            -
                    // todo
         | 
| 180 | 
            -
                }
         | 
| 181 | 
            -
             | 
| 182 | 
            -
                override val memorySize: Long
         | 
| 183 | 
            -
                    get() = 0
         | 
| 184 | 
            -
            }
         | 
| @@ -1,8 +0,0 @@ | |
| 1 | 
            -
            import com.facebook.react.bridge.ReactApplicationContext
         | 
| 2 | 
            -
            import com.margelo.nitro.unistyles.Insets
         | 
| 3 | 
            -
             | 
| 4 | 
            -
            class UnistylesModuleInsets(private val reactContext: ReactApplicationContext) {
         | 
| 5 | 
            -
                fun getInsets(): Insets {
         | 
| 6 | 
            -
                    return Insets(top = 0.0, bottom = 0.0, left = 0.0, right = 0.0, ime = 0.0)
         | 
| 7 | 
            -
                }
         | 
| 8 | 
            -
            }
         |