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 +27 -32
- package/android/src/main/java/com/appandflow/reservedregions/RegionsChangeEvent.kt +2 -1
- package/android/src/main/java/com/appandflow/reservedregions/ReservedRegionsView.kt +54 -35
- package/android/src/main/java/com/appandflow/reservedregions/ReservedRegionsViewManager.kt +8 -12
- package/ios/ReservedRegionsView.h +3 -3
- package/ios/ReservedRegionsView.mm +21 -0
- package/lib/module/index.js +3 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts +9 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +4 -6
- package/src/index.tsx +9 -3
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 {
|
|
25
|
+
import { View } from 'react-native';
|
|
26
|
+
import { ReservedRegionsProvider, useReservedRegions } from 'react-native-reserved-regions';
|
|
26
27
|
|
|
27
28
|
function Screen() {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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.
|
|
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() =
|
|
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.
|
|
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
|
|
23
|
+
internal typealias RegionsChangeHandler = (ReservedRegionsView, List<ReservedRegion>, Boolean) -> Unit
|
|
24
24
|
|
|
25
25
|
@OptIn(UnstableReactNativeAPI::class)
|
|
26
|
-
class ReservedRegionsView(context: Context) : ReactViewGroup(context),
|
|
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
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
95
|
-
|
|
96
|
-
|
|
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 =
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
|
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.
|
|
8
|
-
import com.facebook.react.
|
|
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 :
|
|
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:
|
|
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(
|
|
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
|
|
5
|
-
#define
|
|
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 /*
|
|
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
|
package/lib/module/index.js
CHANGED
|
@@ -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
|
|
package/lib/module/index.js.map
CHANGED
|
@@ -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;
|
|
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 }:
|
|
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;
|
|
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.
|
|
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.
|
|
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
|
-
"
|
|
108
|
-
"
|
|
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 }:
|
|
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[] => {
|