react-native-reserved-regions 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@ Provider-scoped display divisions and occlusions for React Native's New Architec
4
4
 
5
5
  ## Requirements
6
6
 
7
- - React Native New Architecture (Fabric)
7
+ - React Native 0.80 or newer with the New Architecture (Fabric)
8
8
  - iOS 27.1 SDK and runtime for UIKit reserved regions; older SDKs compile out observation and older runtimes report an empty list
9
9
  - Android 7.0 (API 24) or newer; folding features require a device supported by Jetpack WindowManager
10
10
 
@@ -22,20 +22,25 @@ On iOS, install pods in your app's `ios` directory. Android links automatically
22
22
  ## Usage
23
23
 
24
24
  ```tsx
25
- import { ReservedRegionsProvider, useReservedRegions, useReservedRegionsReady } from 'react-native-reserved-regions';
25
+ import { View } from 'react-native';
26
+ import { ReservedRegionsProvider, useReservedRegions } from 'react-native-reserved-regions';
26
27
 
27
28
  function Screen() {
28
- const regions = useReservedRegions();
29
- const isReady = useReservedRegionsReady();
30
- if (!isReady) return null;
31
- return regions.map((region, index) => {
32
- if (region.kind === 'division') {
33
- console.log(region.frame, region.occludesContent);
34
- } else {
35
- console.log(region.frame);
36
- }
37
- return null;
38
- });
29
+ const fold = useReservedRegions().find(
30
+ (region) => region.kind === 'division' && region.frame.height > region.frame.width,
31
+ );
32
+ if (!fold) return <Detail />;
33
+ return (
34
+ <View style={{ flex: 1, flexDirection: 'row' }}>
35
+ <View style={{ width: fold.frame.x }}>
36
+ <List />
37
+ </View>
38
+ <View style={{ width: fold.frame.width }} />
39
+ <View style={{ flex: 1 }}>
40
+ <Detail />
41
+ </View>
42
+ </View>
43
+ );
39
44
  }
40
45
 
41
46
  export default function App() {
@@ -90,27 +95,17 @@ The library reports geometry and does not reposition content. Normal safe area i
90
95
 
91
96
  ## Measurement timing
92
97
 
93
- Native events request synchronous delivery. Android measures after Fabric mounting
94
- and also observes pre-draw changes. The iOS example tests same-frame delivery with
95
- an [upstream React Native event-beat patch](docs/workflow.md#react-native-event-beat-test-patch).
96
- That example patch is not installed into consuming apps. Readiness indicates a
97
- completed measurement, not a universal first-frame guarantee.
98
-
99
- ### Readiness-gated animated content
100
-
101
- On the tested RN `0.88.0-rc.1` Android stack, conditionally mounting Reanimated
102
- `4.7.0` content when `useReservedRegionsReady()` becomes true can throw
103
- `__requestMapperRunFinalizer` is undefined: Worklets `0.13.0` can run synchronous
104
- UI work ahead of queued mapper initialization. The combined hinges example uses
105
- an [example-only Worklets FIFO patch](https://github.com/appandflow/react-native-hinges/blob/main/patches/react-native-worklets%400.13.0.patch).
106
- Installing either library does not patch a consuming app's Worklets dependency.
107
- Keeping the animated subtree mounted avoided this failure in the tested case;
108
- apps that gate its mount need to apply the patch, rebuild the native app, and
109
- validate it themselves.
98
+ The first measurement requests synchronous delivery; later changes are regular
99
+ events. Android measures after Fabric mounting
100
+ and again on its own relayout, fold changes and window inset changes; scrolling
101
+ or moving an ancestor does not trigger a measurement. On iOS, same-frame delivery requires React Native
102
+ [#58530](https://github.com/react/react-native/pull/58530); see
103
+ [measurement timing](https://appandflow.github.io/react-native-reserved-regions/docs/platforms#measurement-timing).
104
+ Readiness indicates a completed measurement, not a
105
+ universal first-frame guarantee.
110
106
 
111
107
  The [performance guide](https://appandflow.github.io/react-native-reserved-regions/docs/performance)
112
- explains provider placement and optional readiness gating. `ReservedRegionsGate`
113
- is included starting with `0.1.0-alpha.3`.
108
+ explains provider placement and optional readiness gating.
114
109
 
115
110
  ## Development
116
111
 
@@ -9,10 +9,11 @@ internal class RegionsChangeEvent(
9
9
  surfaceId: Int,
10
10
  viewTag: Int,
11
11
  private val regions: List<ReservedRegion>,
12
+ private val synchronous: Boolean,
12
13
  ) : Event<RegionsChangeEvent>(surfaceId, viewTag) {
13
14
  override fun getEventName() = NAME
14
15
 
15
- override fun experimental_isSynchronous() = true
16
+ override fun experimental_isSynchronous() = synchronous
16
17
 
17
18
  override fun getEventData(): WritableMap {
18
19
  val payload = Arguments.createMap()
@@ -3,7 +3,7 @@ package com.appandflow.reservedregions
3
3
  import android.content.Context
4
4
  import android.graphics.Rect
5
5
  import android.os.Build
6
- import android.view.ViewTreeObserver
6
+ import android.view.WindowInsets
7
7
  import androidx.core.content.ContextCompat
8
8
  import androidx.core.util.Consumer
9
9
  import androidx.window.WindowSdkExtensions
@@ -20,21 +20,23 @@ import com.facebook.react.views.view.ReactViewGroup
20
20
  import kotlin.math.max
21
21
  import kotlin.math.min
22
22
 
23
- internal typealias RegionsChangeHandler = (ReservedRegionsView, List<ReservedRegion>) -> Unit
23
+ internal typealias RegionsChangeHandler = (ReservedRegionsView, List<ReservedRegion>, Boolean) -> Unit
24
24
 
25
25
  @OptIn(UnstableReactNativeAPI::class)
26
- class ReservedRegionsView(context: Context) : ReactViewGroup(context), ViewTreeObserver.OnPreDrawListener, UIManagerListener {
26
+ class ReservedRegionsView(context: Context) : ReactViewGroup(context), UIManagerListener {
27
27
  private var uiManager: UIManager? = null
28
28
  private var tracker: WindowInfoTrackerCallbackAdapter? = null
29
29
  private val layoutInfoConsumer = Consumer<WindowLayoutInfo> { info ->
30
30
  foldingFeatures = info.displayFeatures.filterIsInstance<FoldingFeature>()
31
31
  foldingFeaturesReady = true
32
- invalidate()
32
+ if (hasDispatchedRegions) updateRegionsIfInputsChanged() else updateRegions()
33
33
  }
34
34
  private var foldingFeatures: List<FoldingFeature> = emptyList()
35
35
  private var foldingFeaturesReady = false
36
36
  private var lastRegions: List<ReservedRegion>? = null
37
+ private var measuredInputs: MeasurementInputs? = null
37
38
  private var awaitingFirstMount = true
39
+ private var hasDispatchedRegions = false
38
40
  private var onRegionsChange: RegionsChangeHandler? = null
39
41
 
40
42
  override fun willDispatchViewUpdates(uiManager: UIManager) {}
@@ -51,17 +53,18 @@ class ReservedRegionsView(context: Context) : ReactViewGroup(context), ViewTreeO
51
53
 
52
54
  override fun onAttachedToWindow() {
53
55
  super.onAttachedToWindow()
54
- uiManager = UIManagerHelper.getUIManagerForReactTag(UIManagerHelper.getReactContext(this), id)
55
- awaitingFirstMount = true
56
- // React Native's FabricMountingManager.cpp applies layout before installing a view's event
57
- // emitter inside one mount batch, so onLayout cannot dispatch a synchronous event on first
58
- // mount; didMountItems is the earliest point after the emitter exists and before the event
59
- // beat. It runs for every mount batch in the app, so it is removed after the first delivery.
60
- uiManager?.addUIManagerEventListener(this)
61
- viewTreeObserver.addOnPreDrawListener(this)
62
- foldingFeatures = emptyList()
56
+ if (!hasDispatchedRegions) {
57
+ uiManager = UIManagerHelper.getUIManagerForReactTag(UIManagerHelper.getReactContext(this), id)
58
+ awaitingFirstMount = true
59
+ // React Native's FabricMountingManager.cpp applies layout before installing a view's event
60
+ // emitter inside one mount batch, so onLayout cannot dispatch a synchronous event on first
61
+ // mount; didMountItems is the earliest point after the emitter exists and before the event
62
+ // beat. It runs for every mount batch in the app, so it is removed after the first delivery.
63
+ uiManager?.addUIManagerEventListener(this)
64
+ foldingFeatures = emptyList()
65
+ lastRegions = null
66
+ }
63
67
  foldingFeaturesReady = false
64
- lastRegions = null
65
68
  val activity = (context as? ThemedReactContext)?.currentActivity
66
69
  if (activity != null) {
67
70
  val windowTracker = WindowInfoTracker.getOrCreate(context)
@@ -75,6 +78,7 @@ class ReservedRegionsView(context: Context) : ReactViewGroup(context), ViewTreeO
75
78
  } else {
76
79
  foldingFeaturesReady = true
77
80
  }
81
+ if (hasDispatchedRegions) updateRegionsIfInputsChanged()
78
82
  }
79
83
 
80
84
  override fun onDetachedFromWindow() {
@@ -82,7 +86,6 @@ class ReservedRegionsView(context: Context) : ReactViewGroup(context), ViewTreeO
82
86
  uiManager = null
83
87
  tracker?.removeWindowLayoutInfoListener(layoutInfoConsumer)
84
88
  tracker = null
85
- viewTreeObserver.removeOnPreDrawListener(this)
86
89
  super.onDetachedFromWindow()
87
90
  }
88
91
 
@@ -91,44 +94,43 @@ class ReservedRegionsView(context: Context) : ReactViewGroup(context), ViewTreeO
91
94
  if (!awaitingFirstMount) updateRegions()
92
95
  }
93
96
 
94
- override fun onPreDraw(): Boolean {
95
- updateRegions()
96
- return true
97
+ override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
98
+ val result = super.onApplyWindowInsets(insets)
99
+ if (!awaitingFirstMount) updateRegions()
100
+ return result
97
101
  }
98
102
 
99
103
  internal fun setOnRegionsChangeHandler(handler: RegionsChangeHandler) {
100
104
  onRegionsChange = handler
101
105
  lastRegions = null
102
- invalidate()
103
106
  }
104
107
 
105
108
  private fun updateRegions(): Boolean {
106
109
  val handler = onRegionsChange ?: return false
107
110
  if (!isAttachedToWindow || !foldingFeaturesReady || width == 0 || height == 0) return false
108
111
 
112
+ val inputs = measurementInputs()
113
+ measuredInputs = inputs
109
114
  val windowLocation = IntArray(2)
110
115
  getLocationInWindow(windowLocation)
111
116
  val regions = mutableListOf<ReservedRegion>()
112
117
 
113
- for (feature in foldingFeatures) {
118
+ for (feature in inputs.foldingFeatures) {
114
119
  if (!feature.isSeparating && feature.occlusionType != FoldingFeature.OcclusionType.FULL) continue
115
- val frame = clippedFrame(feature.bounds, windowLocation[0], windowLocation[1]) ?: continue
120
+ val frame = intersectingFrame(feature.bounds, windowLocation[0], windowLocation[1]) ?: continue
116
121
  regions.add(ReservedRegion("division", frame, feature.occlusionType == FoldingFeature.OcclusionType.FULL))
117
122
  }
118
123
 
119
- if (Build.VERSION.SDK_INT >= 28) {
120
- val screenLocation = IntArray(2)
121
- getLocationOnScreen(screenLocation)
122
- rootWindowInsets?.displayCutout?.boundingRects?.forEach { bounds ->
123
- clippedFrame(bounds, screenLocation[0], screenLocation[1])?.let { frame ->
124
- regions.add(ReservedRegion("occlusion", frame))
125
- }
124
+ for (bounds in inputs.cutouts) {
125
+ intersectingFrame(bounds, windowLocation[0], windowLocation[1])?.let { frame ->
126
+ regions.add(ReservedRegion("occlusion", frame))
126
127
  }
127
128
  }
128
129
 
129
130
  if (regions == lastRegions) return false
130
131
  lastRegions = regions
131
- handler(this, regions)
132
+ handler(this, regions, !hasDispatchedRegions)
133
+ hasDispatchedRegions = true
132
134
  if (awaitingFirstMount) {
133
135
  awaitingFirstMount = false
134
136
  uiManager?.removeUIManagerEventListener(this)
@@ -136,12 +138,29 @@ class ReservedRegionsView(context: Context) : ReactViewGroup(context), ViewTreeO
136
138
  return true
137
139
  }
138
140
 
139
- private fun clippedFrame(bounds: Rect, originX: Int, originY: Int): Rect? {
140
- val left = max(0, bounds.left - originX)
141
- val top = max(0, bounds.top - originY)
142
- val right = min(width, bounds.right - originX)
143
- val bottom = min(height, bounds.bottom - originY)
141
+ private fun updateRegionsIfInputsChanged() {
142
+ if (measurementInputs() != measuredInputs) updateRegions()
143
+ }
144
+
145
+ private fun measurementInputs() = MeasurementInputs(
146
+ foldingFeatures,
147
+ if (Build.VERSION.SDK_INT >= 28) rootWindowInsets?.displayCutout?.boundingRects.orEmpty() else emptyList(),
148
+ Rect(left, top, right, bottom),
149
+ )
150
+
151
+ private fun intersectingFrame(bounds: Rect, originX: Int, originY: Int): Rect? {
152
+ val frame = Rect(bounds.left - originX, bounds.top - originY, bounds.right - originX, bounds.bottom - originY)
153
+ val left = max(0, frame.left)
154
+ val top = max(0, frame.top)
155
+ val right = min(width, frame.right)
156
+ val bottom = min(height, frame.bottom)
144
157
  if (right < left || bottom < top || (right == left && bottom == top)) return null
145
- return Rect(left, top, right, bottom)
158
+ return frame
146
159
  }
147
160
  }
161
+
162
+ private data class MeasurementInputs(
163
+ val foldingFeatures: List<FoldingFeature>,
164
+ val cutouts: List<Rect>,
165
+ val frame: Rect,
166
+ )
@@ -4,17 +4,11 @@ import com.facebook.react.bridge.ReactContext
4
4
  import com.facebook.react.module.annotations.ReactModule
5
5
  import com.facebook.react.uimanager.ThemedReactContext
6
6
  import com.facebook.react.uimanager.UIManagerHelper
7
- import com.facebook.react.uimanager.ViewGroupManager
8
- import com.facebook.react.viewmanagers.ReservedRegionsViewManagerInterface
9
- import com.facebook.react.viewmanagers.ReservedRegionsViewManagerDelegate
7
+ import com.facebook.react.views.view.ReactViewGroup
8
+ import com.facebook.react.views.view.ReactViewManager
10
9
 
11
10
  @ReactModule(name = ReservedRegionsViewManager.NAME)
12
- class ReservedRegionsViewManager : ViewGroupManager<ReservedRegionsView>(),
13
- ReservedRegionsViewManagerInterface<ReservedRegionsView> {
14
- private val delegate = ReservedRegionsViewManagerDelegate(this)
15
-
16
- override fun getDelegate() = delegate
17
-
11
+ class ReservedRegionsViewManager : ReactViewManager() {
18
12
  override fun getName() = NAME
19
13
 
20
14
  override fun createViewInstance(context: ThemedReactContext) = ReservedRegionsView(context)
@@ -22,12 +16,14 @@ class ReservedRegionsViewManager : ViewGroupManager<ReservedRegionsView>(),
22
16
  override fun getExportedCustomDirectEventTypeConstants() =
23
17
  mutableMapOf(RegionsChangeEvent.NAME to mutableMapOf("registrationName" to "onRegionsChange"))
24
18
 
25
- override fun addEventEmitters(reactContext: ThemedReactContext, view: ReservedRegionsView) {
19
+ override fun addEventEmitters(reactContext: ThemedReactContext, view: ReactViewGroup) {
26
20
  super.addEventEmitters(reactContext, view)
27
- view.setOnRegionsChangeHandler { source, regions ->
21
+ (view as ReservedRegionsView).setOnRegionsChangeHandler { source, regions, synchronous ->
28
22
  val sourceContext = source.context as ReactContext
29
23
  UIManagerHelper.getEventDispatcherForReactTag(sourceContext, source.id)
30
- ?.dispatchEvent(RegionsChangeEvent(UIManagerHelper.getSurfaceId(sourceContext), source.id, regions))
24
+ ?.dispatchEvent(
25
+ RegionsChangeEvent(UIManagerHelper.getSurfaceId(sourceContext), source.id, regions, synchronous),
26
+ )
31
27
  }
32
28
  }
33
29
 
@@ -1,8 +1,8 @@
1
1
  #import <React/RCTViewComponentView.h>
2
2
  #import <UIKit/UIKit.h>
3
3
 
4
- #ifndef ReservedRegionsViewNativeComponent_h
5
- #define ReservedRegionsViewNativeComponent_h
4
+ #ifndef ReservedRegionsView_h
5
+ #define ReservedRegionsView_h
6
6
 
7
7
  NS_ASSUME_NONNULL_BEGIN
8
8
 
@@ -11,4 +11,4 @@ NS_ASSUME_NONNULL_BEGIN
11
11
 
12
12
  NS_ASSUME_NONNULL_END
13
13
 
14
- #endif /* ReservedRegionsViewNativeComponent_h */
14
+ #endif /* ReservedRegionsView_h */
@@ -10,6 +10,7 @@ using namespace facebook::react;
10
10
 
11
11
  @implementation ReservedRegionsView {
12
12
  NSArray<NSDictionary *> *_currentRegions;
13
+ BOOL _hasDispatchedRegions;
13
14
  }
14
15
 
15
16
  + (ComponentDescriptorProvider)componentDescriptorProvider
@@ -49,6 +50,17 @@ using namespace facebook::react;
49
50
  [self setNeedsLayout];
50
51
  }
51
52
 
53
+ - (void)updateLayoutMetrics:(LayoutMetrics const &)layoutMetrics
54
+ oldLayoutMetrics:(LayoutMetrics const &)oldLayoutMetrics
55
+ {
56
+ [super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
57
+ // React Native's UIView+ComponentViewProtocol applies layout through center and bounds; an origin-only change
58
+ // does not trigger layoutSubviews.
59
+ if (layoutMetrics.frame.origin != oldLayoutMetrics.frame.origin) {
60
+ [self setNeedsLayout];
61
+ }
62
+ }
63
+
52
64
  - (void)updateEventEmitter:(EventEmitter::Shared const &)eventEmitter
53
65
  {
54
66
  [super updateEventEmitter:eventEmitter];
@@ -96,6 +108,14 @@ using namespace facebook::react;
96
108
  ("occludesContent", false));
97
109
  }
98
110
  auto eventEmitter = _eventEmitter;
111
+ if (_hasDispatchedRegions) {
112
+ // React Native's EventQueue replaces this view's pending unique event when it is the last one queued.
113
+ eventEmitter->dispatchUniqueEvent(
114
+ "regionsChange",
115
+ folly::dynamic::object("regions", std::move(regionPayloads)));
116
+ return;
117
+ }
118
+ _hasDispatchedRegions = YES;
99
119
  eventEmitter->experimental_flushSync([eventEmitter, regions = std::move(regionPayloads)]() mutable {
100
120
  eventEmitter->dispatchEvent(
101
121
  "regionsChange",
@@ -108,6 +128,7 @@ using namespace facebook::react;
108
128
  {
109
129
  [super prepareForRecycle];
110
130
  _currentRegions = nil;
131
+ _hasDispatchedRegions = NO;
111
132
  }
112
133
 
113
134
  @end
@@ -3,9 +3,11 @@
3
3
  import * as React from 'react';
4
4
  import { ReservedRegionsView } from './ReservedRegionsView';
5
5
 
6
- /** A rectangle in logical points relative to the nearest ReservedRegionsProvider. */
6
+ /** A rectangle in logical points relative to the nearest ReservedRegionsProvider. Not clipped to the provider. */
7
7
 
8
8
  /** An active area reserved by the display or system UI. */
9
+
10
+ /** Props for ReservedRegionsProvider. */
9
11
  import { jsx as _jsx } from "react/jsx-runtime";
10
12
  const ReservedRegionsContext = /*#__PURE__*/React.createContext(null);
11
13
 
@@ -1 +1 @@
1
- {"version":3,"names":["React","ReservedRegionsView","jsx","_jsx","ReservedRegionsContext","createContext","ReservedRegionsProvider","children","props","snapshot","setSnapshot","useState","regions","isReady","onRegionsChange","useCallback","event","nativeEvent","flatMap","region","kind","frame","occludesContent","Provider","value","useReservedRegions","useContext","Error","useReservedRegionsReady","ReservedRegionsGate"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAO,KAAKA,KAAK,MAAM,OAAO;AAE9B,SAASC,mBAAmB,QAAQ,uBAAuB;;AAG3D;;AAYA;AAAA,SAAAC,GAAA,IAAAC,IAAA;AAsBA,MAAMC,sBAAsB,gBAAGJ,KAAK,CAACK,aAAa,CAAiC,IAAI,CAAC;;AAExF;AACA,OAAO,SAASC,uBAAuBA,CAAC;EAAEC,QAAQ;EAAE,GAAGC;AAAiB,CAAC,EAAqB;EAC5F,MAAM,CAACC,QAAQ,EAAEC,WAAW,CAAC,GAAGV,KAAK,CAACW,QAAQ,CAA0B;IAAEC,OAAO,EAAE,EAAE;IAAEC,OAAO,EAAE;EAAM,CAAC,CAAC;EACxG,MAAMC,eAAe,GAAGd,KAAK,CAACe,WAAW,CAAEC,KAA0C,IAAK;IACxF,MAAMJ,OAAO,GAAGI,KAAK,CAACC,WAAW,CAACL,OAAO,CAACM,OAAO,CAAEC,MAAM,IAAuB;MAC9E,IAAIA,MAAM,CAACC,IAAI,KAAK,UAAU,EAAE;QAC9B,OAAO,CAAC;UAAEA,IAAI,EAAE,UAAU;UAAEC,KAAK,EAAEF,MAAM,CAACE,KAAK;UAAEC,eAAe,EAAEH,MAAM,CAACG;QAAgB,CAAC,CAAC;MAC7F;MACA,IAAIH,MAAM,CAACC,IAAI,KAAK,WAAW,EAAE;QAC/B,OAAO,CAAC;UAAEA,IAAI,EAAE,WAAW;UAAEC,KAAK,EAAEF,MAAM,CAACE;QAAM,CAAC,CAAC;MACrD;MACA,OAAO,EAAE;IACX,CAAC,CAAC;IACFX,WAAW,CAAC;MAAEE,OAAO;MAAEC,OAAO,EAAE;IAAK,CAAC,CAAC;EACzC,CAAC,EAAE,EAAE,CAAC;EAEN,oBACEV,IAAA,CAACC,sBAAsB,CAACmB,QAAQ;IAACC,KAAK,EAAEf,QAAS;IAAAF,QAAA,eAC/CJ,IAAA,CAACF,mBAAmB;MAAA,GAAKO,KAAK;MAAEM,eAAe,EAAEA,eAAgB;MAAAP,QAAA,EAC9DA;IAAQ,CACU;EAAC,CACS,CAAC;AAEtC;;AAEA;AACA,OAAO,SAASkB,kBAAkBA,CAAA,EAA8B;EAC9D,MAAMhB,QAAQ,GAAGT,KAAK,CAAC0B,UAAU,CAACtB,sBAAsB,CAAC;EACzD,IAAIK,QAAQ,KAAK,IAAI,EAAE;IACrB,MAAM,IAAIkB,KAAK,CAAC,gEAAgE,CAAC;EACnF;EACA,OAAOlB,QAAQ,CAACG,OAAO;AACzB;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASgB,uBAAuBA,CAAA,EAAY;EACjD,MAAMnB,QAAQ,GAAGT,KAAK,CAAC0B,UAAU,CAACtB,sBAAsB,CAAC;EACzD,IAAIK,QAAQ,KAAK,IAAI,EAAE;IACrB,MAAM,IAAIkB,KAAK,CAAC,qEAAqE,CAAC;EACxF;EACA,OAAOlB,QAAQ,CAACI,OAAO;AACzB;;AAEA;AACA,OAAO,SAASgB,mBAAmBA,CAAC;EAAEtB;AAAkC,CAAC,EAAmB;EAC1F,OAAOqB,uBAAuB,CAAC,CAAC,GAAGrB,QAAQ,GAAG,IAAI;AACpD","ignoreList":[]}
1
+ {"version":3,"names":["React","ReservedRegionsView","jsx","_jsx","ReservedRegionsContext","createContext","ReservedRegionsProvider","children","props","snapshot","setSnapshot","useState","regions","isReady","onRegionsChange","useCallback","event","nativeEvent","flatMap","region","kind","frame","occludesContent","Provider","value","useReservedRegions","useContext","Error","useReservedRegionsReady","ReservedRegionsGate"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAO,KAAKA,KAAK,MAAM,OAAO;AAE9B,SAASC,mBAAmB,QAAQ,uBAAuB;;AAG3D;;AAYA;;AAiBA;AAAA,SAAAC,GAAA,IAAAC,IAAA;AAWA,MAAMC,sBAAsB,gBAAGJ,KAAK,CAACK,aAAa,CAAiC,IAAI,CAAC;;AAExF;AACA,OAAO,SAASC,uBAAuBA,CAAC;EAAEC,QAAQ;EAAE,GAAGC;AAAoC,CAAC,EAAqB;EAC/G,MAAM,CAACC,QAAQ,EAAEC,WAAW,CAAC,GAAGV,KAAK,CAACW,QAAQ,CAA0B;IAAEC,OAAO,EAAE,EAAE;IAAEC,OAAO,EAAE;EAAM,CAAC,CAAC;EACxG,MAAMC,eAAe,GAAGd,KAAK,CAACe,WAAW,CAAEC,KAA0C,IAAK;IACxF,MAAMJ,OAAO,GAAGI,KAAK,CAACC,WAAW,CAACL,OAAO,CAACM,OAAO,CAAEC,MAAM,IAAuB;MAC9E,IAAIA,MAAM,CAACC,IAAI,KAAK,UAAU,EAAE;QAC9B,OAAO,CAAC;UAAEA,IAAI,EAAE,UAAU;UAAEC,KAAK,EAAEF,MAAM,CAACE,KAAK;UAAEC,eAAe,EAAEH,MAAM,CAACG;QAAgB,CAAC,CAAC;MAC7F;MACA,IAAIH,MAAM,CAACC,IAAI,KAAK,WAAW,EAAE;QAC/B,OAAO,CAAC;UAAEA,IAAI,EAAE,WAAW;UAAEC,KAAK,EAAEF,MAAM,CAACE;QAAM,CAAC,CAAC;MACrD;MACA,OAAO,EAAE;IACX,CAAC,CAAC;IACFX,WAAW,CAAC;MAAEE,OAAO;MAAEC,OAAO,EAAE;IAAK,CAAC,CAAC;EACzC,CAAC,EAAE,EAAE,CAAC;EAEN,oBACEV,IAAA,CAACC,sBAAsB,CAACmB,QAAQ;IAACC,KAAK,EAAEf,QAAS;IAAAF,QAAA,eAC/CJ,IAAA,CAACF,mBAAmB;MAAA,GAAKO,KAAK;MAAEM,eAAe,EAAEA,eAAgB;MAAAP,QAAA,EAC9DA;IAAQ,CACU;EAAC,CACS,CAAC;AAEtC;;AAEA;AACA,OAAO,SAASkB,kBAAkBA,CAAA,EAA8B;EAC9D,MAAMhB,QAAQ,GAAGT,KAAK,CAAC0B,UAAU,CAACtB,sBAAsB,CAAC;EACzD,IAAIK,QAAQ,KAAK,IAAI,EAAE;IACrB,MAAM,IAAIkB,KAAK,CAAC,gEAAgE,CAAC;EACnF;EACA,OAAOlB,QAAQ,CAACG,OAAO;AACzB;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASgB,uBAAuBA,CAAA,EAAY;EACjD,MAAMnB,QAAQ,GAAGT,KAAK,CAAC0B,UAAU,CAACtB,sBAAsB,CAAC;EACzD,IAAIK,QAAQ,KAAK,IAAI,EAAE;IACrB,MAAM,IAAIkB,KAAK,CAAC,qEAAqE,CAAC;EACxF;EACA,OAAOlB,QAAQ,CAACI,OAAO;AACzB;;AAEA;AACA,OAAO,SAASgB,mBAAmBA,CAAC;EAAEtB;AAAkC,CAAC,EAAmB;EAC1F,OAAOqB,uBAAuB,CAAC,CAAC,GAAGrB,QAAQ,GAAG,IAAI;AACpD","ignoreList":[]}
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
- import type { ViewProps } from 'react-native';
3
- /** A rectangle in logical points relative to the nearest ReservedRegionsProvider. */
2
+ import type { View, ViewProps } from 'react-native';
3
+ /** A rectangle in logical points relative to the nearest ReservedRegionsProvider. Not clipped to the provider. */
4
4
  export type ReservedRegionFrame = Readonly<{
5
5
  /** Distance from the provider's left edge. */
6
6
  x: number;
@@ -25,8 +25,13 @@ export type ReservedRegion = Readonly<{
25
25
  /** The occlusion's bounds in the provider's coordinate space. */
26
26
  frame: ReservedRegionFrame;
27
27
  }>;
28
+ /** Props for ReservedRegionsProvider. */
29
+ type ReservedRegionsProviderProps = ViewProps & {
30
+ /** Receives the provider's native view, for example to call `measure`. */
31
+ ref?: React.Ref<React.ComponentRef<typeof View>>;
32
+ };
28
33
  /** Makes active reserved regions available to descendants in provider coordinates. */
29
- export declare function ReservedRegionsProvider({ children, ...props }: ViewProps): React.JSX.Element;
34
+ export declare function ReservedRegionsProvider({ children, ...props }: ReservedRegionsProviderProps): React.JSX.Element;
30
35
  /** Returns active regions reported by the nearest ReservedRegionsProvider. */
31
36
  export declare function useReservedRegions(): readonly ReservedRegion[];
32
37
  /**
@@ -36,4 +41,5 @@ export declare function useReservedRegions(): readonly ReservedRegion[];
36
41
  export declare function useReservedRegionsReady(): boolean;
37
42
  /** Renders children after the nearest provider reports its first measurement. */
38
43
  export declare function ReservedRegionsGate({ children }: React.PropsWithChildren): React.ReactNode;
44
+ export {};
39
45
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAI9C,qFAAqF;AACrF,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,8CAA8C;IAC9C,CAAC,EAAE,MAAM,CAAC;IACV,6CAA6C;IAC7C,CAAC,EAAE,MAAM,CAAC;IACV,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC;AAEH,2DAA2D;AAC3D,MAAM,MAAM,cAAc,GACtB,QAAQ,CAAC;IACP,sDAAsD;IACtD,IAAI,EAAE,UAAU,CAAC;IACjB,gEAAgE;IAChE,KAAK,EAAE,mBAAmB,CAAC;IAC3B,yDAAyD;IACzD,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC,GACF,QAAQ,CAAC;IACP,gEAAgE;IAChE,IAAI,EAAE,WAAW,CAAC;IAClB,iEAAiE;IACjE,KAAK,EAAE,mBAAmB,CAAC;CAC5B,CAAC,CAAC;AASP,sFAAsF;AACtF,wBAAgB,uBAAuB,CAAC,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAsB5F;AAED,8EAA8E;AAC9E,wBAAgB,kBAAkB,IAAI,SAAS,cAAc,EAAE,CAM9D;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,OAAO,CAMjD;AAED,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,iBAAiB,GAAG,KAAK,CAAC,SAAS,CAE1F"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIpD,kHAAkH;AAClH,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,8CAA8C;IAC9C,CAAC,EAAE,MAAM,CAAC;IACV,6CAA6C;IAC7C,CAAC,EAAE,MAAM,CAAC;IACV,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC;AAEH,2DAA2D;AAC3D,MAAM,MAAM,cAAc,GACtB,QAAQ,CAAC;IACP,sDAAsD;IACtD,IAAI,EAAE,UAAU,CAAC;IACjB,gEAAgE;IAChE,KAAK,EAAE,mBAAmB,CAAC;IAC3B,yDAAyD;IACzD,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC,GACF,QAAQ,CAAC;IACP,gEAAgE;IAChE,IAAI,EAAE,WAAW,CAAC;IAClB,iEAAiE;IACjE,KAAK,EAAE,mBAAmB,CAAC;CAC5B,CAAC,CAAC;AAEP,yCAAyC;AACzC,KAAK,4BAA4B,GAAG,SAAS,GAAG;IAC9C,0EAA0E;IAC1E,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC;CAClD,CAAC;AASF,sFAAsF;AACtF,wBAAgB,uBAAuB,CAAC,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,4BAA4B,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAsB/G;AAED,8EAA8E;AAC9E,wBAAgB,kBAAkB,IAAI,SAAS,cAAc,EAAE,CAM9D;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,OAAO,CAMjD;AAED,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,iBAAiB,GAAG,KAAK,CAAC,SAAS,CAE1F"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-reserved-regions",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Provider-scoped reserved display regions for React Native",
5
5
  "keywords": [
6
6
  "android",
@@ -20,9 +20,7 @@
20
20
  "lib",
21
21
  "android",
22
22
  "ios",
23
- "cpp",
24
23
  "*.podspec",
25
- "react-native.config.js",
26
24
  "!ios/build",
27
25
  "!android/build",
28
26
  "!android/gradle",
@@ -50,7 +48,7 @@
50
48
  "devDependencies": {
51
49
  "@jest/globals": "^29.7.0",
52
50
  "@react-native/babel-preset": "0.88.0-rc.1",
53
- "@react-native/jest-preset": "0.86.2",
51
+ "@react-native/jest-preset": "0.88.0-rc.1",
54
52
  "@types/react": "^19.2.0",
55
53
  "@types/react-test-renderer": "^19.1.0",
56
54
  "del-cli": "^7.0.0",
@@ -104,8 +102,8 @@
104
102
  "type": "fabric-view",
105
103
  "languages": "kotlin-objc",
106
104
  "tools": [
107
- "eslint",
108
- "jest"
105
+ "jest",
106
+ "oxlint"
109
107
  ],
110
108
  "version": "0.63.1"
111
109
  },
package/src/index.tsx CHANGED
@@ -1,9 +1,9 @@
1
1
  import * as React from 'react';
2
- import type { ViewProps } from 'react-native';
2
+ import type { View, ViewProps } from 'react-native';
3
3
  import { ReservedRegionsView } from './ReservedRegionsView';
4
4
  import type { RegionsChangeEvent } from './ReservedRegionsViewNativeComponent';
5
5
 
6
- /** A rectangle in logical points relative to the nearest ReservedRegionsProvider. */
6
+ /** A rectangle in logical points relative to the nearest ReservedRegionsProvider. Not clipped to the provider. */
7
7
  export type ReservedRegionFrame = Readonly<{
8
8
  /** Distance from the provider's left edge. */
9
9
  x: number;
@@ -32,6 +32,12 @@ export type ReservedRegion =
32
32
  frame: ReservedRegionFrame;
33
33
  }>;
34
34
 
35
+ /** Props for ReservedRegionsProvider. */
36
+ type ReservedRegionsProviderProps = ViewProps & {
37
+ /** Receives the provider's native view, for example to call `measure`. */
38
+ ref?: React.Ref<React.ComponentRef<typeof View>>;
39
+ };
40
+
35
41
  type ReservedRegionsSnapshot = Readonly<{
36
42
  regions: readonly ReservedRegion[];
37
43
  isReady: boolean;
@@ -40,7 +46,7 @@ type ReservedRegionsSnapshot = Readonly<{
40
46
  const ReservedRegionsContext = React.createContext<ReservedRegionsSnapshot | null>(null);
41
47
 
42
48
  /** Makes active reserved regions available to descendants in provider coordinates. */
43
- export function ReservedRegionsProvider({ children, ...props }: ViewProps): React.JSX.Element {
49
+ export function ReservedRegionsProvider({ children, ...props }: ReservedRegionsProviderProps): React.JSX.Element {
44
50
  const [snapshot, setSnapshot] = React.useState<ReservedRegionsSnapshot>({ regions: [], isReady: false });
45
51
  const onRegionsChange = React.useCallback((event: { nativeEvent: RegionsChangeEvent }) => {
46
52
  const regions = event.nativeEvent.regions.flatMap((region): ReservedRegion[] => {