@sbaiahmed1/react-native-blur 4.6.1 → 4.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -11
- package/android/src/main/java/com/sbaiahmed1/reactnativeblur/BlurType.kt +24 -22
- package/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurSwitch.kt +19 -1
- package/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurSwitchManager.kt +5 -0
- package/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurView.kt +63 -10
- package/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurViewManager.kt +5 -0
- package/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeProgressiveBlurView.kt +60 -10
- package/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeProgressiveBlurViewManager.kt +5 -0
- package/ios/Views/BlurEffectView.swift +32 -19
- package/lib/module/BlurSwitch.js +11 -6
- package/lib/module/BlurSwitch.js.map +1 -1
- package/lib/module/BlurView.js +2 -0
- package/lib/module/BlurView.js.map +1 -1
- package/lib/module/LiquidGlassContainer.js.map +1 -1
- package/lib/module/LiquidGlassView.js.map +1 -1
- package/lib/module/ProgressiveBlurView.js +3 -0
- package/lib/module/ProgressiveBlurView.js.map +1 -1
- package/lib/module/ReactNativeBlurSwitchNativeComponent.ts +2 -37
- package/lib/module/ReactNativeBlurViewNativeComponent.ts +2 -0
- package/lib/module/ReactNativeLiquidGlassContainerNativeComponent.ts +0 -5
- package/lib/module/ReactNativeLiquidGlassViewNativeComponent.ts +0 -35
- package/lib/module/ReactNativeProgressiveBlurViewNativeComponent.ts +2 -0
- package/lib/typescript/src/BlurSwitch.d.ts +10 -0
- package/lib/typescript/src/BlurSwitch.d.ts.map +1 -1
- package/lib/typescript/src/BlurView.d.ts +11 -2
- package/lib/typescript/src/BlurView.d.ts.map +1 -1
- package/lib/typescript/src/LiquidGlassContainer.d.ts +3 -2
- package/lib/typescript/src/LiquidGlassContainer.d.ts.map +1 -1
- package/lib/typescript/src/LiquidGlassView.d.ts +25 -15
- package/lib/typescript/src/LiquidGlassView.d.ts.map +1 -1
- package/lib/typescript/src/ProgressiveBlurView.d.ts +11 -2
- package/lib/typescript/src/ProgressiveBlurView.d.ts.map +1 -1
- package/lib/typescript/src/ReactNativeBlurSwitchNativeComponent.d.ts +2 -32
- package/lib/typescript/src/ReactNativeBlurSwitchNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/ReactNativeBlurViewNativeComponent.d.ts +2 -1
- package/lib/typescript/src/ReactNativeBlurViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/ReactNativeLiquidGlassContainerNativeComponent.d.ts +0 -5
- package/lib/typescript/src/ReactNativeLiquidGlassContainerNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/ReactNativeLiquidGlassViewNativeComponent.d.ts +0 -30
- package/lib/typescript/src/ReactNativeLiquidGlassViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/ReactNativeProgressiveBlurViewNativeComponent.d.ts +2 -1
- package/lib/typescript/src/ReactNativeProgressiveBlurViewNativeComponent.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/BlurSwitch.tsx +23 -3
- package/src/BlurView.tsx +14 -2
- package/src/LiquidGlassContainer.tsx +3 -2
- package/src/LiquidGlassView.tsx +25 -15
- package/src/ProgressiveBlurView.tsx +15 -2
- package/src/ReactNativeBlurSwitchNativeComponent.ts +2 -37
- package/src/ReactNativeBlurViewNativeComponent.ts +2 -0
- package/src/ReactNativeLiquidGlassContainerNativeComponent.ts +0 -5
- package/src/ReactNativeLiquidGlassViewNativeComponent.ts +0 -35
- package/src/ReactNativeProgressiveBlurViewNativeComponent.ts +2 -0
package/README.md
CHANGED
|
@@ -626,6 +626,7 @@ All props are optional and have sensible defaults.
|
|
|
626
626
|
| ---------------------------------- | ------------ | ----------- | ----------------------------------------------------------------------------- |
|
|
627
627
|
| `blurType` | `BlurType` | `'xlight'` | The type of blur effect to apply |
|
|
628
628
|
| `blurAmount` | `number` | `10.0` | The intensity of the blur effect (0-100) |
|
|
629
|
+
| `blurRounds` | `number` | `5` | The number of blur interactions must be an integer value (1-15) |
|
|
629
630
|
| `ignoreSafeArea` | `boolean` | `true` | (iOS only) Controls whether the blur effect should ignore all safe area edges |
|
|
630
631
|
| `reducedTransparencyFallbackColor` | `string` | `'#FFFFFF'` | Fallback color when reduced transparency is enabled |
|
|
631
632
|
| `overlayColor` | `ColorValue` | `undefined` | The overlay color to apply on top of the blur effect |
|
|
@@ -647,16 +648,17 @@ All props are optional and have sensible defaults.
|
|
|
647
648
|
|
|
648
649
|
All props are optional and have sensible defaults.
|
|
649
650
|
|
|
650
|
-
| Prop | Type | Default | Description
|
|
651
|
-
| ---------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------- |
|
|
652
|
-
| `blurType` | `BlurType` | `'regular'` | The type of blur effect to apply
|
|
653
|
-
| `blurAmount` | `number` | `20.0` | Maximum blur radius in pixels
|
|
654
|
-
| `
|
|
655
|
-
| `
|
|
656
|
-
| `
|
|
657
|
-
| `
|
|
658
|
-
| `
|
|
659
|
-
| `
|
|
651
|
+
| Prop | Type | Default | Description |
|
|
652
|
+
| ---------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------- | --------------------------------------------------------------- |
|
|
653
|
+
| `blurType` | `BlurType` | `'regular'` | The type of blur effect to apply |
|
|
654
|
+
| `blurAmount` | `number` | `20.0` | Maximum blur radius in pixels |
|
|
655
|
+
| `blurRounds` | `number` | `5` | The number of blur interactions must be an integer value (1-15) |
|
|
656
|
+
| `direction` | `'blurredTopClearBottom' \| 'blurredBottomClearTop' \| 'blurredCenterClearTopAndBottom'` | `'blurredTopClearBottom'` | Direction of the blur gradient |
|
|
657
|
+
| `startOffset` | `number` | `0.0` | Where the gradient starts (0.0 to 1.0) |
|
|
658
|
+
| `reducedTransparencyFallbackColor` | `string` | `'#FFFFFF'` | Fallback color when reduced transparency is enabled |
|
|
659
|
+
| `overlayColor` | `ColorValue` | `undefined` | The overlay color to apply on top of the blur effect |
|
|
660
|
+
| `style` | `ViewStyle` | `undefined` | Style object for the blur view |
|
|
661
|
+
| `children` | `ReactNode` | `undefined` | Child components to render inside the blur view |
|
|
660
662
|
|
|
661
663
|
> **Platform Note**: `ProgressiveBlurView` works on both **iOS** and **Android**.
|
|
662
664
|
>
|
|
@@ -696,7 +698,8 @@ All props are optional and have sensible defaults.
|
|
|
696
698
|
| --------------- | ------------------------------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
697
699
|
| `value` | `boolean` | `false` | The current value of the switch |
|
|
698
700
|
| `onValueChange` | `(value: boolean) => void` | `undefined` | Callback invoked when the switch value changes |
|
|
699
|
-
| `blurAmount` | `number` | `10`
|
|
701
|
+
| `blurAmount` | `number` | `10.0` | (Android only) The intensity of the blur effect (0-100) |
|
|
702
|
+
| `blurRounds` | `number` | `5` | The number of blur interactions must be an integer value (1-15) |
|
|
700
703
|
| `thumbColor` | `ColorValue` | `'#FFFFFF'` | (iOS only) The color of the switch thumb |
|
|
701
704
|
| `trackColor` | `{ false?: ColorValue; true?: ColorValue }` | `{ false: '#E5E5EA', true: '#34C759' }` | Track colors. On Android, only `true` is used - QmBlurView auto-calculates on/off colors from base color |
|
|
702
705
|
| `disabled` | `boolean` | `false` | Whether the switch is disabled (prevents interaction but maintains current value) |
|
|
@@ -845,6 +848,7 @@ interface MyGlassContainerProps {
|
|
|
845
848
|
const blurProps: BlurViewProps = {
|
|
846
849
|
blurType: 'systemMaterial',
|
|
847
850
|
blurAmount: 50,
|
|
851
|
+
blurRounds: 10,
|
|
848
852
|
reducedTransparencyFallbackColor: '#FFFFFF',
|
|
849
853
|
overlayColor: '#FF000040',
|
|
850
854
|
};
|
|
@@ -867,6 +871,7 @@ const blurSwitchProps: BlurSwitchProps = {
|
|
|
867
871
|
value: true,
|
|
868
872
|
onValueChange: (value) => console.log(value),
|
|
869
873
|
blurAmount: 20,
|
|
874
|
+
blurRounds: 15,
|
|
870
875
|
trackColor: { true: '#34C759' },
|
|
871
876
|
disabled: false,
|
|
872
877
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package com.sbaiahmed1.reactnativeblur
|
|
2
2
|
|
|
3
|
+
import android.content.res.Configuration
|
|
3
4
|
import android.graphics.Color
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -9,51 +10,52 @@ import android.graphics.Color
|
|
|
9
10
|
enum class BlurType(val overlayColor: Int) {
|
|
10
11
|
XLIGHT(Color.argb(140, 240, 240, 240)),
|
|
11
12
|
LIGHT(Color.argb(42, 255, 255, 255)),
|
|
12
|
-
DARK(Color.argb(120,
|
|
13
|
+
DARK(Color.argb(120, 26, 22, 22)),
|
|
13
14
|
EXTRA_DARK(Color.argb(160, 35, 35, 35)),
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
REGULAR_LIGHT(Color.argb(35, 255, 255, 255)),
|
|
16
|
+
REGULAR_DARK(Color.argb(35, 28, 28, 30)),
|
|
17
|
+
PROMINENT_LIGHT(Color.argb(140, 240, 240, 240)),
|
|
18
|
+
PROMINENT_DARK(Color.argb(140, 28, 28, 30)),
|
|
19
|
+
SYSTEM_ULTRA_THIN_MATERIAL_LIGHT(Color.argb(75, 240, 240, 240)),
|
|
18
20
|
SYSTEM_ULTRA_THIN_MATERIAL_DARK(Color.argb(65, 40, 40, 40)),
|
|
19
|
-
|
|
20
|
-
SYSTEM_THIN_MATERIAL_LIGHT(Color.argb(105, 240, 240, 240)),
|
|
21
|
+
SYSTEM_THIN_MATERIAL_LIGHT(Color.argb(102, 240, 240, 240)),
|
|
21
22
|
SYSTEM_THIN_MATERIAL_DARK(Color.argb(102, 35, 35, 35)),
|
|
22
|
-
|
|
23
|
-
SYSTEM_MATERIAL_LIGHT(Color.argb(130, 245, 245, 245)),
|
|
23
|
+
SYSTEM_MATERIAL_LIGHT(Color.argb(140, 245, 245, 245)),
|
|
24
24
|
SYSTEM_MATERIAL_DARK(Color.argb(215, 65, 60, 60)),
|
|
25
|
-
|
|
26
|
-
SYSTEM_THICK_MATERIAL_LIGHT(Color.argb(160, 242, 242, 242)),
|
|
25
|
+
SYSTEM_THICK_MATERIAL_LIGHT(Color.argb(210, 248, 248, 248)),
|
|
27
26
|
SYSTEM_THICK_MATERIAL_DARK(Color.argb(160, 35, 35, 35)),
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
SYSTEM_CHROME_MATERIAL_DARK(Color.argb(90, 32, 32, 32));
|
|
27
|
+
SYSTEM_CHROME_MATERIAL_LIGHT(Color.argb(165, 248, 248, 248)),
|
|
28
|
+
SYSTEM_CHROME_MATERIAL_DARK(Color.argb(100, 32, 32, 32));
|
|
31
29
|
|
|
32
30
|
companion object {
|
|
33
31
|
/**
|
|
34
32
|
* Get BlurType from string, with fallback to LIGHT for unknown types.
|
|
33
|
+
* Uses the provided configuration to determine if dark mode is active for
|
|
34
|
+
* appropriate defaults.
|
|
35
35
|
*/
|
|
36
|
-
fun fromString(type: String): BlurType {
|
|
36
|
+
fun fromString(type: String, configuration: Configuration): BlurType {
|
|
37
|
+
val isDarkMode = (configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
|
|
38
|
+
|
|
37
39
|
return when (type.lowercase()) {
|
|
38
40
|
"xlight" -> XLIGHT
|
|
39
41
|
"light" -> LIGHT
|
|
40
42
|
"dark" -> DARK
|
|
41
43
|
"extradark" -> EXTRA_DARK
|
|
42
|
-
"regular" ->
|
|
43
|
-
"prominent" ->
|
|
44
|
-
"systemultrathinmaterial" ->
|
|
44
|
+
"regular" -> if (isDarkMode) REGULAR_DARK else REGULAR_LIGHT
|
|
45
|
+
"prominent" -> if (isDarkMode) PROMINENT_DARK else PROMINENT_LIGHT
|
|
46
|
+
"systemultrathinmaterial" -> if (isDarkMode) SYSTEM_ULTRA_THIN_MATERIAL_DARK else SYSTEM_ULTRA_THIN_MATERIAL_LIGHT
|
|
45
47
|
"systemultrathinmateriallight" -> SYSTEM_ULTRA_THIN_MATERIAL_LIGHT
|
|
46
48
|
"systemultrathinmaterialdark" -> SYSTEM_ULTRA_THIN_MATERIAL_DARK
|
|
47
|
-
"systemthinmaterial" ->
|
|
49
|
+
"systemthinmaterial" -> if (isDarkMode) SYSTEM_THIN_MATERIAL_DARK else SYSTEM_THIN_MATERIAL_LIGHT
|
|
48
50
|
"systemthinmateriallight" -> SYSTEM_THIN_MATERIAL_LIGHT
|
|
49
51
|
"systemthinmaterialdark" -> SYSTEM_THIN_MATERIAL_DARK
|
|
50
|
-
"systemmaterial" ->
|
|
52
|
+
"systemmaterial" -> if (isDarkMode) SYSTEM_MATERIAL_DARK else SYSTEM_MATERIAL_LIGHT
|
|
51
53
|
"systemmateriallight" -> SYSTEM_MATERIAL_LIGHT
|
|
52
54
|
"systemmaterialdark" -> SYSTEM_MATERIAL_DARK
|
|
53
|
-
"systemthickmaterial" ->
|
|
55
|
+
"systemthickmaterial" -> if (isDarkMode) SYSTEM_THICK_MATERIAL_DARK else SYSTEM_THICK_MATERIAL_LIGHT
|
|
54
56
|
"systemthickmateriallight" -> SYSTEM_THICK_MATERIAL_LIGHT
|
|
55
57
|
"systemthickmaterialdark" -> SYSTEM_THICK_MATERIAL_DARK
|
|
56
|
-
"systemchromematerial" ->
|
|
58
|
+
"systemchromematerial" -> if (isDarkMode) SYSTEM_CHROME_MATERIAL_DARK else SYSTEM_CHROME_MATERIAL_LIGHT
|
|
57
59
|
"systemchromemateriallight" -> SYSTEM_CHROME_MATERIAL_LIGHT
|
|
58
60
|
"systemchromematerialdark" -> SYSTEM_CHROME_MATERIAL_DARK
|
|
59
61
|
else -> XLIGHT // default fallback
|
|
@@ -22,9 +22,11 @@ class ReactNativeBlurSwitch : BlurSwitchButtonView {
|
|
|
22
22
|
private var onValueChangeListener: ((Boolean) -> Unit)? = null
|
|
23
23
|
private var currentValue = false
|
|
24
24
|
private var isDisabled = false
|
|
25
|
+
private var currentBlurRounds = DEFAULT_BLUR_ROUNDS
|
|
25
26
|
|
|
26
27
|
companion object {
|
|
27
28
|
private const val TAG = "ReactNativeBlurSwitch"
|
|
29
|
+
private const val DEFAULT_BLUR_ROUNDS = 5
|
|
28
30
|
private const val DEFAULT_WIDTH_DP = 65f
|
|
29
31
|
private const val DEFAULT_HEIGHT_DP = 36f
|
|
30
32
|
private const val MIN_BLUR_AMOUNT = 0f
|
|
@@ -70,7 +72,7 @@ class ReactNativeBlurSwitch : BlurSwitchButtonView {
|
|
|
70
72
|
*/
|
|
71
73
|
private fun initializeSwitch() {
|
|
72
74
|
try {
|
|
73
|
-
blurRounds =
|
|
75
|
+
blurRounds = currentBlurRounds
|
|
74
76
|
|
|
75
77
|
setOnCheckedChangeListener { isChecked ->
|
|
76
78
|
if (isDisabled) {
|
|
@@ -172,6 +174,22 @@ class ReactNativeBlurSwitch : BlurSwitchButtonView {
|
|
|
172
174
|
}
|
|
173
175
|
}
|
|
174
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Set the number of blur rounds.
|
|
179
|
+
* @param rounds The number of blur rounds (1-15)
|
|
180
|
+
*/
|
|
181
|
+
fun setRounds(rounds: Int) {
|
|
182
|
+
val blurRounds = rounds.coerceIn(1, 15)
|
|
183
|
+
currentBlurRounds = blurRounds
|
|
184
|
+
logDebug("setRounds: $rounds -> $blurRounds")
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
super.setBlurRounds(blurRounds)
|
|
188
|
+
} catch (e: Exception) {
|
|
189
|
+
logError("Failed to set blur rounds: ${e.message}", e)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
175
193
|
/**
|
|
176
194
|
* Set whether the switch is disabled.
|
|
177
195
|
* @param disabled True to disable, false to enable
|
package/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeBlurSwitchManager.kt
CHANGED
|
@@ -40,6 +40,11 @@ class ReactNativeBlurSwitchManager : SimpleViewManager<ReactNativeBlurSwitch>()
|
|
|
40
40
|
view?.setBlurAmount(blurAmount.toFloat())
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
@ReactProp(name = "blurRounds")
|
|
44
|
+
fun setBlurRounds(view: ReactNativeBlurSwitch?, blurRounds: Int) {
|
|
45
|
+
view?.setRounds(blurRounds)
|
|
46
|
+
}
|
|
47
|
+
|
|
43
48
|
@ReactProp(name = "thumbColor")
|
|
44
49
|
fun setThumbColor(view: ReactNativeBlurSwitch?, color: String?) {
|
|
45
50
|
color?.let {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package com.sbaiahmed1.reactnativeblur
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
+
import android.content.res.Configuration
|
|
4
5
|
import android.graphics.Color
|
|
5
6
|
import android.graphics.Outline
|
|
6
7
|
import android.util.AttributeSet
|
|
@@ -30,11 +31,13 @@ import android.view.View.MeasureSpec
|
|
|
30
31
|
class ReactNativeBlurView : BlurViewGroup {
|
|
31
32
|
private var currentBlurRadius = DEFAULT_BLUR_RADIUS
|
|
32
33
|
private var currentOverlayColor = Color.TRANSPARENT
|
|
34
|
+
private var currentBlurRounds = DEFAULT_BLUR_ROUNDS
|
|
33
35
|
private var currentCornerRadius = 0f
|
|
34
36
|
private var glassTintColor: Int = Color.TRANSPARENT
|
|
35
37
|
private var glassOpacity: Float = 1.0f
|
|
36
38
|
private var viewType: String = "blur"
|
|
37
39
|
private var glassType: String = "clear"
|
|
40
|
+
private var currentBlurType: String = "xlight"
|
|
38
41
|
private var isBlurInitialized: Boolean = false
|
|
39
42
|
private var initRunnable: Runnable? = null
|
|
40
43
|
|
|
@@ -42,6 +45,7 @@ class ReactNativeBlurView : BlurViewGroup {
|
|
|
42
45
|
private const val TAG = "ReactNativeBlurView"
|
|
43
46
|
private const val MAX_BLUR_RADIUS = 100f
|
|
44
47
|
private const val DEFAULT_BLUR_RADIUS = 10f
|
|
48
|
+
private const val DEFAULT_BLUR_ROUNDS = 5
|
|
45
49
|
private const val DEBUG = false
|
|
46
50
|
|
|
47
51
|
// Cross-platform blur amount constants
|
|
@@ -91,7 +95,7 @@ class ReactNativeBlurView : BlurViewGroup {
|
|
|
91
95
|
super.setBackgroundColor(currentOverlayColor)
|
|
92
96
|
clipChildren = true
|
|
93
97
|
clipToOutline = true
|
|
94
|
-
blurRounds =
|
|
98
|
+
blurRounds = currentBlurRounds
|
|
95
99
|
super.setDownsampleFactor(6.0F)
|
|
96
100
|
}
|
|
97
101
|
|
|
@@ -187,16 +191,18 @@ class ReactNativeBlurView : BlurViewGroup {
|
|
|
187
191
|
/**
|
|
188
192
|
* Finds the optimal view to use as blur capture root.
|
|
189
193
|
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
194
|
+
* Priority:
|
|
195
|
+
* 1. Nearest react-native-screens Screen ancestor — scopes blur to the current
|
|
196
|
+
* screen and prevents capturing navigation transition artifacts.
|
|
197
|
+
* 2. Nearest ReactRootView ancestor — scopes blur to the React Native root when
|
|
198
|
+
* the component is not inside a Screen (e.g. plain View hierarchies). Without
|
|
199
|
+
* this fallback, QmBlurView defaults to the activity decor view and blurs the
|
|
200
|
+
* entire screen instead of just the component area (issue #89).
|
|
201
|
+
* 3. null — returned for modals, which intentionally need to blur content from
|
|
202
|
+
* the main activity window (decor view is correct there).
|
|
197
203
|
*/
|
|
198
204
|
private fun findOptimalBlurRoot(): ViewGroup? {
|
|
199
|
-
return findNearestScreenAncestor()
|
|
205
|
+
return findNearestScreenAncestor() ?: findNearestReactRootView()
|
|
200
206
|
}
|
|
201
207
|
|
|
202
208
|
/**
|
|
@@ -214,6 +220,22 @@ class ReactNativeBlurView : BlurViewGroup {
|
|
|
214
220
|
return null
|
|
215
221
|
}
|
|
216
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Walks up the view hierarchy looking for the React Native root view.
|
|
225
|
+
* Used as a fallback when no Screen ancestor exists, to scope the blur
|
|
226
|
+
* capture to the RN root rather than the full activity decor view.
|
|
227
|
+
*/
|
|
228
|
+
private fun findNearestReactRootView(): ViewGroup? {
|
|
229
|
+
var currentParent = this.parent
|
|
230
|
+
while (currentParent != null) {
|
|
231
|
+
if (currentParent.javaClass.name == "com.facebook.react.ReactRootView") {
|
|
232
|
+
return currentParent as? ViewGroup
|
|
233
|
+
}
|
|
234
|
+
currentParent = currentParent.parent
|
|
235
|
+
}
|
|
236
|
+
return null
|
|
237
|
+
}
|
|
238
|
+
|
|
217
239
|
/**
|
|
218
240
|
* Initialize the blur view with current settings.
|
|
219
241
|
* Called after the view is attached and the blur root has been swapped.
|
|
@@ -270,12 +292,29 @@ class ReactNativeBlurView : BlurViewGroup {
|
|
|
270
292
|
}
|
|
271
293
|
}
|
|
272
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Set the number of blur rounds.
|
|
297
|
+
* @param rounds The number of blur rounds (1-15)
|
|
298
|
+
*/
|
|
299
|
+
fun setRounds(rounds: Int) {
|
|
300
|
+
val blurRounds = rounds.coerceIn(1, 15)
|
|
301
|
+
currentBlurRounds = blurRounds
|
|
302
|
+
logDebug("setRounds: $rounds -> $blurRounds")
|
|
303
|
+
|
|
304
|
+
try {
|
|
305
|
+
super.setBlurRounds(blurRounds)
|
|
306
|
+
} catch (e: Exception) {
|
|
307
|
+
logError("Failed to set blur rounds: ${e.message}", e)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
273
311
|
/**
|
|
274
312
|
* Set the blur type which determines the overlay color.
|
|
275
313
|
* @param type The blur type string (case-insensitive)
|
|
276
314
|
*/
|
|
277
315
|
fun setBlurType(type: String) {
|
|
278
|
-
|
|
316
|
+
currentBlurType = type
|
|
317
|
+
val blurType = BlurType.fromString(type, resources.configuration)
|
|
279
318
|
currentOverlayColor = blurType.overlayColor
|
|
280
319
|
logDebug("setBlurType: $type -> ${blurType.name}")
|
|
281
320
|
|
|
@@ -460,4 +499,18 @@ class ReactNativeBlurView : BlurViewGroup {
|
|
|
460
499
|
// re-positioning children based on its own logic (e.g. gravity), which would
|
|
461
500
|
// conflict with React Native's layout.
|
|
462
501
|
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Handle configuration changes, such as dark mode or orientation changes.
|
|
505
|
+
* This ensures the blur view updates its overlay color based on the new
|
|
506
|
+
* configuration.
|
|
507
|
+
*/
|
|
508
|
+
override fun onConfigurationChanged(newConfig: Configuration) {
|
|
509
|
+
super.onConfigurationChanged(newConfig)
|
|
510
|
+
|
|
511
|
+
if (viewType == "blur") {
|
|
512
|
+
// Re-apply blur type to update overlay color based on new configuration
|
|
513
|
+
setBlurType(currentBlurType)
|
|
514
|
+
}
|
|
515
|
+
}
|
|
463
516
|
}
|
|
@@ -39,6 +39,11 @@ class ReactNativeBlurViewManager : ViewGroupManager<ReactNativeBlurView>(),
|
|
|
39
39
|
view?.setBlurAmount(blurAmount.toFloat())
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
@ReactProp(name = "blurRounds")
|
|
43
|
+
override fun setBlurRounds(view: ReactNativeBlurView?, blurRounds: Int) {
|
|
44
|
+
view?.setRounds(blurRounds)
|
|
45
|
+
}
|
|
46
|
+
|
|
42
47
|
@ReactProp(name = "borderRadius")
|
|
43
48
|
override fun setBorderRadius(view: ReactNativeBlurView?, borderRadius: Float) {
|
|
44
49
|
view?.setBorderRadius(borderRadius)
|
package/android/src/main/java/com/sbaiahmed1/reactnativeblur/ReactNativeProgressiveBlurView.kt
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package com.sbaiahmed1.reactnativeblur
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
+
import android.content.res.Configuration
|
|
4
5
|
import android.graphics.Canvas
|
|
5
6
|
import android.graphics.Color
|
|
6
7
|
import android.graphics.LinearGradient
|
|
@@ -30,7 +31,9 @@ class ReactNativeProgressiveBlurView : FrameLayout {
|
|
|
30
31
|
private val gradientPaint = Paint(Paint.ANTI_ALIAS_FLAG)
|
|
31
32
|
|
|
32
33
|
private var currentBlurRadius = DEFAULT_BLUR_RADIUS
|
|
34
|
+
private var currentBlurRounds = DEFAULT_BLUR_ROUNDS
|
|
33
35
|
private var currentOverlayColor = Color.TRANSPARENT
|
|
36
|
+
private var currentBlurType = "xlight"
|
|
34
37
|
private var currentDirection = "topToBottom"
|
|
35
38
|
private var currentStartOffset = 0.0f
|
|
36
39
|
private var hasExplicitBackground: Boolean = false
|
|
@@ -42,6 +45,7 @@ class ReactNativeProgressiveBlurView : FrameLayout {
|
|
|
42
45
|
private const val TAG = "ReactNativeProgressiveBlur"
|
|
43
46
|
private const val MAX_BLUR_RADIUS = 100f
|
|
44
47
|
private const val DEFAULT_BLUR_RADIUS = 10f
|
|
48
|
+
private const val DEFAULT_BLUR_ROUNDS = 5
|
|
45
49
|
private const val DEBUG = false
|
|
46
50
|
|
|
47
51
|
// Cross-platform blur amount constants
|
|
@@ -129,7 +133,7 @@ class ReactNativeProgressiveBlurView : FrameLayout {
|
|
|
129
133
|
blurView = BlurView(context, null).apply {
|
|
130
134
|
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
131
135
|
setDownsampleFactor(6.0F)
|
|
132
|
-
blurRounds =
|
|
136
|
+
blurRounds = currentBlurRounds
|
|
133
137
|
}
|
|
134
138
|
addView(blurView)
|
|
135
139
|
}
|
|
@@ -209,16 +213,18 @@ class ReactNativeProgressiveBlurView : FrameLayout {
|
|
|
209
213
|
/**
|
|
210
214
|
* Finds the optimal view to use as blur capture root.
|
|
211
215
|
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
216
|
+
* Priority:
|
|
217
|
+
* 1. Nearest react-native-screens Screen ancestor — scopes blur to the current
|
|
218
|
+
* screen and prevents capturing navigation transition artifacts.
|
|
219
|
+
* 2. Nearest ReactRootView ancestor — scopes blur to the React Native root when
|
|
220
|
+
* the component is not inside a Screen (e.g. plain View hierarchies). Without
|
|
221
|
+
* this fallback, QmBlurView defaults to the activity decor view and blurs the
|
|
222
|
+
* entire screen instead of just the component area (issue #89).
|
|
223
|
+
* 3. null — returned for modals, which intentionally need to blur content from
|
|
224
|
+
* the main activity window (decor view is correct there).
|
|
219
225
|
*/
|
|
220
226
|
private fun findOptimalBlurRoot(): ViewGroup? {
|
|
221
|
-
return findNearestScreenAncestor()
|
|
227
|
+
return findNearestScreenAncestor() ?: findNearestReactRootView()
|
|
222
228
|
}
|
|
223
229
|
|
|
224
230
|
/**
|
|
@@ -235,6 +241,22 @@ class ReactNativeProgressiveBlurView : FrameLayout {
|
|
|
235
241
|
return null
|
|
236
242
|
}
|
|
237
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Walks up the view hierarchy looking for the React Native root view.
|
|
246
|
+
* Used as a fallback when no Screen ancestor exists, to scope the blur
|
|
247
|
+
* capture to the RN root rather than the full activity decor view.
|
|
248
|
+
*/
|
|
249
|
+
private fun findNearestReactRootView(): ViewGroup? {
|
|
250
|
+
var currentParent = this.parent
|
|
251
|
+
while (currentParent != null) {
|
|
252
|
+
if (currentParent.javaClass.name == "com.facebook.react.ReactRootView") {
|
|
253
|
+
return currentParent as? ViewGroup
|
|
254
|
+
}
|
|
255
|
+
currentParent = currentParent.parent
|
|
256
|
+
}
|
|
257
|
+
return null
|
|
258
|
+
}
|
|
259
|
+
|
|
238
260
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
239
261
|
val width = MeasureSpec.getSize(widthMeasureSpec)
|
|
240
262
|
val height = MeasureSpec.getSize(heightMeasureSpec)
|
|
@@ -375,6 +397,17 @@ class ReactNativeProgressiveBlurView : FrameLayout {
|
|
|
375
397
|
cleanup()
|
|
376
398
|
}
|
|
377
399
|
|
|
400
|
+
/**
|
|
401
|
+
* Handle configuration changes, such as dark mode or orientation changes.
|
|
402
|
+
* This ensures the blur view updates its overlay color based on the new
|
|
403
|
+
* configuration.
|
|
404
|
+
*/
|
|
405
|
+
override fun onConfigurationChanged(newConfig: Configuration) {
|
|
406
|
+
super.onConfigurationChanged(newConfig)
|
|
407
|
+
|
|
408
|
+
setBlurType(currentBlurType)
|
|
409
|
+
}
|
|
410
|
+
|
|
378
411
|
/**
|
|
379
412
|
* Cleanup method to prevent memory leaks.
|
|
380
413
|
* Resets initialization state so blur is re-initialized on next attach.
|
|
@@ -429,6 +462,22 @@ class ReactNativeProgressiveBlurView : FrameLayout {
|
|
|
429
462
|
}
|
|
430
463
|
}
|
|
431
464
|
|
|
465
|
+
/**
|
|
466
|
+
* Set the number of blur rounds.
|
|
467
|
+
* @param rounds The number of blur rounds (1-15)
|
|
468
|
+
*/
|
|
469
|
+
fun setRounds(rounds: Int) {
|
|
470
|
+
val blurRounds = rounds.coerceIn(1, 15)
|
|
471
|
+
currentBlurRounds = blurRounds
|
|
472
|
+
logDebug("setRounds: $rounds -> $blurRounds")
|
|
473
|
+
|
|
474
|
+
try {
|
|
475
|
+
blurView?.blurRounds = blurRounds
|
|
476
|
+
} catch (e: Exception) {
|
|
477
|
+
logError("Failed to set blur rounds: ${e.message}", e)
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
432
481
|
/**
|
|
433
482
|
* Set the direction of the progressive blur gradient.
|
|
434
483
|
* @param direction The direction string: "blurredTopClearBottom" or "blurredBottomClearTop"
|
|
@@ -476,7 +525,8 @@ class ReactNativeProgressiveBlurView : FrameLayout {
|
|
|
476
525
|
* @param type The blur type string (case-insensitive)
|
|
477
526
|
*/
|
|
478
527
|
fun setBlurType(type: String) {
|
|
479
|
-
|
|
528
|
+
currentBlurType = type
|
|
529
|
+
val blurType = BlurType.fromString(type, resources.configuration)
|
|
480
530
|
currentOverlayColor = blurType.overlayColor
|
|
481
531
|
logDebug("setBlurType: $type -> ${blurType.name} -> ${Integer.toHexString(currentOverlayColor)}")
|
|
482
532
|
|
|
@@ -45,6 +45,11 @@ class ReactNativeProgressiveBlurViewManager : SimpleViewManager<ReactNativeProgr
|
|
|
45
45
|
view?.setBlurAmount(blurAmount.toFloat())
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
@ReactProp(name = "blurRounds")
|
|
49
|
+
override fun setBlurRounds(view: ReactNativeProgressiveBlurView?, blurRounds: Int) {
|
|
50
|
+
view?.setRounds(blurRounds)
|
|
51
|
+
}
|
|
52
|
+
|
|
48
53
|
@ReactProp(name = "direction")
|
|
49
54
|
override fun setDirection(view: ReactNativeProgressiveBlurView?, direction: String?) {
|
|
50
55
|
// Provide default value if direction is null or empty
|
|
@@ -21,41 +21,54 @@ class BlurEffectView: UIVisualEffectView {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
func updateBlur(style: UIBlurEffect.Style, intensity: Double) {
|
|
24
|
+
// Skip expensive animator recreation when nothing changed.
|
|
25
|
+
// During FlashList recycling, updateUIView fires on every layout pass
|
|
26
|
+
// even when props are identical, causing jank (issue #100).
|
|
27
|
+
guard style != self.blurStyle || intensity != self.intensity else { return }
|
|
24
28
|
self.blurStyle = style
|
|
25
29
|
self.intensity = intensity
|
|
26
30
|
setupBlur()
|
|
27
31
|
}
|
|
28
32
|
|
|
33
|
+
override func didMoveToWindow() {
|
|
34
|
+
super.didMoveToWindow()
|
|
35
|
+
guard window != nil else { return }
|
|
36
|
+
// UIKit resumes paused CAAnimations when a view re-joins a window
|
|
37
|
+
// (e.g. after modal dismiss + re-present). If the animation plays
|
|
38
|
+
// toward its end state the blur drifts to full intensity. Re-pause
|
|
39
|
+
// and re-set the fraction here to lock it back to our intended value.
|
|
40
|
+
// pausesOnCompletion = true (set in setupBlur) ensures the animator
|
|
41
|
+
// stays .active even if it reaches fraction 1.0, so this is always safe.
|
|
42
|
+
animator?.pauseAnimation()
|
|
43
|
+
animator?.fractionComplete = intensity
|
|
44
|
+
}
|
|
45
|
+
|
|
29
46
|
private func setupBlur() {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
animator.stopAnimation(true)
|
|
33
|
-
animator.finishAnimation(at: .current)
|
|
47
|
+
if let existing = animator, existing.state == .active {
|
|
48
|
+
existing.stopAnimation(true)
|
|
34
49
|
}
|
|
35
50
|
animator = nil
|
|
36
51
|
|
|
37
|
-
// Reset effect
|
|
38
52
|
effect = nil
|
|
39
53
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
animator?.addAnimations { [weak self] in
|
|
54
|
+
let newAnimator = UIViewPropertyAnimator(duration: 1, curve: .linear)
|
|
55
|
+
newAnimator.addAnimations { [weak self] in
|
|
43
56
|
self?.effect = UIBlurEffect(style: self?.blurStyle ?? .systemMaterial)
|
|
44
57
|
}
|
|
45
|
-
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
// pausesOnCompletion: if UIKit ever resumes and runs this to the end,
|
|
59
|
+
// the animator stays .active (paused at 1.0) instead of going .inactive.
|
|
60
|
+
// This guarantees didMoveToWindow can always call pauseAnimation() safely.
|
|
61
|
+
newAnimator.pausesOnCompletion = true
|
|
62
|
+
newAnimator.startAnimation()
|
|
63
|
+
newAnimator.pauseAnimation()
|
|
64
|
+
newAnimator.fractionComplete = intensity
|
|
65
|
+
animator = newAnimator
|
|
53
66
|
}
|
|
54
67
|
|
|
55
68
|
deinit {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
69
|
+
if let animator = animator, animator.state == .active {
|
|
70
|
+
animator.stopAnimation(true)
|
|
71
|
+
}
|
|
59
72
|
}
|
|
60
73
|
}
|
|
61
74
|
|
package/lib/module/BlurSwitch.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import { Platform, Switch } from 'react-native';
|
|
4
|
+
import { Platform, StyleSheet, Switch } from 'react-native';
|
|
5
5
|
import ReactNativeBlurSwitch from './ReactNativeBlurSwitchNativeComponent';
|
|
6
6
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
7
7
|
const toColorString = (color, fallback) => {
|
|
@@ -30,8 +30,9 @@ const toColorString = (color, fallback) => {
|
|
|
30
30
|
*/
|
|
31
31
|
export const BlurSwitch = ({
|
|
32
32
|
value = false,
|
|
33
|
-
onValueChange,
|
|
34
33
|
blurAmount = 10,
|
|
34
|
+
blurRounds = 5,
|
|
35
|
+
onValueChange,
|
|
35
36
|
thumbColor = '#FFFFFF',
|
|
36
37
|
trackColor = {
|
|
37
38
|
false: '#E5E5EA',
|
|
@@ -53,15 +54,13 @@ export const BlurSwitch = ({
|
|
|
53
54
|
});
|
|
54
55
|
}
|
|
55
56
|
return /*#__PURE__*/_jsx(ReactNativeBlurSwitch, {
|
|
56
|
-
style: [
|
|
57
|
-
width: 65,
|
|
58
|
-
height: 36
|
|
59
|
-
}, style],
|
|
57
|
+
style: [styles.switch, style],
|
|
60
58
|
value: value,
|
|
61
59
|
onValueChange: event => {
|
|
62
60
|
onValueChange?.(event.nativeEvent.value);
|
|
63
61
|
},
|
|
64
62
|
blurAmount: blurAmount,
|
|
63
|
+
blurRounds: blurRounds,
|
|
65
64
|
thumbColor: toColorString(thumbColor, '#FFFFFF'),
|
|
66
65
|
trackColorOff: toColorString(trackColor?.false, '#E5E5EA'),
|
|
67
66
|
trackColorOn: toColorString(trackColor?.true, '#34C759'),
|
|
@@ -69,5 +68,11 @@ export const BlurSwitch = ({
|
|
|
69
68
|
...props
|
|
70
69
|
});
|
|
71
70
|
};
|
|
71
|
+
const styles = StyleSheet.create({
|
|
72
|
+
switch: {
|
|
73
|
+
width: 65,
|
|
74
|
+
height: 36
|
|
75
|
+
}
|
|
76
|
+
});
|
|
72
77
|
export default BlurSwitch;
|
|
73
78
|
//# sourceMappingURL=BlurSwitch.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","Platform","Switch","ReactNativeBlurSwitch","jsx","_jsx","toColorString","color","fallback","BlurSwitch","value","
|
|
1
|
+
{"version":3,"names":["React","Platform","StyleSheet","Switch","ReactNativeBlurSwitch","jsx","_jsx","toColorString","color","fallback","BlurSwitch","value","blurAmount","blurRounds","onValueChange","thumbColor","trackColor","false","true","disabled","style","props","OS","styles","switch","event","nativeEvent","trackColorOff","trackColorOn","create","width","height"],"sourceRoot":"../../src","sources":["BlurSwitch.tsx"],"mappings":";;AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,SAASC,QAAQ,EAAEC,UAAU,EAAEC,MAAM,QAAQ,cAAc;AAE3D,OAAOC,qBAAqB,MAAM,wCAAwC;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAE3E,MAAMC,aAAa,GAAGA,CACpBC,KAA6B,EAC7BC,QAAgB,KACL;EACX,IAAI,OAAOD,KAAK,KAAK,QAAQ,EAAE,OAAOA,KAAK;EAC3C,OAAOC,QAAQ;AACjB,CAAC;AAyED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,UAAqC,GAAGA,CAAC;EACpDC,KAAK,GAAG,KAAK;EACbC,UAAU,GAAG,EAAE;EACfC,UAAU,GAAG,CAAC;EACdC,aAAa;EACbC,UAAU,GAAG,SAAS;EACtBC,UAAU,GAAG;IAAEC,KAAK,EAAE,SAAS;IAAEC,IAAI,EAAE;EAAU,CAAC;EAClDC,QAAQ,GAAG,KAAK;EAChBC,KAAK;EACL,GAAGC;AACL,CAAC,KAAK;EACJ,IAAIpB,QAAQ,CAACqB,EAAE,KAAK,KAAK,EAAE;IACzB,oBACEhB,IAAA,CAACH,MAAM;MACLQ,KAAK,EAAEA,KAAM;MACbG,aAAa,EAAEA,aAAc;MAC7BC,UAAU,EAAEA,UAAW;MACvBC,UAAU,EAAEA,UAAW;MACvBG,QAAQ,EAAEA,QAAS;MACnBC,KAAK,EAAEA,KAAM;MAAA,GACTC;IAAK,CACV,CAAC;EAEN;EAEA,oBACEf,IAAA,CAACF,qBAAqB;IACpBgB,KAAK,EAAE,CAACG,MAAM,CAACC,MAAM,EAAEJ,KAAK,CAAE;IAC9BT,KAAK,EAAEA,KAAM;IACbG,aAAa,EAAGW,KAAK,IAAK;MACxBX,aAAa,GAAGW,KAAK,CAACC,WAAW,CAACf,KAAK,CAAC;IAC1C,CAAE;IACFC,UAAU,EAAEA,UAAW;IACvBC,UAAU,EAAEA,UAAW;IACvBE,UAAU,EAAER,aAAa,CAACQ,UAAU,EAAE,SAAS,CAAE;IACjDY,aAAa,EAAEpB,aAAa,CAACS,UAAU,EAAEC,KAAK,EAAE,SAAS,CAAE;IAC3DW,YAAY,EAAErB,aAAa,CAACS,UAAU,EAAEE,IAAI,EAAE,SAAS,CAAE;IACzDC,QAAQ,EAAEA,QAAS;IAAA,GACfE;EAAK,CACV,CAAC;AAEN,CAAC;AAED,MAAME,MAAM,GAAGrB,UAAU,CAAC2B,MAAM,CAAC;EAC/BL,MAAM,EAAE;IACNM,KAAK,EAAE,EAAE;IACTC,MAAM,EAAE;EACV;AACF,CAAC,CAAC;AAEF,eAAerB,UAAU","ignoreList":[]}
|