@apple-pie/slice-native 0.1.6 → 0.1.7

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.
Files changed (124) hide show
  1. package/README.md +138 -261
  2. package/SliceNative.podspec +24 -0
  3. package/dist/cjs/components.js +4 -0
  4. package/dist/cjs/hooks.js +2 -0
  5. package/dist/cjs/index.js +9 -0
  6. package/dist/cjs/src/components/SliceBackgroundBlur/SliceBackgroundBlur.js +11 -0
  7. package/dist/cjs/src/components/SliceButton/SliceButton.js +50 -23
  8. package/dist/cjs/src/components/SliceLiquidGlassView/SliceLiquidGlassView.js +12 -0
  9. package/dist/cjs/src/components/SliceTextInput/SliceTextInput.js +39 -9
  10. package/dist/cjs/src/hooks/useDeviceCapabilities/useDeviceCapabilities.js +19 -0
  11. package/dist/cjs/src/utils/sizes/sizes.js +40 -0
  12. package/dist/cjs/utils.js +3 -0
  13. package/dist/esm/components.mjs +2 -0
  14. package/dist/esm/hooks.mjs +1 -0
  15. package/dist/esm/index.mjs +4 -0
  16. package/dist/esm/src/components/SliceBackgroundBlur/SliceBackgroundBlur.mjs +9 -0
  17. package/dist/esm/src/components/SliceButton/SliceButton.mjs +50 -23
  18. package/dist/esm/src/components/SliceLiquidGlassView/SliceLiquidGlassView.mjs +10 -0
  19. package/dist/esm/src/components/SliceTextInput/SliceTextInput.mjs +39 -9
  20. package/dist/esm/src/hooks/useDeviceCapabilities/useDeviceCapabilities.mjs +17 -0
  21. package/dist/esm/src/utils/sizes/sizes.mjs +37 -0
  22. package/dist/esm/utils.mjs +1 -0
  23. package/dist/react-native/native/src/components/SliceBackgroundBlur/SliceBackgroundBlur.js +6 -0
  24. package/dist/react-native/native/src/components/SliceBackgroundBlur/SliceBackgroundBlur.native.js +6 -0
  25. package/dist/react-native/native/src/components/SliceBackgroundBlur/_types.js +1 -0
  26. package/dist/react-native/native/src/components/SliceBackgroundBlur/index.js +1 -0
  27. package/dist/react-native/native/src/components/SliceButton/SliceButton.js +141 -0
  28. package/dist/react-native/native/src/components/SliceButton/_types.js +1 -0
  29. package/dist/react-native/native/src/components/SliceButton/index.js +1 -0
  30. package/dist/react-native/native/src/components/SliceIcon/SliceIcon.js +15 -0
  31. package/dist/react-native/native/src/components/SliceIcon/_types.js +1 -0
  32. package/dist/react-native/native/src/components/SliceIcon/iconRegistry.js +663 -0
  33. package/dist/react-native/native/src/components/SliceIcon/icons.js +140 -0
  34. package/dist/react-native/native/src/components/SliceIcon/index.js +2 -0
  35. package/dist/react-native/native/src/components/SliceLiquidGlassView/SliceLiquidGlassView.ios.js +6 -0
  36. package/dist/react-native/native/src/components/SliceLiquidGlassView/SliceLiquidGlassView.js +7 -0
  37. package/dist/react-native/native/src/components/SliceLiquidGlassView/_types.js +1 -0
  38. package/dist/react-native/native/src/components/SliceLiquidGlassView/index.js +1 -0
  39. package/dist/react-native/native/src/components/SliceText/SliceText.js +16 -0
  40. package/dist/react-native/native/src/components/SliceText/_types.js +1 -0
  41. package/dist/react-native/native/src/components/SliceText/index.js +1 -0
  42. package/dist/react-native/native/src/components/SliceTextInput/SliceTextInput.js +111 -0
  43. package/dist/react-native/native/src/components/SliceTextInput/_types.js +1 -0
  44. package/dist/react-native/native/src/components/SliceTextInput/index.js +1 -0
  45. package/dist/react-native/native/src/components/index.js +6 -0
  46. package/dist/react-native/native/src/hooks/index.js +2 -0
  47. package/dist/react-native/native/src/hooks/useCreateThemedStyles/index.js +1 -0
  48. package/dist/react-native/native/src/hooks/useCreateThemedStyles/useCreateThemedStyles.js +8 -0
  49. package/dist/react-native/native/src/hooks/useDeviceCapabilities/_types.js +1 -0
  50. package/dist/react-native/native/src/hooks/useDeviceCapabilities/index.js +1 -0
  51. package/dist/react-native/native/src/hooks/useDeviceCapabilities/useDeviceCapabilities.js +14 -0
  52. package/dist/react-native/native/src/hooks/useDeviceCapabilities/useDeviceCapabilities.native.js +23 -0
  53. package/dist/react-native/native/src/index.js +6 -0
  54. package/dist/react-native/native/src/providers/SliceThemeProvider.js +30 -0
  55. package/dist/react-native/native/src/providers/index.js +1 -0
  56. package/dist/react-native/native/src/specs/BackgroundBlurNativeComponent.ts +16 -0
  57. package/dist/react-native/native/src/specs/LiquidGlassNativeComponent.ts +18 -0
  58. package/dist/react-native/native/src/specs/NativeDeviceCapabilities.ts +13 -0
  59. package/dist/react-native/native/src/theme/index.js +69 -0
  60. package/dist/react-native/native/src/theme/motion.js +43 -0
  61. package/dist/react-native/native/src/types/index.js +1 -0
  62. package/dist/react-native/native/src/types/sharedTypes.js +1 -0
  63. package/dist/react-native/native/src/utils/index.js +2 -0
  64. package/dist/react-native/native/src/utils/sizes/index.js +1 -0
  65. package/dist/react-native/native/src/utils/sizes/sizes.js +35 -0
  66. package/dist/react-native/native/src/utils/typography/index.js +1 -0
  67. package/dist/react-native/native/src/utils/typography/typography.js +13 -0
  68. package/dist/react-native/src/theme/colors/colors.js +385 -0
  69. package/dist/react-native/src/theme/colors/types.js +1 -0
  70. package/dist/react-native/src/theme/corners/corners.js +10 -0
  71. package/dist/react-native/src/theme/corners/types.js +1 -0
  72. package/dist/react-native/src/theme/elevations/_types.js +1 -0
  73. package/dist/react-native/src/theme/elevations/elevations.js +13 -0
  74. package/dist/react-native/src/theme/motion/_types.js +1 -0
  75. package/dist/react-native/src/theme/motion/motion.js +11 -0
  76. package/dist/react-native/src/theme/spacing/_types.js +1 -0
  77. package/dist/react-native/src/theme/spacing/spacing.js +10 -0
  78. package/dist/react-native/src/theme/type/type.js +371 -0
  79. package/dist/react-native/src/theme/type/types.js +1 -0
  80. package/dist/types/native/src/components/SliceBackgroundBlur/SliceBackgroundBlur.d.ts +3 -0
  81. package/dist/types/native/src/components/SliceBackgroundBlur/SliceBackgroundBlur.native.d.ts +2 -0
  82. package/dist/types/native/src/components/SliceBackgroundBlur/_types.d.ts +24 -0
  83. package/dist/types/native/src/components/SliceBackgroundBlur/index.d.ts +2 -0
  84. package/dist/types/native/src/components/SliceButton/_types.d.ts +33 -2
  85. package/dist/types/native/src/components/SliceLiquidGlassView/SliceLiquidGlassView.d.ts +3 -0
  86. package/dist/types/native/src/components/SliceLiquidGlassView/SliceLiquidGlassView.ios.d.ts +2 -0
  87. package/dist/types/native/src/components/SliceLiquidGlassView/_types.d.ts +17 -0
  88. package/dist/types/native/src/components/SliceLiquidGlassView/index.d.ts +2 -0
  89. package/dist/types/native/src/components/SliceTextInput/_types.d.ts +23 -1
  90. package/dist/types/native/src/components/index.d.ts +4 -0
  91. package/dist/types/native/src/hooks/index.d.ts +2 -0
  92. package/dist/types/native/src/hooks/useDeviceCapabilities/_types.d.ts +17 -0
  93. package/dist/types/native/src/hooks/useDeviceCapabilities/index.d.ts +2 -0
  94. package/dist/types/native/src/hooks/useDeviceCapabilities/useDeviceCapabilities.d.ts +3 -0
  95. package/dist/types/native/src/hooks/useDeviceCapabilities/useDeviceCapabilities.native.d.ts +7 -0
  96. package/dist/types/native/src/index.d.ts +12 -0
  97. package/dist/types/native/src/specs/BackgroundBlurNativeComponent.d.ts +9 -0
  98. package/dist/types/native/src/specs/LiquidGlassNativeComponent.d.ts +9 -0
  99. package/dist/types/native/src/specs/NativeDeviceCapabilities.d.ts +11 -0
  100. package/dist/types/native/src/types/index.d.ts +1 -0
  101. package/dist/types/native/src/types/sharedTypes.d.ts +14 -0
  102. package/dist/types/native/src/utils/index.d.ts +1 -0
  103. package/dist/types/native/src/utils/sizes/index.d.ts +1 -0
  104. package/dist/types/native/src/utils/sizes/sizes.d.ts +7 -0
  105. package/package.json +30 -13
  106. package/platform/android/build.gradle +22 -0
  107. package/platform/android/src/main/AndroidManifest.xml +1 -0
  108. package/platform/android/src/main/java/com/slice/SliceBackgroundBlurManager.java +28 -0
  109. package/platform/android/src/main/java/com/slice/SliceBackgroundBlurView.java +98 -0
  110. package/platform/android/src/main/java/com/slice/SliceDeviceCapabilitiesModule.java +25 -0
  111. package/platform/android/src/main/java/com/slice/SlicePackage.java +25 -0
  112. package/platform/ios/BackgroundBlurSurface.swift +89 -0
  113. package/platform/ios/LiquidGlassSurface.swift +82 -0
  114. package/platform/ios/RCTBackgroundBlurView.h +3 -0
  115. package/platform/ios/RCTBackgroundBlurView.mm +42 -0
  116. package/platform/ios/RCTLiquidGlassView.h +8 -0
  117. package/platform/ios/RCTLiquidGlassView.mm +73 -0
  118. package/platform/ios/SliceDeviceCapabilities.h +4 -0
  119. package/platform/ios/SliceDeviceCapabilities.mm +35 -0
  120. package/react-native.config.js +1 -0
  121. package/src/specs/BackgroundBlurNativeComponent.ts +16 -0
  122. package/src/specs/LiquidGlassNativeComponent.ts +18 -0
  123. package/src/specs/NativeDeviceCapabilities.ts +13 -0
  124. package/dist/types/native/index.d.ts +0 -8
