react-native-reserved-regions 0.1.0-alpha.0 → 0.1.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -14
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/{reservedregions → appandflow/reservedregions}/RegionsChangeEvent.kt +3 -1
- package/android/src/main/java/com/{reservedregions → appandflow/reservedregions}/ReservedRegion.kt +1 -1
- package/android/src/main/java/com/{reservedregions → appandflow/reservedregions}/ReservedRegionsPackage.kt +1 -1
- package/android/src/main/java/com/{reservedregions → appandflow/reservedregions}/ReservedRegionsView.kt +45 -15
- package/android/src/main/java/com/{reservedregions → appandflow/reservedregions}/ReservedRegionsViewManager.kt +1 -1
- package/ios/ReservedRegionsView.mm +35 -38
- package/lib/module/ReservedRegionsView.js +9 -1
- package/lib/module/ReservedRegionsView.js.map +1 -1
- package/lib/module/ReservedRegionsViewNativeComponent.ts +1 -5
- package/lib/module/index.js +43 -23
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/ReservedRegionsView.d.ts +1 -1
- package/lib/typescript/src/ReservedRegionsView.d.ts.map +1 -1
- package/lib/typescript/src/ReservedRegionsViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +5 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +74 -78
- package/src/ReservedRegionsView.tsx +5 -4
- package/src/ReservedRegionsViewNativeComponent.ts +1 -5
- package/src/index.tsx +39 -37
package/README.md
CHANGED
|
@@ -5,15 +5,19 @@ Provider-scoped display divisions and occlusions for React Native's New Architec
|
|
|
5
5
|
## Requirements
|
|
6
6
|
|
|
7
7
|
- React Native New Architecture (Fabric)
|
|
8
|
-
- iOS 27.1 for UIKit reserved regions;
|
|
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
|
|
|
11
11
|
The example app uses React Native 0.88.0-rc.1.
|
|
12
|
+
Its iOS target adopts `UISceneDelegate` because the RC template currently crashes on iOS 27 without it ([React Native issue #58606](https://github.com/react/react-native/issues/58606)).
|
|
12
13
|
|
|
13
14
|
## Installation
|
|
14
15
|
|
|
16
|
+
`0.1.0-alpha.2` is the functional release candidate. Install the explicit version
|
|
17
|
+
once publication is verified; until then, run the repository example.
|
|
18
|
+
|
|
15
19
|
```sh
|
|
16
|
-
npm install react-native-reserved-regions
|
|
20
|
+
npm install react-native-reserved-regions@0.1.0-alpha.2
|
|
17
21
|
```
|
|
18
22
|
|
|
19
23
|
On iOS, install pods in your app's `ios` directory. Android links automatically through React Native autolinking.
|
|
@@ -21,13 +25,12 @@ On iOS, install pods in your app's `ios` directory. Android links automatically
|
|
|
21
25
|
## Usage
|
|
22
26
|
|
|
23
27
|
```tsx
|
|
24
|
-
import {
|
|
25
|
-
ReservedRegionsProvider,
|
|
26
|
-
useReservedRegions,
|
|
27
|
-
} from 'react-native-reserved-regions';
|
|
28
|
+
import { ReservedRegionsProvider, useReservedRegions, useReservedRegionsReady } from 'react-native-reserved-regions';
|
|
28
29
|
|
|
29
30
|
function Screen() {
|
|
30
31
|
const regions = useReservedRegions();
|
|
32
|
+
const isReady = useReservedRegionsReady();
|
|
33
|
+
if (!isReady) return null;
|
|
31
34
|
return regions.map((region, index) => {
|
|
32
35
|
if (region.kind === 'division') {
|
|
33
36
|
console.log(region.frame, region.occludesContent);
|
|
@@ -47,7 +50,7 @@ export default function App() {
|
|
|
47
50
|
}
|
|
48
51
|
```
|
|
49
52
|
|
|
50
|
-
`useReservedRegions()` returns an
|
|
53
|
+
`useReservedRegions()` always returns an array, initially `[]`. `useReservedRegionsReady()` is initially `false` and becomes `true` with the first measurement, including an empty result. It stays true for that provider’s lifetime. Unsupported platforms report a known empty result; readiness does not indicate hardware support or guarantee first-frame timing. Both hooks must be called beneath a `ReservedRegionsProvider`. Each region's `frame` uses logical points relative to that provider's top-left corner. A nested provider establishes its own coordinate space. The provider accepts standard React Native `View` props and should cover the content whose reserved regions you want to inspect.
|
|
51
54
|
|
|
52
55
|
The public type is a tagged union:
|
|
53
56
|
|
|
@@ -68,22 +71,48 @@ A division splits the available display. `occludesContent` tells you whether con
|
|
|
68
71
|
|
|
69
72
|
## Native sources
|
|
70
73
|
|
|
71
|
-
- iOS queries UIKit's active `UIView` reserved division and occlusion regions on the provider view.
|
|
74
|
+
- iOS queries UIKit's active `UIView` reserved division and occlusion regions on the provider view. Typed UIKit calls are guarded by the SDK version and runtime availability. Building with an SDK older than iOS 27.1 compiles out this feature. [Apple's reserved regions overview](https://developer.apple.com/videos/play/tech-talks/111463/)
|
|
72
75
|
- Android observes Jetpack WindowManager `FoldingFeature` values and Android display cutout rectangles. A separating or fully occluding fold becomes a division; `OcclusionType.FULL` sets `occludesContent` to `true`. Display cutouts become occlusions. [FoldingFeature reference](https://developer.android.com/reference/androidx/window/layout/FoldingFeature), [WindowInsets reference](https://developer.android.com/reference/android/view/WindowInsets)
|
|
73
76
|
|
|
74
77
|
The library reports geometry and does not reposition content. Normal safe area insets, system bars, and keyboard insets remain separate concerns.
|
|
75
78
|
|
|
79
|
+
## Measurement timing
|
|
80
|
+
|
|
81
|
+
Native events request synchronous delivery. Android measures after Fabric mounting
|
|
82
|
+
and also observes pre-draw changes. The iOS example tests same-frame delivery with
|
|
83
|
+
an [upstream React Native event-beat patch](docs/workflow.md#react-native-event-beat-test-patch).
|
|
84
|
+
That example patch is not installed into consuming apps. Readiness indicates a
|
|
85
|
+
completed measurement, not a universal first-frame guarantee.
|
|
86
|
+
|
|
87
|
+
### Readiness-gated animated content
|
|
88
|
+
|
|
89
|
+
On the tested RN `0.88.0-rc.1` Android stack, conditionally mounting Reanimated
|
|
90
|
+
`4.7.0` content when `useReservedRegionsReady()` becomes true can throw
|
|
91
|
+
`__requestMapperRunFinalizer` is undefined: Worklets `0.13.0` can run synchronous
|
|
92
|
+
UI work ahead of queued mapper initialization. The combined hinges example uses
|
|
93
|
+
an [example-only Worklets FIFO patch](https://github.com/appandflow/react-native-hinges/blob/main/patches/react-native-worklets%400.13.0.patch).
|
|
94
|
+
Installing either library does not patch a consuming app's Worklets dependency.
|
|
95
|
+
Keeping the animated subtree mounted avoided this failure in the tested case;
|
|
96
|
+
apps that gate its mount need to apply the patch, rebuild the native app, and
|
|
97
|
+
validate it themselves.
|
|
98
|
+
|
|
76
99
|
## Development
|
|
77
100
|
|
|
78
101
|
```sh
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
102
|
+
pnpm install --frozen-lockfile
|
|
103
|
+
pnpm run format:check
|
|
104
|
+
pnpm run lint
|
|
105
|
+
pnpm run typecheck
|
|
106
|
+
pnpm test
|
|
107
|
+
pnpm run build
|
|
108
|
+
pnpm run docs:build
|
|
84
109
|
```
|
|
85
110
|
|
|
86
|
-
|
|
111
|
+
The example includes full-screen, inset and content-box providers with
|
|
112
|
+
`react-native-safe-area-context` values and overlays for comparison.
|
|
113
|
+
See [the development workflow](docs/workflow.md) for native runs,
|
|
114
|
+
[the docs website](website/README.md) for local previews, and
|
|
115
|
+
[the release process](RELEASE.md) for publishing.
|
|
87
116
|
|
|
88
117
|
## License
|
|
89
118
|
|
package/android/build.gradle
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
package com.reservedregions
|
|
1
|
+
package com.appandflow.reservedregions
|
|
2
2
|
|
|
3
3
|
import com.facebook.react.bridge.Arguments
|
|
4
4
|
import com.facebook.react.bridge.WritableMap
|
|
@@ -12,6 +12,8 @@ internal class RegionsChangeEvent(
|
|
|
12
12
|
) : Event<RegionsChangeEvent>(surfaceId, viewTag) {
|
|
13
13
|
override fun getEventName() = NAME
|
|
14
14
|
|
|
15
|
+
override fun experimental_isSynchronous() = true
|
|
16
|
+
|
|
15
17
|
override fun getEventData(): WritableMap {
|
|
16
18
|
val payload = Arguments.createMap()
|
|
17
19
|
val regionArray = Arguments.createArray()
|
|
@@ -1,45 +1,80 @@
|
|
|
1
|
-
package com.reservedregions
|
|
1
|
+
package com.appandflow.reservedregions
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import android.graphics.Rect
|
|
5
5
|
import android.os.Build
|
|
6
6
|
import android.view.ViewTreeObserver
|
|
7
|
-
import android.view.WindowInsets
|
|
8
7
|
import androidx.core.content.ContextCompat
|
|
9
8
|
import androidx.core.util.Consumer
|
|
9
|
+
import androidx.window.WindowSdkExtensions
|
|
10
10
|
import androidx.window.java.layout.WindowInfoTrackerCallbackAdapter
|
|
11
11
|
import androidx.window.layout.FoldingFeature
|
|
12
12
|
import androidx.window.layout.WindowInfoTracker
|
|
13
13
|
import androidx.window.layout.WindowLayoutInfo
|
|
14
|
+
import com.facebook.react.bridge.UIManager
|
|
15
|
+
import com.facebook.react.bridge.UIManagerListener
|
|
16
|
+
import com.facebook.react.common.annotations.UnstableReactNativeAPI
|
|
14
17
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
18
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
15
19
|
import com.facebook.react.views.view.ReactViewGroup
|
|
16
20
|
import kotlin.math.max
|
|
17
21
|
import kotlin.math.min
|
|
18
22
|
|
|
19
23
|
internal typealias RegionsChangeHandler = (ReservedRegionsView, List<ReservedRegion>) -> Unit
|
|
20
24
|
|
|
21
|
-
class
|
|
25
|
+
@OptIn(UnstableReactNativeAPI::class)
|
|
26
|
+
class ReservedRegionsView(context: Context) : ReactViewGroup(context), ViewTreeObserver.OnPreDrawListener, UIManagerListener {
|
|
27
|
+
private var uiManager: UIManager? = null
|
|
22
28
|
private var tracker: WindowInfoTrackerCallbackAdapter? = null
|
|
23
29
|
private val layoutInfoConsumer = Consumer<WindowLayoutInfo> { info ->
|
|
24
30
|
foldingFeatures = info.displayFeatures.filterIsInstance<FoldingFeature>()
|
|
25
|
-
|
|
31
|
+
foldingFeaturesReady = true
|
|
32
|
+
invalidate()
|
|
26
33
|
}
|
|
27
34
|
private var foldingFeatures: List<FoldingFeature> = emptyList()
|
|
35
|
+
private var foldingFeaturesReady = false
|
|
28
36
|
private var lastRegions: List<ReservedRegion>? = null
|
|
29
37
|
private var onRegionsChange: RegionsChangeHandler? = null
|
|
30
38
|
|
|
39
|
+
override fun willDispatchViewUpdates(uiManager: UIManager) {}
|
|
40
|
+
|
|
41
|
+
override fun willMountItems(uiManager: UIManager) {}
|
|
42
|
+
|
|
43
|
+
override fun didScheduleMountItems(uiManager: UIManager) {}
|
|
44
|
+
|
|
45
|
+
override fun didDispatchMountItems(uiManager: UIManager) {}
|
|
46
|
+
|
|
47
|
+
override fun didMountItems(uiManager: UIManager) {
|
|
48
|
+
// Fabric installs event emitters after layout; its event beat runs before pre-draw.
|
|
49
|
+
updateRegions()
|
|
50
|
+
}
|
|
51
|
+
|
|
31
52
|
override fun onAttachedToWindow() {
|
|
32
53
|
super.onAttachedToWindow()
|
|
54
|
+
uiManager = UIManagerHelper.getUIManagerForReactTag(UIManagerHelper.getReactContext(this), id)
|
|
55
|
+
uiManager?.addUIManagerEventListener(this)
|
|
33
56
|
viewTreeObserver.addOnPreDrawListener(this)
|
|
57
|
+
foldingFeatures = emptyList()
|
|
58
|
+
foldingFeaturesReady = false
|
|
59
|
+
lastRegions = null
|
|
34
60
|
val activity = (context as? ThemedReactContext)?.currentActivity
|
|
35
61
|
if (activity != null) {
|
|
36
|
-
|
|
62
|
+
val windowTracker = WindowInfoTracker.getOrCreate(context)
|
|
63
|
+
if (WindowSdkExtensions.getInstance().extensionVersion >= 9) {
|
|
64
|
+
foldingFeatures = windowTracker.getCurrentWindowLayoutInfo(activity)
|
|
65
|
+
.displayFeatures.filterIsInstance<FoldingFeature>()
|
|
66
|
+
foldingFeaturesReady = true
|
|
67
|
+
}
|
|
68
|
+
tracker = WindowInfoTrackerCallbackAdapter(windowTracker)
|
|
37
69
|
tracker?.addWindowLayoutInfoListener(activity, ContextCompat.getMainExecutor(context), layoutInfoConsumer)
|
|
70
|
+
} else {
|
|
71
|
+
foldingFeaturesReady = true
|
|
38
72
|
}
|
|
39
|
-
updateRegions()
|
|
40
73
|
}
|
|
41
74
|
|
|
42
75
|
override fun onDetachedFromWindow() {
|
|
76
|
+
uiManager?.removeUIManagerEventListener(this)
|
|
77
|
+
uiManager = null
|
|
43
78
|
tracker?.removeWindowLayoutInfoListener(layoutInfoConsumer)
|
|
44
79
|
tracker = null
|
|
45
80
|
viewTreeObserver.removeOnPreDrawListener(this)
|
|
@@ -53,12 +88,13 @@ class ReservedRegionsView(context: Context) : ReactViewGroup(context), ViewTreeO
|
|
|
53
88
|
|
|
54
89
|
internal fun setOnRegionsChangeHandler(handler: RegionsChangeHandler) {
|
|
55
90
|
onRegionsChange = handler
|
|
56
|
-
|
|
91
|
+
lastRegions = null
|
|
92
|
+
invalidate()
|
|
57
93
|
}
|
|
58
94
|
|
|
59
95
|
private fun updateRegions() {
|
|
60
96
|
val handler = onRegionsChange ?: return
|
|
61
|
-
if (!isAttachedToWindow || width == 0 || height == 0) return
|
|
97
|
+
if (!isAttachedToWindow || !foldingFeaturesReady || width == 0 || height == 0) return
|
|
62
98
|
|
|
63
99
|
val windowLocation = IntArray(2)
|
|
64
100
|
getLocationInWindow(windowLocation)
|
|
@@ -70,13 +106,7 @@ class ReservedRegionsView(context: Context) : ReactViewGroup(context), ViewTreeO
|
|
|
70
106
|
regions.add(ReservedRegion("division", frame, feature.occlusionType == FoldingFeature.OcclusionType.FULL))
|
|
71
107
|
}
|
|
72
108
|
|
|
73
|
-
if (Build.VERSION.SDK_INT >=
|
|
74
|
-
rootWindowInsets?.getBoundingRects(WindowInsets.Type.displayCutout())?.forEach { bounds ->
|
|
75
|
-
clippedFrame(bounds, windowLocation[0], windowLocation[1])?.let { frame ->
|
|
76
|
-
regions.add(ReservedRegion("occlusion", frame))
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
} else if (Build.VERSION.SDK_INT >= 28) {
|
|
109
|
+
if (Build.VERSION.SDK_INT >= 28) {
|
|
80
110
|
val screenLocation = IntArray(2)
|
|
81
111
|
getLocationOnScreen(screenLocation)
|
|
82
112
|
rootWindowInsets?.displayCutout?.boundingRects?.forEach { bounds ->
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
#import "ReservedRegionsView.h"
|
|
2
2
|
|
|
3
|
-
#import <objc/message.h>
|
|
4
|
-
|
|
5
3
|
#import <react/renderer/components/ReservedRegionsViewSpec/ComponentDescriptors.h>
|
|
6
|
-
#import <react/renderer/components/ReservedRegionsViewSpec/EventEmitters.h>
|
|
7
4
|
#import <react/renderer/components/ReservedRegionsViewSpec/Props.h>
|
|
8
5
|
#import <react/renderer/components/ReservedRegionsViewSpec/RCTComponentViewHelpers.h>
|
|
9
6
|
|
|
@@ -38,62 +35,62 @@ using namespace facebook::react;
|
|
|
38
35
|
- (void)didMoveToWindow
|
|
39
36
|
{
|
|
40
37
|
[super didMoveToWindow];
|
|
41
|
-
[self
|
|
38
|
+
[self setNeedsLayout];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
- (void)updateEventEmitter:(EventEmitter::Shared const &)eventEmitter
|
|
42
|
+
{
|
|
43
|
+
[super updateEventEmitter:eventEmitter];
|
|
44
|
+
[self setNeedsLayout];
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
- (void)updateRegions
|
|
45
48
|
{
|
|
46
|
-
if (self.window == nil || !_eventEmitter) {
|
|
49
|
+
if (self.window == nil || !_eventEmitter || CGSizeEqualToSize(self.bounds.size, CGSizeZero)) {
|
|
47
50
|
return;
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
// UIView reserved region selectors first ship in the iOS 27.1 SDK.
|
|
51
|
-
SEL querySelector = NSSelectorFromString(@"reservedRegionsOfKind:options:");
|
|
52
|
-
Class kindClass = NSClassFromString(@"UIViewReservedRegionKind");
|
|
53
53
|
NSMutableArray<NSDictionary *> *regions = [NSMutableArray new];
|
|
54
|
-
|
|
55
|
-
if (
|
|
56
|
-
NSArray<
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
#if defined(__IPHONE_27_1) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_27_1
|
|
55
|
+
if (@available(iOS 27.1, *)) {
|
|
56
|
+
NSArray<UIViewReservedRegionKind *> *kinds = @[
|
|
57
|
+
UIViewReservedRegionKind.divisionRegionKind,
|
|
58
|
+
UIViewReservedRegionKind.occlusionRegionKind,
|
|
59
59
|
];
|
|
60
|
-
|
|
61
|
-
for (
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
id kind = ((id (*)(id, SEL))objc_msgSend)(kindClass, kindSelector);
|
|
68
|
-
NSArray *results = ((NSArray *(*)(id, SEL, id, NSUInteger))objc_msgSend)(self, querySelector, kind, 0);
|
|
69
|
-
for (id region in results) {
|
|
70
|
-
if (![region respondsToSelector:@selector(frame)]) {
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
CGRect frame = [[region valueForKey:@"frame"] CGRectValue];
|
|
74
|
-
[regions addObject:@{
|
|
75
|
-
@"kind": entry[@"kind"],
|
|
76
|
-
@"frame": [NSValue valueWithCGRect:frame],
|
|
77
|
-
}];
|
|
60
|
+
NSArray<NSString *> *names = @[@"division", @"occlusion"];
|
|
61
|
+
for (NSUInteger index = 0; index < kinds.count; index++) {
|
|
62
|
+
for (UIViewReservedRegion *region in [self reservedRegionsOfKind:kinds[index]
|
|
63
|
+
options:UIViewReservedRegionQueryOptionsNone]) {
|
|
64
|
+
[regions addObject:@{@"kind": names[index], @"frame": [NSValue valueWithCGRect:region.frame]}];
|
|
78
65
|
}
|
|
79
66
|
}
|
|
80
67
|
}
|
|
68
|
+
#endif
|
|
81
69
|
|
|
82
70
|
if ([_currentRegions isEqualToArray:regions]) {
|
|
83
71
|
return;
|
|
84
72
|
}
|
|
85
73
|
_currentRegions = [regions copy];
|
|
86
74
|
|
|
87
|
-
|
|
75
|
+
auto regionPayloads = folly::dynamic::array();
|
|
88
76
|
for (NSDictionary *region in regions) {
|
|
89
77
|
CGRect frame = [region[@"frame"] CGRectValue];
|
|
90
|
-
|
|
91
|
-
[region[@"kind"] UTF8String]
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
78
|
+
regionPayloads.push_back(folly::dynamic::object
|
|
79
|
+
("kind", [region[@"kind"] UTF8String])
|
|
80
|
+
("frame", folly::dynamic::object
|
|
81
|
+
("x", frame.origin.x)
|
|
82
|
+
("y", frame.origin.y)
|
|
83
|
+
("width", frame.size.width)
|
|
84
|
+
("height", frame.size.height))
|
|
85
|
+
("occludesContent", false));
|
|
95
86
|
}
|
|
96
|
-
|
|
87
|
+
auto eventEmitter = _eventEmitter;
|
|
88
|
+
eventEmitter->experimental_flushSync([eventEmitter, regions = std::move(regionPayloads)]() mutable {
|
|
89
|
+
eventEmitter->dispatchEvent(
|
|
90
|
+
"regionsChange",
|
|
91
|
+
folly::dynamic::object("regions", std::move(regions)),
|
|
92
|
+
RawEvent::Category::Discrete);
|
|
93
|
+
});
|
|
97
94
|
}
|
|
98
95
|
|
|
99
96
|
- (void)prepareForRecycle
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
import { useEffect } from 'react';
|
|
3
4
|
import { View } from 'react-native';
|
|
4
5
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
5
6
|
export function ReservedRegionsView({
|
|
6
|
-
onRegionsChange
|
|
7
|
+
onRegionsChange,
|
|
7
8
|
...props
|
|
8
9
|
}) {
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
onRegionsChange?.({
|
|
12
|
+
nativeEvent: {
|
|
13
|
+
regions: []
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}, [onRegionsChange]);
|
|
9
17
|
return /*#__PURE__*/_jsx(View, {
|
|
10
18
|
...props
|
|
11
19
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["View","jsx","_jsx","ReservedRegionsView","onRegionsChange","
|
|
1
|
+
{"version":3,"names":["useEffect","View","jsx","_jsx","ReservedRegionsView","onRegionsChange","props","nativeEvent","regions"],"sourceRoot":"../../src","sources":["ReservedRegionsView.tsx"],"mappings":";;AAAA,SAASA,SAAS,QAAQ,OAAO;AACjC,SAASC,IAAI,QAAwB,cAAc;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAOpD,OAAO,SAASC,mBAAmBA,CAAC;EAAEC,eAAe;EAAE,GAAGC;AAAgC,CAAC,EAAE;EAC3FN,SAAS,CAAC,MAAM;IACdK,eAAe,GAAG;MAAEE,WAAW,EAAE;QAAEC,OAAO,EAAE;MAAG;IAAE,CAAC,CAAC;EACrD,CAAC,EAAE,CAACH,eAAe,CAAC,CAAC;EACrB,oBAAOF,IAAA,CAACF,IAAI;IAAA,GAAKK;EAAK,CAAG,CAAC;AAC5B","ignoreList":[]}
|
package/lib/module/index.js
CHANGED
|
@@ -14,29 +14,37 @@ export function ReservedRegionsProvider({
|
|
|
14
14
|
children,
|
|
15
15
|
...props
|
|
16
16
|
}) {
|
|
17
|
-
const [
|
|
17
|
+
const [snapshot, setSnapshot] = React.useState({
|
|
18
|
+
regions: [],
|
|
19
|
+
isReady: false
|
|
20
|
+
});
|
|
21
|
+
const onRegionsChange = React.useCallback(event => {
|
|
22
|
+
const regions = event.nativeEvent.regions.flatMap(region => {
|
|
23
|
+
if (region.kind === 'division') {
|
|
24
|
+
return [{
|
|
25
|
+
kind: 'division',
|
|
26
|
+
frame: region.frame,
|
|
27
|
+
occludesContent: region.occludesContent
|
|
28
|
+
}];
|
|
29
|
+
}
|
|
30
|
+
if (region.kind === 'occlusion') {
|
|
31
|
+
return [{
|
|
32
|
+
kind: 'occlusion',
|
|
33
|
+
frame: region.frame
|
|
34
|
+
}];
|
|
35
|
+
}
|
|
36
|
+
return [];
|
|
37
|
+
});
|
|
38
|
+
setSnapshot({
|
|
39
|
+
regions,
|
|
40
|
+
isReady: true
|
|
41
|
+
});
|
|
42
|
+
}, []);
|
|
18
43
|
return /*#__PURE__*/_jsx(ReservedRegionsContext.Provider, {
|
|
19
|
-
value:
|
|
44
|
+
value: snapshot,
|
|
20
45
|
children: /*#__PURE__*/_jsx(ReservedRegionsView, {
|
|
21
46
|
...props,
|
|
22
|
-
onRegionsChange:
|
|
23
|
-
setRegions(event.nativeEvent.regions.flatMap(region => {
|
|
24
|
-
if (region.kind === 'division') {
|
|
25
|
-
return [{
|
|
26
|
-
kind: 'division',
|
|
27
|
-
frame: region.frame,
|
|
28
|
-
occludesContent: region.occludesContent
|
|
29
|
-
}];
|
|
30
|
-
}
|
|
31
|
-
if (region.kind === 'occlusion') {
|
|
32
|
-
return [{
|
|
33
|
-
kind: 'occlusion',
|
|
34
|
-
frame: region.frame
|
|
35
|
-
}];
|
|
36
|
-
}
|
|
37
|
-
return [];
|
|
38
|
-
}));
|
|
39
|
-
},
|
|
47
|
+
onRegionsChange: onRegionsChange,
|
|
40
48
|
children: children
|
|
41
49
|
})
|
|
42
50
|
});
|
|
@@ -44,10 +52,22 @@ export function ReservedRegionsProvider({
|
|
|
44
52
|
|
|
45
53
|
/** Returns active regions reported by the nearest ReservedRegionsProvider. */
|
|
46
54
|
export function useReservedRegions() {
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
55
|
+
const snapshot = React.useContext(ReservedRegionsContext);
|
|
56
|
+
if (snapshot === null) {
|
|
49
57
|
throw new Error('useReservedRegions must be used inside ReservedRegionsProvider');
|
|
50
58
|
}
|
|
51
|
-
return regions;
|
|
59
|
+
return snapshot.regions;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Whether the nearest provider has reported its first measurement, including an empty
|
|
64
|
+
* result. Remains true for that provider's lifetime; does not imply platform support.
|
|
65
|
+
*/
|
|
66
|
+
export function useReservedRegionsReady() {
|
|
67
|
+
const snapshot = React.useContext(ReservedRegionsContext);
|
|
68
|
+
if (snapshot === null) {
|
|
69
|
+
throw new Error('useReservedRegionsReady must be used inside ReservedRegionsProvider');
|
|
70
|
+
}
|
|
71
|
+
return snapshot.isReady;
|
|
52
72
|
}
|
|
53
73
|
//# sourceMappingURL=index.js.map
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","ReservedRegionsView","jsx","_jsx","ReservedRegionsContext","createContext","ReservedRegionsProvider","children","props","
|
|
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"],"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","ignoreList":[]}
|
|
@@ -5,5 +5,5 @@ export type ReservedRegionsViewProps = ViewProps & {
|
|
|
5
5
|
nativeEvent: RegionsChangeEvent;
|
|
6
6
|
}) => void;
|
|
7
7
|
};
|
|
8
|
-
export declare function ReservedRegionsView({ onRegionsChange
|
|
8
|
+
export declare function ReservedRegionsView({ onRegionsChange, ...props }: ReservedRegionsViewProps): import("react").JSX.Element;
|
|
9
9
|
//# sourceMappingURL=ReservedRegionsView.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReservedRegionsView.d.ts","sourceRoot":"","sources":["../../../src/ReservedRegionsView.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ReservedRegionsView.d.ts","sourceRoot":"","sources":["../../../src/ReservedRegionsView.tsx"],"names":[],"mappings":"AACA,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAE/E,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG;IACjD,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,WAAW,EAAE,kBAAkB,CAAA;KAAE,KAAK,IAAI,CAAC;CACxE,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,EAAE,eAAe,EAAE,GAAG,KAAK,EAAE,EAAE,wBAAwB,+BAK1F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReservedRegionsViewNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/ReservedRegionsViewNativeComponent.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"ReservedRegionsViewNativeComponent.d.ts","sourceRoot":"","sources":["../../../src/ReservedRegionsViewNativeComponent.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzF,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE;YACL,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC;YACvB,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC;YACvB,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC;YAC3B,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC;SAC7B,CAAC;QACF,eAAe,EAAE,OAAO,CAAC;KAC1B,EAAE,CAAC;CACL,CAAC,CAAC;AAEH,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,eAAe,CAAC,EAAE,YAAY,CAAC,kBAAkB,CAAC,kBAAkB,CAAC,CAAC;CACvE"}
|
|
@@ -29,4 +29,9 @@ export type ReservedRegion = Readonly<{
|
|
|
29
29
|
export declare function ReservedRegionsProvider({ children, ...props }: ViewProps): React.JSX.Element;
|
|
30
30
|
/** Returns active regions reported by the nearest ReservedRegionsProvider. */
|
|
31
31
|
export declare function useReservedRegions(): readonly ReservedRegion[];
|
|
32
|
+
/**
|
|
33
|
+
* Whether the nearest provider has reported its first measurement, including an empty
|
|
34
|
+
* result. Remains true for that provider's lifetime; does not imply platform support.
|
|
35
|
+
*/
|
|
36
|
+
export declare function useReservedRegionsReady(): boolean;
|
|
32
37
|
//# 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,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"}
|
package/package.json
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-reserved-regions",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.2",
|
|
4
4
|
"description": "Provider-scoped reserved display regions for React Native",
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
"keywords": [
|
|
6
|
+
"android",
|
|
7
|
+
"ios",
|
|
8
|
+
"react-native"
|
|
9
|
+
],
|
|
10
|
+
"homepage": "https://github.com/appandflow/react-native-reserved-regions#readme",
|
|
11
|
+
"bugs": "https://github.com/appandflow/react-native-reserved-regions/issues",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "Janic Duplessis <janicduplessis@gmail.com> (https://github.com/janicduplessis)",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/appandflow/react-native-reserved-regions.git"
|
|
14
17
|
},
|
|
15
18
|
"files": [
|
|
16
19
|
"src",
|
|
@@ -31,80 +34,63 @@
|
|
|
31
34
|
"!**/__mocks__",
|
|
32
35
|
"!**/.*"
|
|
33
36
|
],
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
"main": "./lib/module/index.js",
|
|
38
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
39
|
+
"exports": {
|
|
40
|
+
".": {
|
|
41
|
+
"react-native-reserved-regions-source": "./src/index.tsx",
|
|
42
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
43
|
+
"default": "./lib/module/index.js"
|
|
44
|
+
},
|
|
45
|
+
"./package.json": "./package.json"
|
|
41
46
|
},
|
|
42
|
-
"keywords": [
|
|
43
|
-
"react-native",
|
|
44
|
-
"ios",
|
|
45
|
-
"android"
|
|
46
|
-
],
|
|
47
|
-
"author": "Janic Duplessis <janicduplessis@gmail.com> (https://github.com/janicduplessis)",
|
|
48
|
-
"license": "MIT",
|
|
49
47
|
"publishConfig": {
|
|
50
48
|
"registry": "https://registry.npmjs.org/"
|
|
51
49
|
},
|
|
52
50
|
"devDependencies": {
|
|
53
|
-
"@eslint/compat": "^2.1.0",
|
|
54
|
-
"@eslint/eslintrc": "^3.3.6",
|
|
55
|
-
"@eslint/js": "^9.39.5",
|
|
56
51
|
"@jest/globals": "^29.7.0",
|
|
57
52
|
"@react-native/babel-preset": "0.88.0-rc.1",
|
|
58
|
-
"@react-native/eslint-config": "0.86.2",
|
|
59
53
|
"@react-native/jest-preset": "0.86.2",
|
|
60
54
|
"@types/react": "^19.2.0",
|
|
61
55
|
"@types/react-test-renderer": "^19.1.0",
|
|
62
56
|
"del-cli": "^7.0.0",
|
|
63
|
-
"eslint": "^9.39.5",
|
|
64
|
-
"eslint-config-prettier": "^10.1.8",
|
|
65
|
-
"eslint-plugin-ft-flow": "^3.0.11",
|
|
66
|
-
"eslint-plugin-prettier": "^5.5.6",
|
|
67
57
|
"jest": "^29.7.0",
|
|
68
|
-
"
|
|
58
|
+
"oxfmt": "0.65.0",
|
|
59
|
+
"oxlint": "1.80.0",
|
|
69
60
|
"react": "19.3.0",
|
|
70
61
|
"react-native": "0.88.0-rc.1",
|
|
71
62
|
"react-native-builder-bob": "^0.43.1",
|
|
72
63
|
"react-test-renderer": "19.3.0",
|
|
73
|
-
"
|
|
74
|
-
"typescript": "^6.0.3"
|
|
64
|
+
"typescript": "7.0.2"
|
|
75
65
|
},
|
|
76
66
|
"peerDependencies": {
|
|
77
67
|
"react": "*",
|
|
78
68
|
"react-native": "*"
|
|
79
69
|
},
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
[
|
|
89
|
-
"
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
],
|
|
94
|
-
[
|
|
95
|
-
"typescript",
|
|
96
|
-
{
|
|
97
|
-
"project": "tsconfig.build.json"
|
|
98
|
-
}
|
|
70
|
+
"jest": {
|
|
71
|
+
"modulePathIgnorePatterns": [
|
|
72
|
+
"<rootDir>/example/node_modules",
|
|
73
|
+
"<rootDir>/lib/",
|
|
74
|
+
"<rootDir>/website/"
|
|
75
|
+
],
|
|
76
|
+
"preset": "@react-native/jest-preset",
|
|
77
|
+
"testEnvironmentOptions": {
|
|
78
|
+
"customExportConditions": [
|
|
79
|
+
"require",
|
|
80
|
+
"react-native",
|
|
81
|
+
"react-native-reserved-regions-source"
|
|
99
82
|
]
|
|
100
|
-
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"engines": {
|
|
86
|
+
"node": ">=22.18.0"
|
|
101
87
|
},
|
|
102
88
|
"codegenConfig": {
|
|
103
89
|
"name": "ReservedRegionsViewSpec",
|
|
104
90
|
"type": "all",
|
|
105
91
|
"jsSrcsDir": "src",
|
|
106
92
|
"android": {
|
|
107
|
-
"javaPackageName": "com.reservedregions"
|
|
93
|
+
"javaPackageName": "com.appandflow.reservedregions"
|
|
108
94
|
},
|
|
109
95
|
"ios": {
|
|
110
96
|
"components": {
|
|
@@ -114,27 +100,6 @@
|
|
|
114
100
|
}
|
|
115
101
|
}
|
|
116
102
|
},
|
|
117
|
-
"prettier": {
|
|
118
|
-
"quoteProps": "consistent",
|
|
119
|
-
"singleQuote": true,
|
|
120
|
-
"tabWidth": 2,
|
|
121
|
-
"trailingComma": "es5",
|
|
122
|
-
"useTabs": false
|
|
123
|
-
},
|
|
124
|
-
"jest": {
|
|
125
|
-
"preset": "@react-native/jest-preset",
|
|
126
|
-
"testEnvironmentOptions": {
|
|
127
|
-
"customExportConditions": [
|
|
128
|
-
"require",
|
|
129
|
-
"react-native",
|
|
130
|
-
"react-native-reserved-regions-source"
|
|
131
|
-
]
|
|
132
|
-
},
|
|
133
|
-
"modulePathIgnorePatterns": [
|
|
134
|
-
"<rootDir>/example/node_modules",
|
|
135
|
-
"<rootDir>/lib/"
|
|
136
|
-
]
|
|
137
|
-
},
|
|
138
103
|
"create-react-native-library": {
|
|
139
104
|
"type": "fabric-view",
|
|
140
105
|
"languages": "kotlin-objc",
|
|
@@ -143,5 +108,36 @@
|
|
|
143
108
|
"jest"
|
|
144
109
|
],
|
|
145
110
|
"version": "0.63.1"
|
|
111
|
+
},
|
|
112
|
+
"react-native-builder-bob": {
|
|
113
|
+
"source": "src",
|
|
114
|
+
"output": "lib",
|
|
115
|
+
"targets": [
|
|
116
|
+
[
|
|
117
|
+
"module",
|
|
118
|
+
{
|
|
119
|
+
"esm": true
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
[
|
|
123
|
+
"typescript",
|
|
124
|
+
{
|
|
125
|
+
"project": "tsconfig.build.json"
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
]
|
|
129
|
+
},
|
|
130
|
+
"scripts": {
|
|
131
|
+
"example": "pnpm --filter react-native-reserved-regions-example",
|
|
132
|
+
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
|
|
133
|
+
"typecheck": "tsc --noEmit",
|
|
134
|
+
"lint": "oxlint .",
|
|
135
|
+
"test": "jest --runInBand --watchman=false",
|
|
136
|
+
"build": "bob build",
|
|
137
|
+
"lint:fix": "oxlint --fix .",
|
|
138
|
+
"format": "oxfmt .",
|
|
139
|
+
"format:check": "oxfmt --check .",
|
|
140
|
+
"docs:build": "pnpm --filter reserved-regions-docs build",
|
|
141
|
+
"docs:start": "pnpm --filter reserved-regions-docs start"
|
|
146
142
|
}
|
|
147
|
-
}
|
|
143
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
1
2
|
import { View, type ViewProps } from 'react-native';
|
|
2
3
|
import type { RegionsChangeEvent } from './ReservedRegionsViewNativeComponent';
|
|
3
4
|
|
|
@@ -5,9 +6,9 @@ export type ReservedRegionsViewProps = ViewProps & {
|
|
|
5
6
|
onRegionsChange?: (event: { nativeEvent: RegionsChangeEvent }) => void;
|
|
6
7
|
};
|
|
7
8
|
|
|
8
|
-
export function ReservedRegionsView({
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
9
|
+
export function ReservedRegionsView({ onRegionsChange, ...props }: ReservedRegionsViewProps) {
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
onRegionsChange?.({ nativeEvent: { regions: [] } });
|
|
12
|
+
}, [onRegionsChange]);
|
|
12
13
|
return <View {...props} />;
|
|
13
14
|
}
|
package/src/index.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import type { ViewProps } from 'react-native';
|
|
3
3
|
import { ReservedRegionsView } from './ReservedRegionsView';
|
|
4
|
+
import type { RegionsChangeEvent } from './ReservedRegionsViewNativeComponent';
|
|
4
5
|
|
|
5
6
|
/** A rectangle in logical points relative to the nearest ReservedRegionsProvider. */
|
|
6
7
|
export type ReservedRegionFrame = Readonly<{
|
|
@@ -31,41 +32,32 @@ export type ReservedRegion =
|
|
|
31
32
|
frame: ReservedRegionFrame;
|
|
32
33
|
}>;
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
readonly ReservedRegion[]
|
|
36
|
-
|
|
35
|
+
type ReservedRegionsSnapshot = Readonly<{
|
|
36
|
+
regions: readonly ReservedRegion[];
|
|
37
|
+
isReady: boolean;
|
|
38
|
+
}>;
|
|
39
|
+
|
|
40
|
+
const ReservedRegionsContext = React.createContext<ReservedRegionsSnapshot | null>(null);
|
|
37
41
|
|
|
38
42
|
/** Makes active reserved regions available to descendants in provider coordinates. */
|
|
39
|
-
export function ReservedRegionsProvider({
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
export function ReservedRegionsProvider({ children, ...props }: ViewProps): React.JSX.Element {
|
|
44
|
+
const [snapshot, setSnapshot] = React.useState<ReservedRegionsSnapshot>({ regions: [], isReady: false });
|
|
45
|
+
const onRegionsChange = React.useCallback((event: { nativeEvent: RegionsChangeEvent }) => {
|
|
46
|
+
const regions = event.nativeEvent.regions.flatMap((region): ReservedRegion[] => {
|
|
47
|
+
if (region.kind === 'division') {
|
|
48
|
+
return [{ kind: 'division', frame: region.frame, occludesContent: region.occludesContent }];
|
|
49
|
+
}
|
|
50
|
+
if (region.kind === 'occlusion') {
|
|
51
|
+
return [{ kind: 'occlusion', frame: region.frame }];
|
|
52
|
+
}
|
|
53
|
+
return [];
|
|
54
|
+
});
|
|
55
|
+
setSnapshot({ regions, isReady: true });
|
|
56
|
+
}, []);
|
|
44
57
|
|
|
45
58
|
return (
|
|
46
|
-
<ReservedRegionsContext.Provider value={
|
|
47
|
-
<ReservedRegionsView
|
|
48
|
-
{...props}
|
|
49
|
-
onRegionsChange={(event) => {
|
|
50
|
-
setRegions(
|
|
51
|
-
event.nativeEvent.regions.flatMap((region): ReservedRegion[] => {
|
|
52
|
-
if (region.kind === 'division') {
|
|
53
|
-
return [
|
|
54
|
-
{
|
|
55
|
-
kind: 'division' as const,
|
|
56
|
-
frame: region.frame,
|
|
57
|
-
occludesContent: region.occludesContent,
|
|
58
|
-
},
|
|
59
|
-
];
|
|
60
|
-
}
|
|
61
|
-
if (region.kind === 'occlusion') {
|
|
62
|
-
return [{ kind: 'occlusion' as const, frame: region.frame }];
|
|
63
|
-
}
|
|
64
|
-
return [];
|
|
65
|
-
})
|
|
66
|
-
);
|
|
67
|
-
}}
|
|
68
|
-
>
|
|
59
|
+
<ReservedRegionsContext.Provider value={snapshot}>
|
|
60
|
+
<ReservedRegionsView {...props} onRegionsChange={onRegionsChange}>
|
|
69
61
|
{children}
|
|
70
62
|
</ReservedRegionsView>
|
|
71
63
|
</ReservedRegionsContext.Provider>
|
|
@@ -74,11 +66,21 @@ export function ReservedRegionsProvider({
|
|
|
74
66
|
|
|
75
67
|
/** Returns active regions reported by the nearest ReservedRegionsProvider. */
|
|
76
68
|
export function useReservedRegions(): readonly ReservedRegion[] {
|
|
77
|
-
const
|
|
78
|
-
if (
|
|
79
|
-
throw new Error(
|
|
80
|
-
|
|
81
|
-
|
|
69
|
+
const snapshot = React.useContext(ReservedRegionsContext);
|
|
70
|
+
if (snapshot === null) {
|
|
71
|
+
throw new Error('useReservedRegions must be used inside ReservedRegionsProvider');
|
|
72
|
+
}
|
|
73
|
+
return snapshot.regions;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Whether the nearest provider has reported its first measurement, including an empty
|
|
78
|
+
* result. Remains true for that provider's lifetime; does not imply platform support.
|
|
79
|
+
*/
|
|
80
|
+
export function useReservedRegionsReady(): boolean {
|
|
81
|
+
const snapshot = React.useContext(ReservedRegionsContext);
|
|
82
|
+
if (snapshot === null) {
|
|
83
|
+
throw new Error('useReservedRegionsReady must be used inside ReservedRegionsProvider');
|
|
82
84
|
}
|
|
83
|
-
return
|
|
85
|
+
return snapshot.isReady;
|
|
84
86
|
}
|