@lodev09/react-native-true-sheet 4.0.0-beta.3 → 4.0.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +21 -2
  2. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +24 -7
  3. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +195 -174
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +5 -1
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +6 -17
  6. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +50 -8
  7. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +101 -18
  8. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +24 -1
  9. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +3 -3
  10. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +14 -24
  11. package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +0 -7
  12. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +0 -8
  13. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +54 -29
  14. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +6 -1
  15. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +1 -1
  16. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +6 -4
  17. package/ios/TrueSheetContainerView.h +9 -7
  18. package/ios/TrueSheetContainerView.mm +6 -7
  19. package/ios/TrueSheetContentView.h +28 -14
  20. package/ios/TrueSheetContentView.mm +122 -179
  21. package/ios/TrueSheetFooterView.mm +17 -0
  22. package/ios/TrueSheetView.mm +13 -10
  23. package/ios/TrueSheetViewController.mm +35 -27
  24. package/ios/utils/UIView+ScrollEdgeInteraction.h +1 -0
  25. package/ios/utils/UIView+ScrollEdgeInteraction.mm +24 -2
  26. package/lib/module/TrueSheet.js +47 -14
  27. package/lib/module/TrueSheet.js.map +1 -1
  28. package/lib/module/TrueSheet.web.js +26 -24
  29. package/lib/module/TrueSheet.web.js.map +1 -1
  30. package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  31. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +4 -0
  32. package/lib/typescript/src/TrueSheet.d.ts +2 -0
  33. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  34. package/lib/typescript/src/TrueSheet.types.d.ts +64 -1
  35. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  36. package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
  37. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +3 -0
  38. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
  39. package/lib/typescript/src/navigation/types.d.ts +1 -1
  40. package/lib/typescript/src/navigation/types.d.ts.map +1 -1
  41. package/package.json +1 -1
  42. package/src/TrueSheet.tsx +48 -13
  43. package/src/TrueSheet.types.ts +68 -1
  44. package/src/TrueSheet.web.tsx +39 -24
  45. package/src/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  46. package/src/fabric/TrueSheetViewNativeComponent.ts +4 -0
  47. package/src/navigation/types.ts +1 -0
package/src/TrueSheet.tsx CHANGED
@@ -59,16 +59,26 @@ if (!TrueSheetModule) {
59
59
 
60
60
  type NativeRef = ComponentRef<typeof TrueSheetViewNativeComponent>;
61
61
 
62
- // Claim touches that no descendant claimed so the responder negotiation
63
- // doesn't bubble up to touchables outside the sheet
64
- const absorbUnclaimedTouches = () => true;
65
-
66
62
  // Stop raw touch events from bubbling past the sheet to outside ancestors
67
63
  const stopTouchPropagation = (event: GestureResponderEvent) => event.stopPropagation();
68
64
 
65
+ // Stop the responder negotiation at the sheet boundary without claiming the
66
+ // responder. Views inside the sheet are asked before this one, so in-sheet
67
+ // touchables work normally; views outside are asked after, so stopping here
68
+ // keeps unclaimed touches from leaking to touchables behind the sheet.
69
+ // Claiming instead (returning true) would steal the responder from a pressed
70
+ // touchable whenever the negotiation re-runs mid-gesture — e.g. a second
71
+ // finger, or the duplicate touch stream from a nested react-native-screens
72
+ // screen that attaches its own touch handler inside the sheet.
73
+ const stopResponderNegotiation = (event: GestureResponderEvent) => {
74
+ event.stopPropagation();
75
+ return false;
76
+ };
77
+
69
78
  interface TrueSheetState {
70
79
  shouldRenderNativeView: boolean;
71
80
  initialPresentReady: boolean;
81
+ scrollableHandle: number | null;
72
82
  }
73
83
 
74
84
  // Gates auto-present (initialDetentIndex) by one commit. Rendered last in the
@@ -120,13 +130,15 @@ export class TrueSheet
120
130
 
121
131
  this.validateDetents();
122
132
 
123
- // Lazy load by default, except when initialDetentIndex is set (for auto-presentation)
133
+ // Lazy load by default, except when auto-presenting or when lazy is disabled
124
134
  const shouldRenderImmediately =
125
- props.initialDetentIndex !== undefined && props.initialDetentIndex >= 0;
135
+ (props.initialDetentIndex !== undefined && props.initialDetentIndex >= 0) ||
136
+ props.lazy === false;
126
137
 
127
138
  this.state = {
128
139
  shouldRenderNativeView: shouldRenderImmediately,
129
140
  initialPresentReady: false,
141
+ scrollableHandle: null,
130
142
  };
131
143
 
132
144
  this.onMount = this.onMount.bind(this);
@@ -152,6 +164,17 @@ export class TrueSheet
152
164
  this.setState({ initialPresentReady: true });
153
165
  }