package/package.json CHANGED
@@ -1,54 +1,54 @@
1
1
  {
2
2
  "name": "@apple-pie/slice-native",
3
- "version": "0.1.6",
4
- "description": "React Native icons and design tokens for Slice",
3
+ "version": "0.1.7",
4
+ "description": "React Native components, icons and design tokens for Slice",
5
5
  "author": "Uris Dacosta",
6
6
  "license": "ISC",
7
7
  "type": "commonjs",
8
8
  "main": "./dist/cjs/index.js",
9
9
  "module": "./dist/esm/index.mjs",
10
- "react-native": "./dist/esm/index.mjs",
11
- "types": "./dist/types/native/index.d.ts",
10
+ "react-native": "./dist/react-native/native/src/index.js",
11
+ "types": "./dist/types/native/src/index.d.ts",
12
12
  "exports": {
13
13
  ".": {
14
- "types": "./dist/types/native/index.d.ts",
15
- "react-native": "./dist/esm/index.mjs",
14
+ "types": "./dist/types/native/src/index.d.ts",
15
+ "react-native": "./dist/react-native/native/src/index.js",
16
16
  "import": "./dist/esm/index.mjs",
17
17
  "require": "./dist/cjs/index.js"
18
18
  },
19
19
  "./package.json": "./package.json",
20
20
  "./theme": {
21
21
  "types": "./dist/types/native/src/theme/index.d.ts",
22
- "react-native": "./dist/esm/theme.mjs",
22
+ "react-native": "./dist/react-native/native/src/theme/index.js",
23
23
  "import": "./dist/esm/theme.mjs",
24
24
  "require": "./dist/cjs/theme.js"
25
25
  },
26
26
  "./components": {
27
27
  "types": "./dist/types/native/src/components/index.d.ts",
28
- "react-native": "./dist/esm/components.mjs",
28
+ "react-native": "./dist/react-native/native/src/components/index.js",
29
29
  "import": "./dist/esm/components.mjs",
30
30
  "require": "./dist/cjs/components.js"
31
31
  },
32
32
  "./hooks": {
33
33
  "types": "./dist/types/native/src/hooks/index.d.ts",
34
- "react-native": "./dist/esm/hooks.mjs",
34
+ "react-native": "./dist/react-native/native/src/hooks/index.js",
35
35
  "import": "./dist/esm/hooks.mjs",
36
36
  "require": "./dist/cjs/hooks.js"
37
37
  },
38
38
  "./providers": {
39
39
  "types": "./dist/types/native/src/providers/index.d.ts",
40
- "react-native": "./dist/esm/providers.mjs",
40
+ "react-native": "./dist/react-native/native/src/providers/index.js",
41
41
  "import": "./dist/esm/providers.mjs",
42
42
  "require": "./dist/cjs/providers.js"
43
43
  },
44
44
  "./utils": {
45
45
  "types": "./dist/types/native/src/utils/index.d.ts",
46
- "react-native": "./dist/esm/utils.mjs",
46
+ "react-native": "./dist/react-native/native/src/utils/index.js",
47
47
  "import": "./dist/esm/utils.mjs",
48
48
  "require": "./dist/cjs/utils.js"
49
49
  }
50
50
  },
51
- "files": ["dist"],
51
+ "files": ["dist", "platform", "src/specs", "SliceNative.podspec", "react-native.config.js"],
52
52
  "sideEffects": false,
53
53
  "scripts": {
54
54
  "test": "vitest run --config vitest.config.mts",
@@ -60,7 +60,7 @@
60
60
  "pack:tarball": "node scripts/pack.mjs",
61
61
  "pack": "node scripts/pack.mjs",
62
62
  "prepack": "npm run build",
63
- "generate": "node scripts/generate-icons.mjs",
63
+ "generate": "node scripts/generate-icons.mjs && biome format --write src/components/SliceIcon/iconRegistry.tsx src/components/SliceIcon/icons.ts",
64
64
  "typecheck": "tsc --noEmit -p tsconfig.json"
65
65
  },
66
66
  "publishConfig": {
@@ -77,5 +77,22 @@
77
77
  "react-native": ">=0.80.0 <1.0.0",
78
78
  "react-native-svg": "^15.0.0",
79
79
  "react-native-reanimated": "^4.0.0"
80
+ },
81
+ "codegenConfig": {
82
+ "name": "SliceNativeSpec",
83
+ "type": "all",
84
+ "jsSrcsDir": "src/specs",
85
+ "ios": {
86
+ "componentProvider": {
87
+ "SliceLiquidGlassView": "RCTLiquidGlassView",
88
+ "SliceBackgroundBlur": "RCTBackgroundBlurView"
89
+ },
90
+ "modulesProvider": {
91
+ "SliceDeviceCapabilities": "SliceDeviceCapabilities"
92
+ }
93
+ },
94
+ "android": {
95
+ "javaPackageName": "com.slice"
96
+ }
80
97
  }
81
98
  }
