@onekeyfe/react-native-native-sheet 3.0.129 → 3.0.130

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.
@@ -0,0 +1,66 @@
1
+ export interface ResolveNativeSheetHeightOptions {
2
+ currentHeight?: number;
3
+ explicitHeight?: number;
4
+ measuredHeight?: number;
5
+ maxHeight: number;
6
+ shouldAutoMeasure: boolean;
7
+ }
8
+
9
+ export interface ResolveNativeSheetLayoutConstraintsOptions {
10
+ lockedHeight?: number;
11
+ maxHeight: number;
12
+ shouldAutoMeasure: boolean;
13
+ }
14
+
15
+ export interface NativeSheetLayoutConstraints {
16
+ hostHeight?: number;
17
+ hostMaxHeight?: number;
18
+ shouldFillHost: boolean;
19
+ }
20
+
21
+ export function resolveNativeSheetLayoutConstraints({
22
+ lockedHeight,
23
+ maxHeight,
24
+ shouldAutoMeasure,
25
+ }: ResolveNativeSheetLayoutConstraintsOptions): NativeSheetLayoutConstraints {
26
+ if (shouldAutoMeasure) {
27
+ return {
28
+ hostMaxHeight: maxHeight,
29
+ shouldFillHost: false,
30
+ };
31
+ }
32
+ if (lockedHeight !== undefined) {
33
+ return {
34
+ hostHeight: lockedHeight,
35
+ shouldFillHost: true,
36
+ };
37
+ }
38
+ return {
39
+ hostMaxHeight: maxHeight,
40
+ shouldFillHost: false,
41
+ };
42
+ }
43
+
44
+ export function resolveNativeSheetHeight({
45
+ currentHeight,
46
+ explicitHeight,
47
+ measuredHeight,
48
+ maxHeight,
49
+ shouldAutoMeasure,
50
+ }: ResolveNativeSheetHeightOptions): number | undefined {
51
+ if (explicitHeight !== undefined && explicitHeight > 0) {
52
+ return Math.min(explicitHeight, maxHeight);
53
+ }
54
+ if (
55
+ explicitHeight === undefined &&
56
+ shouldAutoMeasure &&
57
+ measuredHeight !== undefined &&
58
+ measuredHeight > 0
59
+ ) {
60
+ return Math.min(measuredHeight, maxHeight);
61
+ }
62
+ if (currentHeight !== undefined && currentHeight > maxHeight) {
63
+ return maxHeight;
64
+ }
65
+ return currentHeight;
66
+ }
@@ -0,0 +1,202 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ import type { NativeSheetDismissReason, NativeSheetShowOptions } from './index';
4
+
5
+ export interface NativeSheetRegistryEntry {
6
+ id: number;
7
+ open: boolean;
8
+ presentationRequested: boolean;
9
+ content: ReactNode;
10
+ options: NativeSheetShowOptions;
11
+ }
12
+
13
+ type RegistryListener = () => void;
14
+
15
+ const PRESENTATION_CLOSE_FALLBACK_MS = 5_000;
16
+
17
+ let nextId = 1;
18
+ let entries: readonly NativeSheetRegistryEntry[] = [];
19
+ const listeners = new Set<RegistryListener>();
20
+ let securityFallbackTimer: ReturnType<typeof setTimeout> | undefined;
21
+ const closeFallbackTimers = new Map<number, ReturnType<typeof setTimeout>>();
22
+ let registryBlocked = false;
23
+
24
+ function clearCloseFallbackTimer(id: number) {
25
+ const timer = closeFallbackTimers.get(id);
26
+ if (timer) {
27
+ clearTimeout(timer);
28
+ closeFallbackTimers.delete(id);
29
+ }
30
+ }
31
+
32
+ function emitChange() {
33
+ listeners.forEach((listener) => listener());
34
+ }
35
+
36
+ export function subscribeNativeSheetRegistry(listener: RegistryListener) {
37
+ listeners.add(listener);
38
+ return () => listeners.delete(listener);
39
+ }
40
+
41
+ export function getNativeSheetRegistrySnapshot() {
42
+ return entries;
43
+ }
44
+
45
+ export function addNativeSheetRegistryEntry(
46
+ options: NativeSheetShowOptions,
47
+ content: ReactNode
48
+ ) {
49
+ const id = nextId;
50
+ nextId += 1;
51
+ if (registryBlocked) {
52
+ options.onOpenChange?.(true);
53
+ options.onOpenChange?.(false);
54
+ options.onDismiss?.('security');
55
+ options.onAnimationComplete?.({ open: false });
56
+ return id;
57
+ }
58
+ const entry: NativeSheetRegistryEntry = {
59
+ id,
60
+ open: true,
61
+ presentationRequested: false,
62
+ content,
63
+ options,
64
+ };
65
+ entries = [...entries, entry];
66
+ options.onOpenChange?.(true);
67
+ emitChange();
68
+ return id;
69
+ }
70
+
71
+ export function closeNativeSheetRegistryEntry(id: number) {
72
+ const entry = entries.find((item) => item.id === id);
73
+ if (!entry || !entry.open) {
74
+ return;
75
+ }
76
+ entry.options.onOpenChange?.(false);
77
+ if (!entry.presentationRequested) {
78
+ entries = entries.filter((item) => item.id !== id);
79
+ entry.options.onDismiss?.('programmatic');
80
+ entry.options.onAnimationComplete?.({ open: false });
81
+ } else {
82
+ entries = entries.map((item) =>
83
+ item.id === id ? { ...item, open: false } : item
84
+ );
85
+ if (!closeFallbackTimers.has(id)) {
86
+ closeFallbackTimers.set(
87
+ id,
88
+ setTimeout(() => {
89
+ closeFallbackTimers.delete(id);
90
+ const pendingEntry = entries.find((item) => item.id === id);
91
+ if (!pendingEntry) {
92
+ return;
93
+ }
94
+ finishNativeSheetRegistryEntry(id, 'programmatic');
95
+ pendingEntry.options.onAnimationComplete?.({ open: false });
96
+ }, PRESENTATION_CLOSE_FALLBACK_MS)
97
+ );
98
+ }
99
+ }
100
+ emitChange();
101
+ }
102
+
103
+ export function markNativeSheetRegistryEntryPresentationRequested(id: number) {
104
+ entries = entries.map((entry) =>
105
+ entry.id === id && !entry.presentationRequested
106
+ ? { ...entry, presentationRequested: true }
107
+ : entry
108
+ );
109
+ }
110
+
111
+ export function finishNativeSheetRegistryEntry(
112
+ id: number,
113
+ reason: NativeSheetDismissReason
114
+ ) {
115
+ const entry = entries.find((item) => item.id === id);
116
+ if (!entry) {
117
+ return;
118
+ }
119
+ clearCloseFallbackTimer(id);
120
+ if (entry.open) {
121
+ entry.options.onOpenChange?.(false);
122
+ }
123
+ entries = entries.filter((item) => item.id !== id);
124
+ entry.options.onDismiss?.(reason);
125
+ emitChange();
126
+ }
127
+
128
+ export function finishAllNativeSheetRegistryEntries(
129
+ reason: NativeSheetDismissReason
130
+ ) {
131
+ const currentEntries = entries;
132
+ if (!currentEntries.length) {
133
+ return;
134
+ }
135
+ entries = [];
136
+ currentEntries.forEach((entry) => {
137
+ clearCloseFallbackTimer(entry.id);
138
+ if (entry.open) {
139
+ entry.options.onOpenChange?.(false);
140
+ }
141
+ entry.options.onDismiss?.(reason);
142
+ entry.options.onAnimationComplete?.({ open: false });
143
+ });
144
+ emitChange();
145
+ }
146
+
147
+ export function requestSecurityDismissAllNativeSheets() {
148
+ if (!entries.length) {
149
+ return;
150
+ }
151
+ const unpresentedEntries = entries.filter(
152
+ (entry) => !entry.presentationRequested
153
+ );
154
+ const presentedEntries = entries.filter(
155
+ (entry) => entry.presentationRequested
156
+ );
157
+ unpresentedEntries.forEach((entry) => {
158
+ if (entry.open) {
159
+ entry.options.onOpenChange?.(false);
160
+ }
161
+ entry.options.onDismiss?.('security');
162
+ entry.options.onAnimationComplete?.({ open: false });
163
+ });
164
+ entries = presentedEntries.map((entry) => {
165
+ if (entry.open) {
166
+ entry.options.onOpenChange?.(false);
167
+ return { ...entry, open: false };
168
+ }
169
+ return entry;
170
+ });
171
+ emitChange();
172
+ if (!entries.length) {
173
+ return;
174
+ }
175
+ if (securityFallbackTimer) {
176
+ clearTimeout(securityFallbackTimer);
177
+ }
178
+ securityFallbackTimer = setTimeout(() => {
179
+ securityFallbackTimer = undefined;
180
+ finishAllNativeSheetRegistryEntries('security');
181
+ }, 500);
182
+ }
183
+
184
+ export function setNativeSheetRegistryBlocked(blocked: boolean) {
185
+ registryBlocked = blocked;
186
+ if (blocked) {
187
+ requestSecurityDismissAllNativeSheets();
188
+ }
189
+ }
190
+
191
+ export function resetNativeSheetRegistryForTests() {
192
+ if (securityFallbackTimer) {
193
+ clearTimeout(securityFallbackTimer);
194
+ securityFallbackTimer = undefined;
195
+ }
196
+ closeFallbackTimers.forEach(clearTimeout);
197
+ closeFallbackTimers.clear();
198
+ entries = [];
199
+ nextId = 1;
200
+ registryBlocked = false;
201
+ listeners.clear();
202
+ }
package/src/index.tsx CHANGED
@@ -1,13 +1,20 @@
1
1
  import {
2
2
  createContext,
3
+ type ReactNode,
3
4
  type PropsWithChildren,
4
5
  useCallback,
5
6
  useContext,
7
+ useEffect,
8
+ useMemo,
9
+ useRef,
10
+ useState,
11
+ useSyncExternalStore,
6
12
  } from 'react';