154
166
 
167
+ // Resolves after commit (didMount/didUpdate) — the scrollable mounts with the
168
+ // sheet content, so its ref is only populated once the native tree renders.
169
+ private updateScrollableHandle(): void {
170
+ const { scrollableRef } = this.props;
171
+ const scrollableHandle = scrollableRef?.current ? findNodeHandle(scrollableRef.current) : null;
172
+
173
+ if (scrollableHandle !== this.state.scrollableHandle) {
174
+ this.setState({ scrollableHandle });
175
+ }
176
+ }
177
+
155
178
  private validateDetents(): void {
156
179
  const { detents, initialDetentIndex } = this.props;
157
180
 
@@ -328,9 +351,8 @@ export class TrueSheet
328
351
  this.backHandlerSubscription?.remove();
329
352
  this.backHandlerSubscription = null;
330
353
 
331
- // Clean up native view after dismiss for lazy loading.
332
- // Skip unmount if a present is in progress to avoid race condition.
333
- if (!this.isPresenting) {
354
+ // Non-lazy content stays mounted; otherwise clean it up unless another presentation is active.
355
+ if (!this.isPresenting && this.props.lazy !== false) {
334
356
  this.setState({ shouldRenderNativeView: false });
335
357
  }
336
358
 
@@ -455,10 +477,20 @@ export class TrueSheet
455
477
 
456
478
  componentDidMount(): void {
457
479
  this.registerInstance();
480
+ this.updateScrollableHandle();
458
481
  }
459
482
 
460
483
  componentDidUpdate(prevProps: TrueSheetProps): void {
461
484
  this.registerInstance();
485
+ this.updateScrollableHandle();
486
+
487
+ if (
488
+ prevProps.lazy !== false &&
489
+ this.props.lazy === false &&
490
+ !this.state.shouldRenderNativeView
491
+ ) {
492
+ this.setState({ shouldRenderNativeView: true });
493
+ }
462
494
 
463
495
  // Validate when detents prop changes
464
496
  if (prevProps.detents !== this.props.detents) {
@@ -519,6 +551,7 @@ export class TrueSheet
519
551
  insetAdjustment = 'automatic',
520
552
  ...rest
521
553
  } = this.props;
554
+ delete rest.lazy;
522
555
 
523
556
  // Trim to max 3 detents and clamp fractions
524
557
  const resolvedDetents: number[] = detents.slice(0, 3).map((detent) => {
@@ -570,6 +603,7 @@ export class TrueSheet
570
603
  maxContentWidth={maxContentWidth}
571
604
  anchor={anchor}
572
605
  anchorOffset={anchorOffset}
606
+ scrollableHandle={this.state.scrollableHandle ?? -1}
573
607
  scrollableOptions={scrollableOptions}
574
608
  headerOptions={headerOptions}
575
609
  footerOptions={{
@@ -595,10 +629,11 @@ export class TrueSheet
595
629
  onVisibilityChange={this.onVisibilityChange}
596
630
  // These must stay on this host view, not on the container. The container is
597
631
  // reparented into the native sheet, so this view is never a native ancestor of
598
- // the sheet content. Claiming the responder here stops the negotiation at the
599
- // sheet boundary without making an ancestor of the content the JS responder,
600
- // which would block scrolling inside the sheet.
601
- onStartShouldSetResponder={absorbUnclaimedTouches}
632
+ // the sheet content this is the React-tree boundary between the sheet and
633
+ // the presenting screen, where both the responder negotiation and raw touch
634
+ // bubbling must stop.
635
+ onStartShouldSetResponder={stopResponderNegotiation}
636
+ onMoveShouldSetResponder={stopResponderNegotiation}
602
637
  onTouchStart={stopTouchPropagation}
603
638
  onTouchMove={stopTouchPropagation}
604
639
  onTouchEnd={stopTouchPropagation}
@@ -1,4 +1,4 @@
1
- import type { ComponentType, ReactElement } from 'react';
1
+ import type { Component, ComponentType, ReactElement, RefObject } from 'react';
2
2
  import type {
3
3
  ColorValue,
4
4
  NativeSyntheticEvent,
@@ -165,6 +165,28 @@ export type ScrollEdgeEffect = 'automatic' | 'hard' | 'soft' | 'hidden';
165
165
  * Options for scrollable behavior.
166
166
  */
167
167
  export interface ScrollableOptions {
168
+ /**
169
+ * Applies the bottom safe-area inset to the scroll content automatically
170
+ * while the content can scroll, so the last item clears the home indicator
171
+ * (iOS) and navigation bar (Android).
172
+ *
173
+ * - **iOS**: Sets the scroll view's native
174
+ * [`contentInsetAdjustmentBehavior`](https://developer.apple.com/documentation/uikit/uiscrollview/contentinsetadjustmentbehavior)
175
+ * to `automatic` while it's plugged into the sheet — no need to set React
176
+ * Native's iOS-only prop yourself.
177
+ * - **Android**: Pads the scroll content with the bottom window inset,
178
+ * mirroring iOS's `automatic` behavior — parity React Native's iOS-only
179
+ * prop can't provide.
180
+ * - **Web**: Lifts the content above the safe area
181
+ * (`env(safe-area-inset-bottom)`).
182
+ *
183
+ * Only applies while the sheet's `insetAdjustment` is `'automatic'`. When
184
+ * the sheet has a relative footer, the footer absorbs the inset instead.
185
+ *
186
+ * @default true
187
+ */
188
+ contentInsetAdjustmentBehavior?: boolean;
189
+
168
190
  /**
169
191
  * Extra offset when scrolling to the focused input when keyboard appears.
170
192
  *
@@ -172,6 +194,18 @@ export interface ScrollableOptions {
172
194
  */
173
195
  keyboardScrollOffset?: number;
174
196
 
197
+ /**
198
+ * Adjusts the bottom inset applied to the scrollable when the keyboard is shown.
199
+ * Positive values increase the inset; negative values reduce it.
200
+ * Pass `-insets.bottom` to cancel out safe-area padding already included in
201
+ * the content's `paddingBottom`.
202
+ * Does not affect the auto scroll of the focused input — it always targets
203
+ * the keyboard edge.
204
+ *
205
+ * @default 0
206
+ */
207
+ keyboardOffset?: number;
208
+
175
209
  /**
176
210
  * Whether scrolling the content expands the sheet to the next detent.
177
211
  * When `false`, only the grabber can expand the sheet.
@@ -397,6 +431,21 @@ export interface TrueSheetProps extends ViewProps {
397
431
  */
398
432
  initialDetentAnimated?: boolean;
399
433
 
434
+ /**
435
+ * Specify whether the sheet content should mount lazily, on first presentation.
436
+ * Set to `false` to mount the content before presentation without presenting the sheet.
437
+ * Use this when content is not ready on the first render, then call `present()`
438
+ * after your readiness signal so auto detents measure the settled content.
439
+ *
440
+ * Content that requires window attachment to measure, such as SwiftUI-hosted views,
441
+ * still only measures during presentation.
442
+ *
443
+ * @platform android
444
+ * @platform ios
445
+ * @default true
446
+ */
447
+ lazy?: boolean;
448
+
400
449
  /**
401
450
  * The detent index that the sheet should start to dim the background.
402
451
  * This is ignored if `dimmed` is set to `false`.
@@ -568,8 +617,26 @@ export interface TrueSheetProps extends ViewProps {
568
617
  */
569
618
  footerStyle?: StyleProp<ViewStyle>;
570
619
 
620
+ /**
621
+ * A ref to the scrollable component (e.g. `ScrollView`, `FlatList`)
622
+ * rendered within the sheet content.
623
+ * Required for scrollable handling — nested scrolling, keyboard insets,
624
+ * and `auto` detent sizing are wired to this scrollable.
625
+ *
626
+ * Example:
627
+ * ```tsx
628
+ * const scrollableRef = useRef<ScrollView>(null)
629
+ *
630
+ * <TrueSheet scrollableRef={scrollableRef}>
631
+ * <ScrollView ref={scrollableRef} />
632
+ * </TrueSheet>
633
+ * ```
634
+ */
635
+ scrollableRef?: RefObject<Component<unknown> | null>;
636
+
571
637
  /**
572
638
  * Options for scrollable behavior.
639
+ * Applies to the scrollable provided via `scrollableRef`.
573
640
  */
574
641
  scrollableOptions?: ScrollableOptions;
575
642
 
@@ -55,15 +55,15 @@ import { getDOMElement } from './web/dom';
55
55
  import { Drawer } from './web/vaul';
56
56
  import { DEFAULT_PEEK_HEIGHT, TRANSITIONS } from './web/vaul/constants';
57
57
 
58
- // First vertically scrollable descendant — web mirror of native's
59
- // findScrollView (RN-web ScrollView/FlatList render a node with
60
- // `overflow-y: auto`; its first child is the content container).
61
- const findVerticalScroller = (root: HTMLElement): HTMLElement | null => {
62
- for (const el of root.querySelectorAll<HTMLElement>('*')) {
63
- const { overflowY } = window.getComputedStyle(el);
64
- if (overflowY === 'auto' || overflowY === 'scroll') return el;
65
- }
66
- return null;
58
+ // Resolves the user's `scrollableRef` to its scrollable DOM element RN-web
59
+ // ScrollView refs expose the scroll node directly (with helpers attached);
60
+ // list refs (e.g. FlatList) expose it via getScrollableNode(). The node's
61
+ // first child is the content container.
62
+ const getScrollableElement = (scrollable: unknown): HTMLElement | null => {
63
+ if (!scrollable) return null;
64
+ if (scrollable instanceof HTMLElement) return scrollable;
65
+ const node = (scrollable as { getScrollableNode?: () => unknown }).getScrollableNode?.();
66
+ return node instanceof HTMLElement ? node : null;
67
67
  };
68
68
 
69
69
  const TrueSheetComponent = forwardRef<TrueSheetMethods, TrueSheetProps>((props, ref) => {
@@ -79,6 +79,8 @@ const TrueSheetComponent = forwardRef<TrueSheetMethods, TrueSheetProps>((props,
79
79
  maxContentWidth,
80
80
  anchor = 'center',
81
81
  anchorOffset = DEFAULT_ANCHOR_OFFSET,
82
+ scrollableRef,
83
+ scrollableOptions,
82
84
  grabber = true,
83
85
  grabberOptions,
84
86
  accessibilityOptions,
@@ -323,9 +325,9 @@ const TrueSheetComponent = forwardRef<TrueSheetMethods, TrueSheetProps>((props,
323
325
  // `naturalHeight`.
324
326
  const [hasBoundedScrollable, setHasBoundedScrollable] = useState(false);
325
327
  const [scrollableAutoHeight, setScrollableAutoHeight] = useState(0);
326
- const pinnedScrollerRef = useRef<HTMLElement | null>(null);
328
+ const detectedScrollerRef = useRef<HTMLElement | null>(null);
327
329
 
328
- // Natural content height (header + content + footer, with a pinned
330
+ // Natural content height (header + content + footer, with a detected
329
331
  // scrollable's viewport replaced by its content size) — the height the
330
332
  // content wants regardless of the sheet's bounds.
331
333
  const measureNaturalHeight = useCallback(() => {
@@ -335,7 +337,7 @@ const TrueSheetComponent = forwardRef<TrueSheetMethods, TrueSheetProps>((props,
335
337
  const footerEl = absoluteFooterRef.current ? null : getDOMElement(footerElRef.current);
336
338
  let height =
337
339
  (headerEl?.offsetHeight ?? 0) + (footerEl?.offsetHeight ?? 0) + contentEl.offsetHeight;
338
- const scroller = pinnedScrollerRef.current;
340
+ const scroller = detectedScrollerRef.current;
339
341
  const scrollContent = scroller?.firstElementChild;
340
342
  if (scroller?.isConnected && scrollContent instanceof HTMLElement) {
341
343
  height += scrollContent.offsetHeight - scroller.clientHeight;
@@ -369,7 +371,7 @@ const TrueSheetComponent = forwardRef<TrueSheetMethods, TrueSheetProps>((props,
369
371
  scrollContent: Element | null;
370
372
  } | null = null;
371
373
 
372
- // (Re)pin the first vertical scrollable and observe the nodes whose size
374
+ // (Re)resolve the plugged scrollable and observe the nodes whose size
373
375
  // feeds the natural height — content growth inside a bounded scroller is
374
376
  // invisible to the sheet's own layout (mirrors native's contentSize
375
377
  // observation). Resolves the content node live: the layout-branch swap
@@ -379,10 +381,8 @@ const TrueSheetComponent = forwardRef<TrueSheetMethods, TrueSheetProps>((props,
379
381
  const headerEl = getDOMElement(headerElRef.current);
380
382
 
381
383
  // Skip mutations that don't change the observed topology — e.g. a
382
- // virtualized list mounting rows inside the pinned scroller, which
383
- // would otherwise trigger a full-subtree rescan (findVerticalScroller
384
- // resolves computed styles) on every scroll frame. Size changes are
385
- // already covered by the ResizeObserver.
384
+ // virtualized list mounting rows inside the plugged scroller. Size
385
+ // changes are already covered by the ResizeObserver.
386
386
  if (
387
387
  observed &&
388
388
  observed.contentEl === contentEl &&
@@ -394,9 +394,10 @@ const TrueSheetComponent = forwardRef<TrueSheetMethods, TrueSheetProps>((props,
394
394
  return;
395
395
  }
396
396
 
397
- const scroller =
398
- contentEl && contentEl.isConnected ? findVerticalScroller(contentEl) : null;
399
- pinnedScrollerRef.current = scroller;
397
+ const resolved =
398
+ contentEl && contentEl.isConnected ? getScrollableElement(scrollableRef?.current) : null;
399
+ const scroller = resolved?.isConnected ? resolved : null;
400
+ detectedScrollerRef.current = scroller;
400
401
  setHasBoundedScrollable(scroller != null);
401
402
  observed = scroller
402
403
  ? { contentEl, headerEl, scroller, scrollContent: scroller.firstElementChild }
@@ -429,7 +430,7 @@ const TrueSheetComponent = forwardRef<TrueSheetMethods, TrueSheetProps>((props,
429
430
  mutationObserver?.disconnect();
430
431
  resizeObserver?.disconnect();
431
432
  };
432
- }, [isOpen, hasAutoDetent, measureNaturalHeight]);
433
+ }, [isOpen, hasAutoDetent, measureNaturalHeight, scrollableRef]);
433
434
 
434
435
  // Below the last detent a vertical touch pan moves the sheet, not the
435
436
  // content — `[data-vaul-scroll-locked]` disables vertical touch panning on
@@ -1069,12 +1070,25 @@ const TrueSheetComponent = forwardRef<TrueSheetMethods, TrueSheetProps>((props,
1069
1070
  // Lift content above iOS home indicator / bottom safe area when enabled.
1070
1071
  // A relative footer owns the inset instead (see resolvedFooterStyle); an
1071
1072
  // absolute footer floats over the content, so the content keeps its lift.
1073
+ // A plugged scrollable keeps the lift too (contentInsetAdjustmentBehavior,
1074
+ // default on) — disable it to scroll edge-to-edge behind the indicator
1075
+ // with user-applied content padding.
1072
1076
  paddingBottom:
1073
- insetAdjustment === 'automatic' && !(footerOwnsInset && !absoluteFooter)
1077
+ insetAdjustment === 'automatic' &&
1078
+ !(footerOwnsInset && !absoluteFooter) &&
1079
+ (!scrollableRef || (scrollableOptions?.contentInsetAdjustmentBehavior ?? true))
1074
1080
  ? 'env(safe-area-inset-bottom, 0px)'
1075
1081
  : 0,
1076
1082
  }),
1077
- [backgroundColor, effectiveCornerRadius, insetAdjustment, footerOwnsInset, absoluteFooter]
1083
+ [
1084
+ backgroundColor,
1085
+ effectiveCornerRadius,
1086
+ insetAdjustment,
1087
+ footerOwnsInset,
1088
+ absoluteFooter,
1089
+ scrollableRef,
1090
+ scrollableOptions,
1091
+ ]
1078
1092
  );
1079
1093
 
1080
1094
  const defaultGrabberColor =
@@ -1254,7 +1268,8 @@ const TrueSheetComponent = forwardRef<TrueSheetMethods, TrueSheetProps>((props,
1254
1268
  {/* Content lays out naturally like native — fill only for auto
1255
1269
  detents with a plugged scrollable, where natural layout is
1256
1270
  circular (sheet height derives from the scroll content size).
1257
- Mirrors the native shadow node's scrollableBounded behavior. */}
1271
+ Unlike native, CSS won't cap the content to the container
1272
+ (no fit-content flex basis), so the fill is still needed here. */}
1258
1273
  <View
1259
1274
  ref={contentRef}
1260
1275
  style={hasAutoDetent && hasBoundedScrollable ? [contentFillStyle, style] : style}
@@ -6,7 +6,7 @@ export interface NativeProps extends ViewProps {
6
6
  }
7
7
 
8
8
  // interfaceOnly: shadow node/state/descriptor are custom (common/cpp) so the
9
- // content can be bounded to the container when a ScrollView is pinned
9
+ // content can be bounded to the container when a ScrollView is detected
10
10
  export default codegenNativeComponent<NativeProps>('TrueSheetContentView', {
11
11
  interfaceOnly: true,
12
12
  });
@@ -35,7 +35,9 @@ type BlurOptionsType = Readonly<{
35
35
  type ScrollEdgeEffect = 'automatic' | 'hard' | 'soft' | 'hidden';
36
36
 
37
37
  type ScrollableOptionsType = Readonly<{
38
+ contentInsetAdjustmentBehavior?: WithDefault<boolean, true>;
38
39
  keyboardScrollOffset?: WithDefault<Double, 0>;
40
+ keyboardOffset?: WithDefault<Double, 0>;
39
41
  scrollingExpandsSheet?: WithDefault<boolean, true>;
40
42
  topScrollEdgeEffect?: WithDefault<ScrollEdgeEffect, 'hidden'>;
41
43
  bottomScrollEdgeEffect?: WithDefault<ScrollEdgeEffect, 'hidden'>;
@@ -122,6 +124,8 @@ export interface NativeProps extends ViewProps {
122
124
  draggable?: WithDefault<boolean, true>;
123
125
  dimmed?: WithDefault<boolean, true>;
124
126
  initialDetentAnimated?: WithDefault<boolean, true>;
127
+ // React tag of the scrollable component within the content (see scrollableRef)
128
+ scrollableHandle?: WithDefault<Int32, -1>;
125
129
  scrollableOptions?: ScrollableOptionsType;
126
130
  headerOptions?: HeaderOptionsType;
127
131
  footerOptions?: FooterOptionsType;
@@ -176,6 +176,7 @@ export type TrueSheetNavigationSheetProps = Pick<
176
176
  | 'footer'
177
177
  | 'footerStyle'
178
178
  | 'footerOptions'
179
+ | 'scrollableRef'
179
180
  | 'scrollableOptions'
180
181
  | 'insetAdjustment'
181
182
  | 'anchor'