@@ -0,0 +1,22 @@
1
+ buildscript {
2
+ repositories { google(); mavenCentral() }
3
+ dependencies { classpath 'com.android.tools.build:gradle:8.7.3' }
4
+ }
5
+ apply plugin: 'com.android.library'
6
+ apply plugin: 'com.facebook.react'
7
+
8
+ react {
9
+ jsRootDir = file('../../src')
10
+ libraryName = 'SliceNativeSpec'
11
+ codegenJavaPackageName = 'com.slice'
12
+ }
13
+ android {
14
+ namespace 'com.slice'
15
+ compileSdkVersion rootProject.ext.has('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 35
16
+ defaultConfig {
17
+ minSdkVersion 24
18
+ targetSdkVersion rootProject.ext.has('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 35
19
+ }
20
+ }
21
+ repositories { google(); mavenCentral() }
22
+ dependencies { implementation 'com.facebook.react:react-android' }
@@ -0,0 +1 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" />
@@ -0,0 +1,28 @@
1
+ package com.slice;
2
+
3
+ import androidx.annotation.Nullable;
4
+ import com.facebook.react.uimanager.SimpleViewManager;
5
+ import com.facebook.react.uimanager.ThemedReactContext;
6
+ import com.facebook.react.uimanager.ViewManagerDelegate;
7
+ import com.facebook.react.uimanager.annotations.ReactProp;
8
+ import com.facebook.react.viewmanagers.SliceBackgroundBlurManagerDelegate;
9
+ import com.facebook.react.viewmanagers.SliceBackgroundBlurManagerInterface;
10
+
11
+ public final class SliceBackgroundBlurManager extends SimpleViewManager<SliceBackgroundBlurView>
12
+ implements SliceBackgroundBlurManagerInterface<SliceBackgroundBlurView> {
13
+ private final ViewManagerDelegate<SliceBackgroundBlurView> delegate = new SliceBackgroundBlurManagerDelegate<>(this);
14
+ @Override protected ViewManagerDelegate<SliceBackgroundBlurView> getDelegate() { return delegate; }
15
+ @Override public String getName() { return "SliceBackgroundBlur"; }
16
+ @Override protected SliceBackgroundBlurView createViewInstance(ThemedReactContext context) {
17
+ return new SliceBackgroundBlurView(context);
18
+ }
19
+ @Override @ReactProp(name = "blurIntensity", defaultDouble = 12)
20
+ public void setBlurIntensity(SliceBackgroundBlurView view, double value) { view.setIntensity(value); }
21
+ @Override @ReactProp(name = "fallbackColor", customType = "Color")
22
+ public void setFallbackColor(SliceBackgroundBlurView view, @Nullable Integer value) { view.setFallbackColor(value); }
23
+ // Directional fading is intentionally iOS-only.
24
+ @Override @ReactProp(name = "fadeStop", defaultDouble = 0)
25
+ public void setFadeStop(SliceBackgroundBlurView view, double value) {}
26
+ @Override @ReactProp(name = "direction")
27
+ public void setDirection(SliceBackgroundBlurView view, @Nullable String value) {}
28
+ }
@@ -0,0 +1,98 @@
1
+ package com.slice;
2
+
3
+ import android.content.Context;
4
+ import android.graphics.Canvas;
5
+ import android.graphics.Color;
6
+ import android.graphics.RenderEffect;
7
+ import android.graphics.RenderNode;
8
+ import android.graphics.Shader;
9
+ import android.os.Build;
10
+ import android.view.View;
11
+ import java.lang.reflect.Method;
12
+
13
+ /** Uniform, same-window backdrop blur. Older platforms draw only the fallback. */
14
+ public final class SliceBackgroundBlurView extends View {
15
+ private double intensity = 12;
16
+ private int fallbackColor = Color.argb(46, 128, 128, 128);
17
+ private BackdropRenderer renderer;
18
+
19
+ public SliceBackgroundBlurView(Context context) {
20
+ super(context);
21
+ setWillNotDraw(false);
22
+ // Resolve a public API at runtime to avoid requiring SDK 37.2 to build consumers.
23
+ // SDK_INT alone cannot distinguish Android's minor API releases.
24
+ if (supportsNativeBlur()) {
25
+ try {
26
+ renderer = new BackdropRenderer();
27
+ } catch (ReflectiveOperationException | LinkageError ignored) {
28
+ renderer = null;
29
+ }
30
+ }
31
+ }
32
+
33
+ static boolean supportsNativeBlur() {
34
+ if (Build.VERSION.SDK_INT < 37) return false;
35
+ try {
36
+ RenderNode.class.getMethod("setBackdropRenderEffect", RenderEffect.class);
37
+ return true;
38
+ } catch (ReflectiveOperationException | RuntimeException | LinkageError ignored) {
39
+ return false;
40
+ }
41
+ }
42
+
43
+ public void setIntensity(double value) {
44
+ intensity = Double.isFinite(value) ? Math.max(0, Math.min(100, value)) : 12;
45
+ invalidate();
46
+ }
47
+
48
+ public void setFallbackColor(Integer value) {
49
+ fallbackColor = value == null ? Color.argb(46, 128, 128, 128) : value;
50
+ invalidate();
51
+ }
52
+
53
+ @Override protected void onDraw(Canvas canvas) {
54
+ super.onDraw(canvas);
55
+ if (intensity == 0) return;
56
+ if (renderer != null && canvas.isHardwareAccelerated() && isHardwareAccelerated()) {
57
+ try {
58
+ float radius = (float) intensity * getResources().getDisplayMetrics().density;
59
+ renderer.draw(canvas, getWidth(), getHeight(), radius);
60
+ return;
61
+ } catch (ReflectiveOperationException | RuntimeException | LinkageError ignored) {
62
+ renderer = null;
63
+ }
64
+ }
65
+ canvas.drawColor(fallbackColor);
66
+ }
67
+
68
+ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
69
+ super.onSizeChanged(w, h, oldw, oldh);
70
+ if (renderer != null) renderer.node.discardDisplayList();
71
+ }
72
+
73
+ @Override protected void onDetachedFromWindow() {
74
+ if (renderer != null) renderer.node.discardDisplayList();
75
+ super.onDetachedFromWindow();
76
+ }
77
+
78
+ // Keep newer graphics classes out of the View's fields on older Android releases.
79
+ private static final class BackdropRenderer {
80
+ final RenderNode node = new RenderNode("SliceBackgroundBlur");
81
+ final Method setBackdropEffect;
82
+
83
+ BackdropRenderer() throws ReflectiveOperationException {
84
+ setBackdropEffect = RenderNode.class.getMethod("setBackdropRenderEffect", RenderEffect.class);
85
+ node.setClipToBounds(true);
86
+ }
87
+
88
+ void draw(Canvas canvas, int width, int height, float radius) throws ReflectiveOperationException {
89
+ setBackdropEffect.invoke(node, RenderEffect.createBlurEffect(radius, radius, Shader.TileMode.CLAMP));
90
+ node.setPosition(0, 0, width, height);
91
+ if (!node.hasDisplayList()) {
92
+ node.beginRecording(width, height);
93
+ node.endRecording();
94
+ }
95
+ canvas.drawRenderNode(node);
96
+ }
97
+ }
98
+ }
@@ -0,0 +1,25 @@
1
+ package com.slice;
2
+
3
+ import android.os.Build;
4
+ import com.facebook.react.bridge.ReactApplicationContext;
5
+ import com.facebook.react.module.annotations.ReactModule;
6
+ import java.util.HashMap;
7
+ import java.util.Map;
8
+
9
+ @ReactModule(name = SliceDeviceCapabilitiesModule.NAME)
10
+ public final class SliceDeviceCapabilitiesModule extends NativeDeviceCapabilitiesSpec {
11
+ public static final String NAME = "SliceDeviceCapabilities";
12
+
13
+ public SliceDeviceCapabilitiesModule(ReactApplicationContext context) { super(context); }
14
+
15
+ @Override public String getName() { return NAME; }
16
+
17
+ @Override protected Map<String, Object> getTypedExportedConstants() {
18
+ Map<String, Object> result = new HashMap<>();
19
+ result.put("supportsLiquidGlass", false);
20
+ result.put("supportsNativeBlur", SliceBackgroundBlurView.supportsNativeBlur());
21
+ result.put("deviceName", Build.MODEL);
22
+ result.put("deviceModel", Build.MODEL);
23
+ return result;
24
+ }
25
+ }
@@ -0,0 +1,25 @@
1
+ package com.slice;
2
+
3
+ import androidx.annotation.Nullable;
4
+ import com.facebook.react.BaseReactPackage;
5
+ import com.facebook.react.bridge.NativeModule;
6
+ import com.facebook.react.bridge.ReactApplicationContext;
7
+ import com.facebook.react.module.model.ReactModuleInfo;
8
+ import com.facebook.react.module.model.ReactModuleInfoProvider;
9
+ import com.facebook.react.uimanager.ViewManager;
10
+ import java.util.Collections;
11
+ import java.util.List;
12
+
13
+ public final class SlicePackage extends BaseReactPackage {
14
+ @Override @Nullable public NativeModule getModule(String name, ReactApplicationContext context) {
15
+ return SliceDeviceCapabilitiesModule.NAME.equals(name) ? new SliceDeviceCapabilitiesModule(context) : null;
16
+ }
17
+ @Override public ReactModuleInfoProvider getReactModuleInfoProvider() {
18
+ return () -> Collections.singletonMap(SliceDeviceCapabilitiesModule.NAME, new ReactModuleInfo(
19
+ SliceDeviceCapabilitiesModule.NAME, SliceDeviceCapabilitiesModule.NAME,
20
+ false, false, false, true));
21
+ }
22
+ @Override public List<ViewManager> createViewManagers(ReactApplicationContext context) {
23
+ return Collections.singletonList(new SliceBackgroundBlurManager());
24
+ }
25
+ }
@@ -0,0 +1,89 @@
1
+ import UIKit
2
+
3
+ @objc(SliceBackgroundBlurSurface)
4
+ public final class BackgroundBlurSurface: UIView {
5
+ private let effectView = UIVisualEffectView(effect: nil)
6
+ private var animator: UIViewPropertyAnimator?
7
+ private var intensity: Double = 12
8
+ private var fadeStop: Double = 0
9
+ private var direction = "down"
10
+ private var fallbackColor: UIColor = .secondarySystemBackground
11
+ private var observer: NSObjectProtocol?
12
+
13
+ public override init(frame: CGRect) {
14
+ super.init(frame: frame)
15
+ isUserInteractionEnabled = false
16
+ addSubview(effectView)
17
+ observer = NotificationCenter.default.addObserver(
18
+ forName: UIAccessibility.reduceTransparencyStatusDidChangeNotification,
19
+ object: nil, queue: .main
20
+ ) { [weak self] _ in
21
+ MainActor.assumeIsolated { self?.updateEffect() }
22
+ }
23
+ }
24
+
25
+ public required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
26
+
27
+ deinit {
28
+ animator?.stopAnimation(true)
29
+ if let observer { NotificationCenter.default.removeObserver(observer) }
30
+ }
31
+
32
+ @objc public func configure(intensity: Double, fadeStop: Double, direction: String, fallbackColor: UIColor?) {
33
+ self.intensity = intensity.isFinite ? min(100, max(0, intensity)) : 12
34
+ self.fadeStop = fadeStop.isFinite ? min(1, max(0, fadeStop)) : 0
35
+ self.direction = direction
36
+ self.fallbackColor = fallbackColor ?? .secondarySystemBackground
37
+ updateEffect()
38
+ setNeedsLayout()
39
+ }
40
+
41
+ public override func didMoveToWindow() {
42
+ super.didMoveToWindow()
43
+ updateEffect()
44
+ }
45
+
46
+ private func updateEffect() {
47
+ setNeedsLayout()
48
+ animator?.stopAnimation(true)
49
+ animator = nil
50
+ effectView.effect = nil
51
+ effectView.backgroundColor = .clear
52
+ if UIAccessibility.isReduceTransparencyEnabled {
53
+ effectView.backgroundColor = fallbackColor
54
+ } else if intensity > 0, window != nil {
55
+ let animator = UIViewPropertyAnimator(duration: 1, curve: .linear) { [weak self] in
56
+ self?.effectView.effect = UIBlurEffect(style: .regular)
57
+ }
58
+ animator.pausesOnCompletion = true
59
+ animator.startAnimation()
60
+ animator.pauseAnimation()
61
+ animator.fractionComplete = CGFloat(intensity / 100)
62
+ self.animator = animator
63
+ }
64
+ }
65
+
66
+ public override func layoutSubviews() {
67
+ super.layoutSubviews()
68
+ effectView.frame = bounds
69
+ guard fadeStop > 0, !UIAccessibility.isReduceTransparencyEnabled else {
70
+ effectView.mask = nil
71
+ return
72
+ }
73
+ // Mask the effect view directly, never its parent. Reassign after resizing
74
+ // so UIKit refreshes its internal copies of the mask.
75
+ let mask = UIView(frame: effectView.bounds)
76
+ let gradient = CAGradientLayer()
77
+ gradient.frame = mask.bounds
78
+ gradient.colors = [UIColor.clear.cgColor, UIColor.black.cgColor, UIColor.black.cgColor]
79
+ gradient.locations = [0, NSNumber(value: fadeStop), 1]
80
+ switch direction {
81
+ case "up": gradient.startPoint = CGPoint(x: 0.5, y: 1); gradient.endPoint = CGPoint(x: 0.5, y: 0)
82
+ case "left": gradient.startPoint = CGPoint(x: 1, y: 0.5); gradient.endPoint = CGPoint(x: 0, y: 0.5)
83
+ case "right": gradient.startPoint = CGPoint(x: 0, y: 0.5); gradient.endPoint = CGPoint(x: 1, y: 0.5)
84
+ default: gradient.startPoint = CGPoint(x: 0.5, y: 0); gradient.endPoint = CGPoint(x: 0.5, y: 1)
85
+ }
86
+ mask.layer.addSublayer(gradient)
87
+ effectView.mask = mask
88
+ }
89
+ }
@@ -0,0 +1,82 @@
1
+ import UIKit
2
+
3
+ /// UIKit surface owned by the Fabric component. React children live in contentView.
4
+ @objc(SliceLiquidGlassSurface)
5
+ public final class LiquidGlassSurface: UIView {
6
+ private let effectView = UIVisualEffectView(effect: nil)
7
+ private var usesClearGlass = false
8
+ private var glassTint: UIColor?
9
+ private var interactive = false
10
+ private var radius: CGFloat = 24
11
+ private var reduceTransparencyObserver: NSObjectProtocol?
12
+
13
+ @objc public var reactContentView: UIView { effectView.contentView }
14
+
15
+ public override init(frame: CGRect) {
16
+ super.init(frame: frame)
17
+ backgroundColor = .clear
18
+ addSubview(effectView)
19
+ effectView.frame = bounds
20
+ effectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
21
+ reduceTransparencyObserver = NotificationCenter.default.addObserver(
22
+ forName: UIAccessibility.reduceTransparencyStatusDidChangeNotification,
23
+ object: nil,
24
+ queue: .main
25
+ ) { [weak self] _ in
26
+ MainActor.assumeIsolated { self?.updateEffect() }
27
+ }
28
+ updateEffect()
29
+ }
30
+
31
+ public required init?(coder: NSCoder) {
32
+ fatalError("init(coder:) has not been implemented")
33
+ }
34
+
35
+ deinit {
36
+ if let observer = reduceTransparencyObserver {
37
+ NotificationCenter.default.removeObserver(observer)
38
+ }
39
+ }
40
+
41
+ @objc public func configure(
42
+ clear: Bool,
43
+ tint: UIColor?,
44
+ interactive: Bool,
45
+ cornerRadius: CGFloat
46
+ ) {
47
+ let nextRadius = cornerRadius.isFinite ? max(0, cornerRadius) : 24
48
+ guard usesClearGlass != clear || glassTint != tint || self.interactive != interactive || radius != nextRadius else {
49
+ return
50
+ }
51
+ usesClearGlass = clear
52
+ glassTint = tint
53
+ self.interactive = interactive
54
+ radius = nextRadius
55
+ updateEffect()
56
+ }
57
+
58
+ private func updateEffect() {
59
+ if UIAccessibility.isReduceTransparencyEnabled {
60
+ effectView.effect = nil
61
+ effectView.backgroundColor = .secondarySystemBackground
62
+ } else {
63
+ effectView.backgroundColor = .clear
64
+ if #available(iOS 26.0, *) {
65
+ let effect = UIGlassEffect(style: usesClearGlass ? .clear : .regular)
66
+ effect.tintColor = glassTint
67
+ effect.isInteractive = interactive
68
+ effectView.effect = effect
69
+ } else {
70
+ effectView.effect = UIBlurEffect(style: .systemUltraThinMaterial)
71
+ }
72
+ }
73
+
74
+ if #available(iOS 26.0, *) {
75
+ effectView.cornerConfiguration = .corners(radius: .fixed(radius))
76
+ } else {
77
+ effectView.layer.cornerRadius = radius
78
+ effectView.layer.cornerCurve = .continuous
79
+ }
80
+ effectView.clipsToBounds = true
81
+ }
82
+ }
@@ -0,0 +1,3 @@
1
+ #import <React/RCTViewComponentView.h>
2
+ @interface RCTBackgroundBlurView : RCTViewComponentView
3
+ @end
@@ -0,0 +1,42 @@
1
+ #import "RCTBackgroundBlurView.h"
2
+ #import "SliceNative-Swift.h"
3
+ #import <React/RCTConversions.h>
4
+ #import <react/renderer/components/SliceNativeSpec/ComponentDescriptors.h>
5
+ #import <react/renderer/components/SliceNativeSpec/Props.h>
6
+ #import <react/renderer/components/SliceNativeSpec/RCTComponentViewHelpers.h>
7
+
8
+ using namespace facebook::react;
9
+ @interface RCTBackgroundBlurView () <RCTSliceBackgroundBlurViewProtocol>
10
+ @end
11
+ @implementation RCTBackgroundBlurView {
12
+ SliceBackgroundBlurSurface *_surface;
13
+ }
14
+ + (ComponentDescriptorProvider)componentDescriptorProvider {
15
+ return concreteComponentDescriptorProvider<SliceBackgroundBlurComponentDescriptor>();
16
+ }
17
+ - (instancetype)initWithFrame:(CGRect)frame {
18
+ if (self = [super initWithFrame:frame]) {
19
+ _props = std::make_shared<const SliceBackgroundBlurProps>();
20
+ _surface = [[SliceBackgroundBlurSurface alloc] initWithFrame:self.bounds];
21
+ self.contentView = _surface;
22
+ }
23
+ return self;
24
+ }
25
+ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps {
26
+ const auto &p = *std::static_pointer_cast<const SliceBackgroundBlurProps>(props);
27
+ NSString *direction = @"down";
28
+ switch (p.direction) {
29
+ case SliceBackgroundBlurDirection::Up: direction = @"up"; break;
30
+ case SliceBackgroundBlurDirection::Left: direction = @"left"; break;
31
+ case SliceBackgroundBlurDirection::Right: direction = @"right"; break;
32
+ default: break;
33
+ }
34
+ [_surface configureWithIntensity:p.blurIntensity fadeStop:p.fadeStop direction:direction fallbackColor:RCTUIColorFromSharedColor(p.fallbackColor)];
35
+ [super updateProps:props oldProps:oldProps];
36
+ }
37
+ - (void)prepareForRecycle {
38
+ [super prepareForRecycle];
39
+ [_surface configureWithIntensity:12 fadeStop:0 direction:@"down" fallbackColor:nil];
40
+ }
41
+ @end
42
+ Class<RCTComponentViewProtocol> SliceBackgroundBlurCls(void) { return RCTBackgroundBlurView.class; }
@@ -0,0 +1,8 @@
1
+ #import <React/RCTViewComponentView.h>
2
+
3
+ NS_ASSUME_NONNULL_BEGIN
4
+
5
+ @interface RCTLiquidGlassView : RCTViewComponentView
6
+ @end
7
+
8
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,73 @@
1
+ #import "RCTLiquidGlassView.h"
2
+ #import "SliceNative-Swift.h"
3
+ #import <React/RCTConversions.h>
4
+
5
+ #import <react/renderer/components/SliceNativeSpec/ComponentDescriptors.h>
6
+ #import <react/renderer/components/SliceNativeSpec/Props.h>
7
+ #import <react/renderer/components/SliceNativeSpec/RCTComponentViewHelpers.h>
8
+ #import <react/renderer/graphics/Color.h>
9
+
10
+ using namespace facebook::react;
11
+
12
+ @interface RCTLiquidGlassView () <RCTSliceLiquidGlassViewViewProtocol>
13
+ @end
14
+
15
+ @implementation RCTLiquidGlassView {
16
+ SliceLiquidGlassSurface *_surface;
17
+ }
18
+
19
+ + (ComponentDescriptorProvider)componentDescriptorProvider
20
+ {
21
+ return concreteComponentDescriptorProvider<SliceLiquidGlassViewComponentDescriptor>();
22
+ }
23
+
24
+ - (instancetype)initWithFrame:(CGRect)frame
25
+ {
26
+ if (self = [super initWithFrame:frame]) {
27
+ _props = std::make_shared<const SliceLiquidGlassViewProps>();
28
+ _surface = [[SliceLiquidGlassSurface alloc] initWithFrame:self.bounds];
29
+ [self addSubview:_surface];
30
+ }
31
+ return self;
32
+ }
33
+
34
+ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
35
+ {
36
+ const auto &newProps = *std::static_pointer_cast<const SliceLiquidGlassViewProps>(props);
37
+ [_surface configureWithClear:newProps.glassStyle == SliceLiquidGlassViewGlassStyle::Clear
38
+ tint:RCTUIColorFromSharedColor(newProps.glassTintColor)
39
+ interactive:newProps.interactive
40
+ cornerRadius:newProps.cornerRadius];
41
+ [super updateProps:props oldProps:oldProps];
42
+ }
43
+
44
+ - (void)layoutSubviews
45
+ {
46
+ [super layoutSubviews];
47
+ // Fill the entire view, including Yoga padding. Child frames already include padding.
48
+ _surface.frame = self.bounds;
49
+ }
50
+
51
+ - (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
52
+ {
53
+ [_surface.reactContentView insertSubview:childComponentView atIndex:index];
54
+ }
55
+
56
+ - (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
57
+ {
58
+ [childComponentView removeFromSuperview];
59
+ }
60
+
61
+ - (void)prepareForRecycle
62
+ {
63
+ [super prepareForRecycle];
64
+ [_surface configureWithClear:NO tint:nil interactive:NO cornerRadius:24];
65
+ }
66
+
67
+ @end
68
+
69
+ // Also supports component discovery in React Native versions using the class provider.
70
+ Class<RCTComponentViewProtocol> SliceLiquidGlassViewCls(void)
71
+ {
72
+ return RCTLiquidGlassView.class;
73
+ }
@@ -0,0 +1,4 @@
1
+ #import <SliceNativeSpec/SliceNativeSpec.h>
2
+
3
+ @interface SliceDeviceCapabilities : NSObject <NativeDeviceCapabilitiesSpec>
4
+ @end
@@ -0,0 +1,35 @@
1
+ #import "SliceDeviceCapabilities.h"
2
+ #import <UIKit/UIKit.h>
3
+ #import <sys/utsname.h>
4
+ #import <cstdlib>
5
+
6
+ @implementation SliceDeviceCapabilities
7
+ RCT_EXPORT_MODULE(SliceDeviceCapabilities)
8
+
9
+ + (BOOL)requiresMainQueueSetup { return NO; }
10
+
11
+ - (facebook::react::ModuleConstants<JS::NativeDeviceCapabilities::Constants>)constantsToExport {
12
+ return [self getConstants];
13
+ }
14
+
15
+ - (facebook::react::ModuleConstants<JS::NativeDeviceCapabilities::Constants>)getConstants {
16
+ struct utsname systemInfo;
17
+ NSString *model = uname(&systemInfo) == 0 ? [NSString stringWithUTF8String:systemInfo.machine] : @"";
18
+ // Simulator uname reports the host architecture; use the simulated device instead.
19
+ const char *simulatorModel = getenv("SIMULATOR_MODEL_IDENTIFIER");
20
+ if (simulatorModel != nullptr) { model = [NSString stringWithUTF8String:simulatorModel]; }
21
+ BOOL supportsGlass = NO;
22
+ if (@available(iOS 26.0, *)) { supportsGlass = YES; }
23
+ return facebook::react::typedConstants<JS::NativeDeviceCapabilities::Constants>({
24
+ .supportsLiquidGlass = (bool)supportsGlass,
25
+ .supportsNativeBlur = true,
26
+ .deviceName = [UIDevice currentDevice].name,
27
+ .deviceModel = model ?: @"",
28
+ });
29
+ }
30
+
31
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
32
+ (const facebook::react::ObjCTurboModule::InitParams &)params {
33
+ return std::make_shared<facebook::react::NativeDeviceCapabilitiesSpecJSI>(params);
34
+ }
35
+ @end
@@ -0,0 +1 @@
1
+ module.exports = { dependency: { platforms: { android: { sourceDir: './platform/android' } } } };
@@ -0,0 +1,16 @@
1
+ import {
2
+ type CodegenTypes,
3
+ type ColorValue,
4
+ type HostComponent,
5
+ type ViewProps,
6
+ codegenNativeComponent,
7
+ } from 'react-native';
8
+
9
+ export interface NativeProps extends ViewProps {
10
+ blurIntensity?: CodegenTypes.WithDefault<CodegenTypes.Double, 12>;
11
+ fadeStop?: CodegenTypes.WithDefault<CodegenTypes.Double, 0>;
12
+ direction?: CodegenTypes.WithDefault<'down' | 'up' | 'left' | 'right', 'down'>;
13
+ fallbackColor?: ColorValue;
14
+ }
15
+
16
+ export default codegenNativeComponent<NativeProps>('SliceBackgroundBlur') as HostComponent<NativeProps>;