7
13
 
8
14
  import {
9
15
  type ColorValue,
10
16
  type NativeSyntheticEvent,
17
+ type LayoutChangeEvent,
11
18
  StyleSheet,
12
19
  useWindowDimensions,
13
20
  View,
@@ -17,6 +24,19 @@ import RNCNativeSheet, {
17
24
  type NativeSheetDismissEvent,
18
25
  type NativeSheetPresentedEvent,
19
26
  } from './NativeSheetNativeComponent';
27
+ import {
28
+ resolveNativeSheetHeight,
29
+ resolveNativeSheetLayoutConstraints,
30
+ } from './NativeSheetHeight';
31
+ import {
32
+ addNativeSheetRegistryEntry,
33
+ closeNativeSheetRegistryEntry,
34
+ finishNativeSheetRegistryEntry,
35
+ getNativeSheetRegistrySnapshot,
36
+ markNativeSheetRegistryEntryPresentationRequested,
37
+ setNativeSheetRegistryBlocked,
38
+ subscribeNativeSheetRegistry,
39
+ } from './NativeSheetRegistry';
20
40
 
21
41
  export type NativeSheetDismissReason =
22
42
  | 'back'
@@ -30,14 +50,27 @@ export interface NativeSheetProps extends PropsWithChildren {
30
50
  /** Controls whether the sheet is presented. */
31
51
  open: boolean;
32
52
  /**
33
- * Fixed outer height for this presentation, in points/dp. Content updates do
34
- * not change this value, so asynchronously loaded children cannot resize the
35
- * native sheet mid-transition.
53
+ * Outer height in points/dp. Changes while open animate the native surface
54
+ * from its bottom edge. When omitted, settled content-size changes update the
55
+ * native height automatically.
36
56
  */
37
- height: number;
57
+ height?: number;
58
+ /** Maximum auto-fit height. Defaults to 92% of the current window height. */
59
+ maxHeight?: number;
38
60
  onDismiss?: (reason: NativeSheetDismissReason) => void;
39
61
  onPresented?: (height: number) => void;
62
+ onAnimationComplete?: (info: NativeSheetAnimationInfo) => void;
63
+ /** Matches the React Native Sheet callback for controlled open state. */
64
+ onOpenChange?: (open: boolean) => void;
65
+ /** Matches the React Native Sheet drag-dismiss option. */
66
+ dismissOnSnapToBottom?: boolean;
67
+ /** Disables the native drag gesture. */
68
+ disableDrag?: boolean;
69
+ /** @deprecated Use dismissOnSnapToBottom. */
40
70
  dismissOnPanDown?: boolean;
71
+ /** Matches the React Native Sheet overlay-dismiss option. */
72
+ dismissOnOverlayPress?: boolean;
73
+ /** @deprecated Use dismissOnOverlayPress. */
41
74
  dismissOnBackdropPress?: boolean;
42
75
  dismissOnBackPress?: boolean;
43
76
  showHandle?: boolean;
@@ -47,6 +80,31 @@ export interface NativeSheetProps extends PropsWithChildren {
47
80
  testID?: string;
48
81
  }
49
82
 
83
+ export type NativeSheetAnimationInfo = Readonly<{ open: boolean }>;
84
+
85
+ export interface NativeSheetShowControls {
86
+ close: () => void;
87
+ }
88
+
89
+ export interface NativeSheetShowOptions
90
+ extends Omit<
91
+ NativeSheetProps,
92
+ 'children' | 'open' | 'onDismiss' | 'onOpenChange'
93
+ > {
94
+ renderContent: ReactNode | ((controls: NativeSheetShowControls) => ReactNode);
95
+ onOpenChange?: (open: boolean) => void;
96
+ onDismiss?: (reason: NativeSheetDismissReason) => void;
97
+ onAnimationComplete?: (info: NativeSheetAnimationInfo) => void;
98
+ }
99
+
100
+ export interface NativeSheetShowHandle {
101
+ close: () => void;
102
+ }
103
+
104
+ interface NativeSheetInternalProps extends NativeSheetProps {
105
+ onPresentationRequested?: () => void;
106
+ }
107
+
50
108
  const NativeSheetSecurityContext = createContext(false);
51
109
 
52
110
  /**
@@ -58,6 +116,10 @@ export function NativeSheetSecurityProvider({
58
116
  blocked,
59
117
  children,
60
118
  }: PropsWithChildren<{ blocked: boolean }>) {
119
+ useEffect(() => {
120
+ setNativeSheetRegistryBlocked(blocked);
121
+ return () => setNativeSheetRegistryBlocked(false);
122
+ }, [blocked]);
61
123
  return (
62
124
  <NativeSheetSecurityContext.Provider value={blocked}>
63
125
  {children}
@@ -71,45 +133,161 @@ export function NativeSheetSecurityProvider({
71
133
  * Fabric mounts the same child view into the platform sheet without serializing
72
134
  * business content through the native bridge.
73
135
  */
74
- export function NativeSheet({
136
+ function NativeSheetComponent({
75
137
  open,
76
138
  height,
139
+ maxHeight,
77
140
  children,
78
141
  onDismiss,
79
142
  onPresented,
80
- dismissOnPanDown = true,
81
- dismissOnBackdropPress = false,
143
+ onAnimationComplete,
144
+ onOpenChange,
145
+ dismissOnSnapToBottom,
146
+ disableDrag = false,
147
+ dismissOnPanDown,
148
+ dismissOnOverlayPress,
149
+ dismissOnBackdropPress,
82
150
  dismissOnBackPress = true,
83
151
  showHandle = true,
84
152
  cornerRadius = 32,
85
153
  dimAmount = 0.4,
86
154
  backgroundColor,
87
155
  testID,
88
- }: NativeSheetProps) {
156
+ onPresentationRequested,
157
+ }: NativeSheetInternalProps) {
89
158
  const securityBlocked = useContext(NativeSheetSecurityContext);
90
- const { width } = useWindowDimensions();
159
+ const { height: windowHeight, width } = useWindowDimensions();
160
+ const [measurement, setMeasurement] = useState<{
161
+ height: number;
162
+ openCycle: number;
163
+ }>();
164
+ const lockedHeightRef = useRef<number | undefined>(undefined);
165
+ const dismissNotifiedRef = useRef(false);
166
+ const wasOpenRef = useRef(false);
167
+ const openCycleRef = useRef(0);
168
+ const explicitHeightForOpenRef = useRef(false);
169
+ if (open && !wasOpenRef.current) {
170
+ openCycleRef.current += 1;
171
+ dismissNotifiedRef.current = false;
172
+ explicitHeightForOpenRef.current = height !== undefined;
173
+ } else if (open && height !== undefined) {
174
+ explicitHeightForOpenRef.current = true;
175
+ }
176
+ wasOpenRef.current = open;
177
+ const shouldAutoMeasure = !explicitHeightForOpenRef.current;
178
+ const resolvedMaxHeight = maxHeight ?? Math.floor(windowHeight * 0.92);
179
+ if (open) {
180
+ lockedHeightRef.current = resolveNativeSheetHeight({
181
+ currentHeight: lockedHeightRef.current,
182
+ explicitHeight: height,
183
+ measuredHeight:
184
+ measurement?.openCycle === openCycleRef.current
185
+ ? measurement.height
186
+ : undefined,
187
+ maxHeight: resolvedMaxHeight,
188
+ shouldAutoMeasure,
189
+ });
190
+ }
191
+ const lockedHeight = lockedHeightRef.current;
192
+ const resolvedDismissOnPanDown =
193
+ !disableDrag && (dismissOnSnapToBottom ?? dismissOnPanDown ?? true);
194
+ const resolvedDismissOnBackdropPress =
195
+ dismissOnOverlayPress ?? dismissOnBackdropPress ?? false;
196
+ const layoutConstraints = useMemo(
197
+ () =>
198
+ resolveNativeSheetLayoutConstraints({
199
+ lockedHeight,
200
+ maxHeight: resolvedMaxHeight,
201
+ shouldAutoMeasure,
202
+ }),
203
+ [lockedHeight, resolvedMaxHeight, shouldAutoMeasure]
204
+ );
91
205
 
206
+ const notifyDismiss = useCallback(
207
+ (reason: NativeSheetDismissReason) => {
208
+ if (dismissNotifiedRef.current) {
209
+ return;
210
+ }
211
+ dismissNotifiedRef.current = true;
212
+ lockedHeightRef.current = undefined;
213
+ onOpenChange?.(false);
214
+ onDismiss?.(reason);
215
+ onAnimationComplete?.({ open: false });
216
+ },
217
+ [onAnimationComplete, onDismiss, onOpenChange]
218
+ );
92
219
  const handleDismiss = useCallback(
93
220
  (event: NativeSyntheticEvent<NativeSheetDismissEvent>) => {
94
- onDismiss?.(event.nativeEvent.reason as NativeSheetDismissReason);
221
+ notifyDismiss(event.nativeEvent.reason as NativeSheetDismissReason);
95
222
  },
96
- [onDismiss]
223
+ [notifyDismiss]
97
224
  );
225
+ useEffect(() => {
226
+ if (!securityBlocked || !open) {
227
+ return;
228
+ }
229
+ // Native normally acknowledges the no-animation security dismissal. This
230
+ // fallback also covers an open request blocked before native presentation.
231
+ const timer = setTimeout(() => notifyDismiss('security'), 500);
232
+ return () => clearTimeout(timer);
233
+ }, [notifyDismiss, open, securityBlocked]);
234
+ useEffect(() => {
235
+ if (open && lockedHeight && !securityBlocked) {
236
+ onPresentationRequested?.();
237
+ }
238
+ }, [lockedHeight, onPresentationRequested, open, securityBlocked]);
98
239
  const handlePresented = useCallback(
99
240
  (event: NativeSyntheticEvent<NativeSheetPresentedEvent>) => {
100
241
  onPresented?.(event.nativeEvent.height);
242
+ onAnimationComplete?.({ open: true });
101
243
  },
102
- [onPresented]
244
+ [onAnimationComplete, onPresented]
245
+ );
246
+ const handleContentLayout = useCallback(
247
+ (event: LayoutChangeEvent) => {
248
+ if (!open || !shouldAutoMeasure) {
249
+ return;
250
+ }
251
+ const nextHeight = Math.ceil(event.nativeEvent.layout.height);
252
+ if (nextHeight > 0) {
253
+ const nextMeasurement = {
254
+ height: Math.min(nextHeight, resolvedMaxHeight),
255
+ openCycle: openCycleRef.current,
256
+ };
257
+ setMeasurement((currentMeasurement) => {
258
+ if (
259
+ currentMeasurement?.height === nextMeasurement.height &&
260
+ currentMeasurement.openCycle === nextMeasurement.openCycle
261
+ ) {
262
+ return currentMeasurement;
263
+ }
264
+ return nextMeasurement;
265
+ });
266
+ }
267
+ },
268
+ [open, resolvedMaxHeight, shouldAutoMeasure]
269
+ );
270
+ const hostStyle = useMemo(
271
+ () => [
272
+ styles.stagingHost,
273
+ {
274
+ width,
275
+ ...(layoutConstraints.hostHeight !== undefined
276
+ ? { height: layoutConstraints.hostHeight }
277
+ : { maxHeight: layoutConstraints.hostMaxHeight }),
278
+ },
279
+ ],
280
+ [layoutConstraints, width]
103
281
  );
104
282
 
105
283
  return (
106
284
  <RNCNativeSheet
107
285
  testID={testID}
108
- open={open}
109
- sheetHeight={height}
286
+ open={open && Boolean(lockedHeight)}
287
+ sheetHeight={lockedHeight ?? 1}
110
288
  securityBlocked={securityBlocked}
111
- dismissOnPanDown={dismissOnPanDown}
112
- dismissOnBackdropPress={dismissOnBackdropPress}
289
+ dismissOnPanDown={resolvedDismissOnPanDown}
290
+ dismissOnBackdropPress={resolvedDismissOnBackdropPress}
113
291
  dismissOnBackPress={dismissOnBackPress}
114
292
  showHandle={showHandle}
115
293
  cornerRadius={cornerRadius}
@@ -117,15 +295,108 @@ export function NativeSheet({
117
295
  sheetBackgroundColor={backgroundColor}
118
296
  onDismiss={handleDismiss}
119
297
  onPresented={handlePresented}
120
- style={[styles.stagingHost, { width, height }]}
298
+ style={hostStyle}
121
299
  >
122
- <View pointerEvents="auto" style={styles.content} collapsable={false}>
123
- {children}
300
+ <View
301
+ key={`open-cycle-${openCycleRef.current}`}
302
+ pointerEvents="auto"
303
+ style={layoutConstraints.shouldFillHost ? styles.content : undefined}
304
+ collapsable={false}
305
+ >
306
+ {shouldAutoMeasure ? (
307
+ <View
308
+ collapsable={false}
309
+ style={styles.autoMeasureContent}
310
+ onLayout={handleContentLayout}
311
+ >
312
+ {children}
313
+ </View>
314
+ ) : (
315
+ children
316
+ )}
124
317
  </View>
125
318
  </RNCNativeSheet>
126
319
  );
127
320
  }
128
321
 
322
+ function showNativeSheet(
323
+ options: NativeSheetShowOptions
324
+ ): NativeSheetShowHandle {
325
+ let id: number | undefined;
326
+ let closeRequestedBeforeRegistration = false;
327
+ const close = () => {
328
+ if (id === undefined) {
329
+ closeRequestedBeforeRegistration = true;
330
+ return;
331
+ }
332
+ closeNativeSheetRegistryEntry(id);
333
+ };
334
+ const content =
335
+ typeof options.renderContent === 'function'
336
+ ? options.renderContent({ close })
337
+ : options.renderContent;
338
+ id = addNativeSheetRegistryEntry(options, content);
339
+ if (closeRequestedBeforeRegistration) {
340
+ closeNativeSheetRegistryEntry(id);
341
+ }
342
+ return { close };
343
+ }
344
+
345
+ function NativeSheetHostEntry({
346
+ entry,
347
+ }: {
348
+ entry: ReturnType<typeof getNativeSheetRegistrySnapshot>[number];
349
+ }) {
350
+ const handleOpenChange = useCallback(
351
+ (nextOpen: boolean) => {
352
+ if (!nextOpen) {
353
+ closeNativeSheetRegistryEntry(entry.id);
354
+ }
355
+ },
356
+ [entry.id]
357
+ );
358
+ const handleDismiss = useCallback(
359
+ (reason: NativeSheetDismissReason) =>
360
+ finishNativeSheetRegistryEntry(entry.id, reason),
361
+ [entry.id]
362
+ );
363
+ const handlePresentationRequested = useCallback(
364
+ () => markNativeSheetRegistryEntryPresentationRequested(entry.id),
365
+ [entry.id]
366
+ );
367
+ return (
368
+ <NativeSheetComponent
369
+ {...entry.options}
370
+ open={entry.open}
371
+ onOpenChange={handleOpenChange}
372
+ onPresented={entry.options.onPresented}
373
+ onPresentationRequested={handlePresentationRequested}
374
+ onDismiss={handleDismiss}
375
+ >
376
+ {entry.content}
377
+ </NativeSheetComponent>
378
+ );
379
+ }
380
+
381
+ export function NativeSheetHost() {
382
+ const entries = useSyncExternalStore(
383
+ subscribeNativeSheetRegistry,
384
+ getNativeSheetRegistrySnapshot,
385
+ getNativeSheetRegistrySnapshot
386
+ );
387
+ return (
388
+ <>
389
+ {entries.map((entry) => (
390
+ <NativeSheetHostEntry key={entry.id} entry={entry} />
391
+ ))}
392
+ </>
393
+ );
394
+ }
395
+
396
+ export const NativeSheet: ((props: NativeSheetProps) => ReactNode) & {
397
+ show: typeof showNativeSheet;
398
+ } = Object.assign(NativeSheetComponent, { show: showNativeSheet });
399
+
129
400
  const styles = StyleSheet.create({
130
401
  stagingHost: {
131
402
  position: 'absolute',
@@ -136,6 +407,10 @@ const styles = StyleSheet.create({
136
407
  content: {
137
408
  flex: 1,
138
409
  },
410
+ autoMeasureContent: {
411
+ alignSelf: 'stretch',
412
+ flexShrink: 0,
413
+ },
139
414
  });
140
415
 
141
416
  export type {