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

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 (244) hide show
  1. package/README.md +28 -5
  2. package/android/build.gradle +23 -1
  3. package/android/proguard-rules.pro +3 -0
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +50 -16
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +303 -184
  6. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +5 -1
  7. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +38 -109
  8. package/android/src/main/java/com/lodev09/truesheet/TrueSheetModule.kt +13 -0
  9. package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayView.kt +127 -0
  10. package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayViewManager.kt +45 -0
  11. package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +2 -1
  12. package/android/src/main/java/com/lodev09/truesheet/TrueSheetStateUpdater.kt +29 -3
  13. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +234 -66
  14. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +491 -186
  15. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +64 -10
  16. package/android/src/main/java/com/lodev09/truesheet/core/RNScreensEventObserver.kt +31 -14
  17. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetBehavior.kt +135 -0
  18. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetView.kt +23 -23
  19. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +130 -6
  20. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +104 -39
  21. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDimView.kt +14 -17
  22. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetGrabberView.kt +2 -4
  23. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetOverlayRootView.kt +148 -0
  24. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetStackManager.kt +33 -40
  25. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +15 -0
  26. package/android/src/main/java/com/lodev09/truesheet/utils/ColorUtils.kt +19 -0
  27. package/android/src/main/java/com/lodev09/truesheet/utils/KeyboardUtils.kt +15 -2
  28. package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +0 -12
  29. package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +19 -7
  30. package/android/src/main/jni/TrueSheetSpec.h +1 -0
  31. package/android/src/main/jni/TrueSheetStateUpdater.cpp +37 -6
  32. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +1 -9
  33. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +114 -30
  34. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +30 -7
  35. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +2 -2
  36. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +9 -8
  37. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h +3 -5
  38. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +14 -3
  39. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.h +5 -6
  40. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.cpp +1 -1
  41. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.h +10 -6
  42. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.cpp +1 -1
  43. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.h +1 -1
  44. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetLayoutUtils.h +43 -0
  45. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h +21 -0
  46. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.cpp +17 -0
  47. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.h +33 -0
  48. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h +3 -5
  49. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +7 -40
  50. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h +3 -7
  51. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp +3 -5
  52. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h +4 -5
  53. package/ios/TrueSheetContainerView.h +23 -7
  54. package/ios/TrueSheetContainerView.mm +45 -25
  55. package/ios/TrueSheetContentView.h +44 -15
  56. package/ios/TrueSheetContentView.mm +221 -190
  57. package/ios/TrueSheetFooterView.mm +133 -28
  58. package/ios/TrueSheetModule.h +2 -7
  59. package/ios/TrueSheetModule.mm +18 -18
  60. package/ios/TrueSheetOverlayView.h +42 -0
  61. package/ios/TrueSheetOverlayView.mm +175 -0
  62. package/ios/TrueSheetView.h +2 -0
  63. package/ios/TrueSheetView.mm +268 -52
  64. package/ios/TrueSheetViewController.h +36 -3
  65. package/ios/TrueSheetViewController.mm +797 -102
  66. package/ios/core/RNScreensEventObserver.mm +31 -28
  67. package/ios/core/TrueSheetBlurView.h +0 -2
  68. package/ios/core/TrueSheetBlurView.mm +3 -36
  69. package/ios/core/TrueSheetDetentCalculator.h +18 -1
  70. package/ios/core/TrueSheetDetentCalculator.mm +44 -9
  71. package/ios/core/TrueSheetDimView.h +27 -0
  72. package/ios/core/TrueSheetDimView.mm +41 -0
  73. package/ios/core/TrueSheetGrabberView.mm +13 -3
  74. package/ios/core/TrueSheetKeyboardObserver.h +7 -0
  75. package/ios/core/TrueSheetKeyboardObserver.mm +107 -15
  76. package/ios/core/TrueSheetOverlayContainerView.h +31 -0
  77. package/ios/core/TrueSheetOverlayContainerView.mm +35 -0
  78. package/ios/events/TrueSheetLifecycleEvents.h +2 -0
  79. package/ios/events/TrueSheetLifecycleEvents.mm +8 -0
  80. package/ios/tvos/TrueSheetStubs.mm +12 -0
  81. package/ios/utils/UIView+ScrollEdgeInteraction.h +1 -0
  82. package/ios/utils/UIView+ScrollEdgeInteraction.mm +24 -2
  83. package/lib/module/TrueSheet.js +222 -54
  84. package/lib/module/TrueSheet.js.map +1 -1
  85. package/lib/module/TrueSheet.web.js +364 -275
  86. package/lib/module/TrueSheet.web.js.map +1 -1
  87. package/lib/module/TrueSheetBackground.js +15 -0
  88. package/lib/module/TrueSheetBackground.js.map +1 -0
  89. package/lib/module/TrueSheetOverlay.js +30 -0
  90. package/lib/module/TrueSheetOverlay.js.map +1 -0
  91. package/lib/module/TrueSheetOverlay.web.js +35 -0
  92. package/lib/module/TrueSheetOverlay.web.js.map +1 -0
  93. package/lib/module/TrueSheetPeek.web.js +20 -15
  94. package/lib/module/TrueSheetPeek.web.js.map +1 -1
  95. package/lib/module/TrueSheetProvider.web.js +13 -9
  96. package/lib/module/TrueSheetProvider.web.js.map +1 -1
  97. package/lib/module/detentBackgrounds.js +21 -0
  98. package/lib/module/detentBackgrounds.js.map +1 -0
  99. package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  100. package/lib/module/fabric/TrueSheetFooterViewNativeComponent.ts +2 -3
  101. package/lib/module/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
  102. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +89 -73
  103. package/lib/module/index.js +1 -0
  104. package/lib/module/index.js.map +1 -1
  105. package/lib/module/mocks/index.js +18 -2
  106. package/lib/module/mocks/index.js.map +1 -1
  107. package/lib/module/mocks/navigationExpoRouter.js.map +1 -1
  108. package/lib/module/navigation/base-types.js +2 -0
  109. package/lib/module/navigation/base-types.js.map +1 -0
  110. package/lib/module/navigation/createTrueSheetRouter.js +158 -100
  111. package/lib/module/navigation/createTrueSheetRouter.js.map +1 -1
  112. package/lib/module/navigation/expo-router/base-types.js +2 -0
  113. package/lib/module/navigation/expo-router/base-types.js.map +1 -0
  114. package/lib/module/navigation/expo-router/extendTrueSheetRouter.js +71 -0
  115. package/lib/module/navigation/expo-router/extendTrueSheetRouter.js.map +1 -0
  116. package/lib/module/navigation/expo-router/index.js +18 -7
  117. package/lib/module/navigation/expo-router/index.js.map +1 -1
  118. package/lib/module/navigation/expo-router/types.js +4 -0
  119. package/lib/module/navigation/expo-router/types.js.map +1 -0
  120. package/lib/module/navigation/index.js.map +1 -1
  121. package/lib/module/navigation/screen/useSheetScreenState.js +1 -0
  122. package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -1
  123. package/lib/module/scrollable.js +28 -0
  124. package/lib/module/scrollable.js.map +1 -0
  125. package/lib/module/web/constants.js +1 -1
  126. package/lib/module/web/constants.js.map +1 -1
  127. package/lib/module/web/keyboard.js +69 -0
  128. package/lib/module/web/keyboard.js.map +1 -0
  129. package/lib/module/web/layout.js +168 -0
  130. package/lib/module/web/layout.js.map +1 -0
  131. package/lib/module/web/vaul/constants.js +9 -0
  132. package/lib/module/web/vaul/constants.js.map +1 -1
  133. package/lib/module/web/vaul/context.js +4 -0
  134. package/lib/module/web/vaul/context.js.map +1 -1
  135. package/lib/module/web/vaul/helpers.js +1 -0
  136. package/lib/module/web/vaul/helpers.js.map +1 -1
  137. package/lib/module/web/vaul/index.js +83 -21
  138. package/lib/module/web/vaul/index.js.map +1 -1
  139. package/lib/module/web/vaul/use-snap-points.js +57 -28
  140. package/lib/module/web/vaul/use-snap-points.js.map +1 -1
  141. package/lib/typescript/src/TrueSheet.d.ts +18 -1
  142. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  143. package/lib/typescript/src/TrueSheet.types.d.ts +209 -35
  144. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  145. package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
  146. package/lib/typescript/src/TrueSheetBackground.d.ts +3 -0
  147. package/lib/typescript/src/TrueSheetBackground.d.ts.map +1 -0
  148. package/lib/typescript/src/TrueSheetOverlay.d.ts +8 -0
  149. package/lib/typescript/src/TrueSheetOverlay.d.ts.map +1 -0
  150. package/lib/typescript/src/TrueSheetOverlay.web.d.ts +8 -0
  151. package/lib/typescript/src/TrueSheetOverlay.web.d.ts.map +1 -0
  152. package/lib/typescript/src/TrueSheetPeek.web.d.ts +8 -4
  153. package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -1
  154. package/lib/typescript/src/TrueSheetProvider.web.d.ts +5 -1
  155. package/lib/typescript/src/TrueSheetProvider.web.d.ts.map +1 -1
  156. package/lib/typescript/src/detentBackgrounds.d.ts +10 -0
  157. package/lib/typescript/src/detentBackgrounds.d.ts.map +1 -0
  158. package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts +2 -3
  159. package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts.map +1 -1
  160. package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts +6 -0
  161. package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts.map +1 -0
  162. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +76 -65
  163. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
  164. package/lib/typescript/src/index.d.ts +1 -0
  165. package/lib/typescript/src/index.d.ts.map +1 -1
  166. package/lib/typescript/src/mocks/index.d.ts +4 -0
  167. package/lib/typescript/src/mocks/index.d.ts.map +1 -1
  168. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts +3 -3
  169. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts.map +1 -1
  170. package/lib/typescript/src/navigation/base-types.d.ts +9 -0
  171. package/lib/typescript/src/navigation/base-types.d.ts.map +1 -0
  172. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts +36 -10
  173. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts.map +1 -1
  174. package/lib/typescript/src/navigation/expo-router/base-types.d.ts +8 -0
  175. package/lib/typescript/src/navigation/expo-router/base-types.d.ts.map +1 -0
  176. package/lib/typescript/src/navigation/expo-router/extendTrueSheetRouter.d.ts +33 -0
  177. package/lib/typescript/src/navigation/expo-router/extendTrueSheetRouter.d.ts.map +1 -0
  178. package/lib/typescript/src/navigation/expo-router/index.d.ts +9 -6
  179. package/lib/typescript/src/navigation/expo-router/index.d.ts.map +1 -1
  180. package/lib/typescript/src/navigation/expo-router/types.d.ts +44 -0
  181. package/lib/typescript/src/navigation/expo-router/types.d.ts.map +1 -0
  182. package/lib/typescript/src/navigation/index.d.ts +3 -4
  183. package/lib/typescript/src/navigation/index.d.ts.map +1 -1
  184. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +2 -1
  185. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -1
  186. package/lib/typescript/src/navigation/types.d.ts +43 -6
  187. package/lib/typescript/src/navigation/types.d.ts.map +1 -1
  188. package/lib/typescript/src/scrollable.d.ts +2 -0
  189. package/lib/typescript/src/scrollable.d.ts.map +1 -0
  190. package/lib/typescript/src/web/constants.d.ts +1 -1
  191. package/lib/typescript/src/web/constants.d.ts.map +1 -1
  192. package/lib/typescript/src/web/keyboard.d.ts +15 -0
  193. package/lib/typescript/src/web/keyboard.d.ts.map +1 -0
  194. package/lib/typescript/src/web/layout.d.ts +20 -0
  195. package/lib/typescript/src/web/layout.d.ts.map +1 -0
  196. package/lib/typescript/src/web/vaul/constants.d.ts +3 -0
  197. package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -1
  198. package/lib/typescript/src/web/vaul/context.d.ts +5 -1
  199. package/lib/typescript/src/web/vaul/context.d.ts.map +1 -1
  200. package/lib/typescript/src/web/vaul/helpers.d.ts +1 -0
  201. package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -1
  202. package/lib/typescript/src/web/vaul/index.d.ts +20 -1
  203. package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
  204. package/lib/typescript/src/web/vaul/types.d.ts +9 -0
  205. package/lib/typescript/src/web/vaul/types.d.ts.map +1 -1
  206. package/lib/typescript/src/web/vaul/use-snap-points.d.ts +6 -4
  207. package/lib/typescript/src/web/vaul/use-snap-points.d.ts.map +1 -1
  208. package/package.json +18 -8
  209. package/react-native.config.js +1 -0
  210. package/src/TrueSheet.tsx +257 -62
  211. package/src/TrueSheet.types.ts +225 -35
  212. package/src/TrueSheet.web.tsx +445 -333
  213. package/src/TrueSheetBackground.tsx +17 -0
  214. package/src/TrueSheetOverlay.tsx +24 -0
  215. package/src/TrueSheetOverlay.web.tsx +33 -0
  216. package/src/TrueSheetPeek.web.tsx +25 -23
  217. package/src/TrueSheetProvider.web.tsx +22 -13
  218. package/src/detentBackgrounds.ts +24 -0
  219. package/src/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  220. package/src/fabric/TrueSheetFooterViewNativeComponent.ts +2 -3
  221. package/src/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
  222. package/src/fabric/TrueSheetViewNativeComponent.ts +89 -73
  223. package/src/index.ts +1 -0
  224. package/src/mocks/index.ts +17 -2
  225. package/src/mocks/navigationExpoRouter.ts +6 -5
  226. package/src/navigation/base-types.ts +20 -0
  227. package/src/navigation/createTrueSheetRouter.ts +220 -144
  228. package/src/navigation/expo-router/base-types.ts +18 -0
  229. package/src/navigation/expo-router/extendTrueSheetRouter.ts +99 -0
  230. package/src/navigation/expo-router/index.ts +41 -30
  231. package/src/navigation/expo-router/types.ts +116 -0
  232. package/src/navigation/index.ts +6 -7
  233. package/src/navigation/screen/useSheetScreenState.ts +2 -0
  234. package/src/navigation/types.ts +81 -23
  235. package/src/scrollable.ts +25 -0
  236. package/src/web/constants.ts +1 -1
  237. package/src/web/keyboard.ts +79 -0
  238. package/src/web/layout.ts +196 -0
  239. package/src/web/vaul/constants.ts +9 -0
  240. package/src/web/vaul/context.ts +7 -1
  241. package/src/web/vaul/helpers.ts +5 -0
  242. package/src/web/vaul/index.tsx +113 -16
  243. package/src/web/vaul/types.ts +10 -0
  244. package/src/web/vaul/use-snap-points.ts +86 -33
@@ -0,0 +1,17 @@
1
+ import { StyleSheet, View } from 'react-native';
2
+ import type { TrueSheetProps } from './TrueSheet.types';
3
+
4
+ export const TrueSheetBackground = ({
5
+ background,
6
+ backgroundStyle,
7
+ }: Pick<TrueSheetProps, 'background' | 'backgroundStyle'>) => (
8
+ <View
9
+ pointerEvents="none"
10
+ style={[
11
+ StyleSheet.absoluteFill,
12
+ { backgroundColor: StyleSheet.flatten(backgroundStyle)?.backgroundColor },
13
+ ]}
14
+ >
15
+ {background}
16
+ </View>
17
+ );
@@ -0,0 +1,24 @@
1
+ import { StyleSheet, type ViewProps } from 'react-native';
2
+
3
+ import TrueSheetOverlayViewNativeComponent from './fabric/TrueSheetOverlayViewNativeComponent';
4
+
5
+ /**
6
+ * Renders its children in a native layer above every presented sheet — for
7
+ * toasts, dialogs, and other content a sheet must not cover.
8
+ * Fills the window; touches that miss its children pass through to the views beneath.
9
+ */
10
+ export const TrueSheetOverlay = ({ style, ...rest }: ViewProps) => (
11
+ <TrueSheetOverlayViewNativeComponent {...rest} style={[styles.overlay, style]} />
12
+ );
13
+
14
+ const styles = StyleSheet.create({
15
+ // Sized to the window from native — absolute so it takes no layout space.
16
+ // Children render in a native container, so the host itself must never
17
+ // catch touches (Android forces it visible on layout).
18
+ overlay: {
19
+ position: 'absolute',
20
+ top: 0,
21
+ left: 0,
22
+ pointerEvents: 'none',
23
+ },
24
+ });
@@ -0,0 +1,33 @@
1
+ /// <reference lib="dom" />
2
+ import { useLayoutEffect, useState } from 'react';
3
+ import { createPortal } from 'react-dom';
4
+ import { StyleSheet, View, type ViewProps } from 'react-native';
5
+
6
+ /**
7
+ * Renders its children in a layer above every presented sheet — for
8
+ * toasts, dialogs, and other content a sheet must not cover.
9
+ * Fills the window; touches that miss its children pass through to the views beneath.
10
+ */
11
+ export const TrueSheetOverlay = ({ style, ...rest }: ViewProps) => {
12
+ // Portal target appended to the body so it stacks above the sheets' portal
13
+ const [container] = useState(() =>
14
+ typeof document !== 'undefined' ? document.createElement('div') : null
15
+ );
16
+
17
+ useLayoutEffect(() => {
18
+ if (!container) return;
19
+
20
+ container.style.cssText = 'position:fixed;inset:0;pointer-events:none';
21
+ document.body.appendChild(container);
22
+ return () => {
23
+ container.remove();
24
+ };
25
+ }, [container]);
26
+
27
+ if (!container) return null;
28
+
29
+ return createPortal(
30
+ <View pointerEvents="box-none" {...rest} style={[StyleSheet.absoluteFill, style]} />,
31
+ container
32
+ );
33
+ };
@@ -1,16 +1,15 @@
1
- import { createContext, type RefObject, useContext, useEffect, useRef } from 'react';
1
+ import { createContext, type RefObject, useContext, useLayoutEffect, useRef } from 'react';
2
2
  import { type LayoutChangeEvent, View, type ViewProps } from 'react-native';
3
3
 
4
- import { getDOMElement } from './web/dom';
5
-
6
4
  /**
7
- * Reports the measured peek content height to the owning TrueSheet.
5
+ * Registers a peek with the owning TrueSheet so it can measure the peek live.
8
6
  * @internal
9
7
  */
10
8
  export interface TrueSheetPeekContextValue {
11
9
  contentRef: RefObject<View | null>;
12
- peekRef: RefObject<View | null>;
13
- setPeekContentHeight: (height: number) => void;
10
+ attachPeek: (view: View) => void;
11
+ detachPeek: (view: View) => void;
12
+ measurePeek: (view: View) => void;
14
13
  }
15
14
 
16
15
  export const TrueSheetPeekContext = createContext<TrueSheetPeekContextValue | null>(null);
@@ -19,14 +18,19 @@ export const TrueSheetPeekContext = createContext<TrueSheetPeekContextValue | nu
19
18
  * Distance from the top of the content view to the bottom of the peek view,
20
19
  * so the peek view's offset within the content (padding, views above it)
21
20
  * counts toward the peek detent. Both rects live in the drawer's transformed
22
- * subtree, so an in-flight translate cancels out of the delta.
21
+ * subtree, so an in-flight translate cancels out of the delta. Rounded so the
22
+ * sub-pixel noise of a mid-transition measurement doesn't register as a
23
+ * change — the content re-measures every frame while the snap animates, and
24
+ * a new value would restart the snap.
23
25
  * @internal
24
26
  */
25
27
  export const measurePeekContentHeight = (
26
28
  peekElement: HTMLElement,
27
29
  contentElement: HTMLElement
28
30
  ): number =>
29
- peekElement.getBoundingClientRect().bottom - contentElement.getBoundingClientRect().top;
31
+ Math.round(
32
+ peekElement.getBoundingClientRect().bottom - contentElement.getBoundingClientRect().top
33
+ );
30
34
 
31
35
  /**
32
36
  * Wrapper component that marks its children as the sheet's peek content.
@@ -36,25 +40,23 @@ export const measurePeekContentHeight = (
36
40
  */
37
41
  export const TrueSheetPeek = ({ onLayout, ...rest }: ViewProps) => {
38
42
  const context = useContext(TrueSheetPeekContext);
39
- const localRef = useRef<View>(null);
43
+ const viewRef = useRef<View>(null);
40
44
 
41
- // Register into the sheet's peek ref so it can measure the peek live
42
- // during detent geometry computation (state lags the first willPresent).
43
- const viewRef = context?.peekRef ?? localRef;
45
+ // Attach by instance rather than sharing a ref — during a screen swap the
46
+ // incoming peek mounts before the outgoing one unmounts, and a shared ref
47
+ // would be nulled by the outgoing unmount.
48
+ useLayoutEffect(() => {
49
+ const view = viewRef.current;
50
+ if (!context || !view) return;
44
51
 
45
- useEffect(() => () => context?.setPeekContentHeight(0), [context]);
52
+ context.attachPeek(view);
53
+ return () => context.detachPeek(view);
54
+ }, [context]);
46
55
 
47
56
  const handleLayout = (event: LayoutChangeEvent) => {
48
- if (context) {
49
- const peekElement = getDOMElement(viewRef.current);
50
- const contentElement = getDOMElement(context.contentRef.current);
51
-
52
- const bottom =
53
- peekElement && contentElement
54
- ? measurePeekContentHeight(peekElement, contentElement)
55
- : event.nativeEvent.layout.height;
56
-
57
- context.setPeekContentHeight(bottom);
57
+ const view = viewRef.current;
58
+ if (context && view) {
59
+ context.measurePeek(view);
58
60
  }
59
61
 
60
62
  onLayout?.(event);
@@ -13,7 +13,13 @@ import {
13
13
 
14
14
  import type { TrueSheetMethods, TrueSheetStaticMethods } from './TrueSheet.types';
15
15
 
16
- type SheetRef = RefObject<TrueSheetMethods | null>;
16
+ export interface SheetHandle {
17
+ methods: TrueSheetMethods;
18
+ // dismiss() without the already-dismissed warning, for stack-wide dismissals
19
+ dismissQuietly: (animated?: boolean) => Promise<void>;
20
+ }
21
+
22
+ type SheetRef = RefObject<SheetHandle | null>;
17
23
  type NodeRef = RefObject<HTMLDivElement | null>;
18
24
 
19
25
  interface StackEntry {
@@ -23,7 +29,8 @@ interface StackEntry {
23
29
  }
24
30
 
25
31
  interface SheetContextValue {
26
- registerByName: (name: string, ref: SheetRef) => () => void;
32
+ registerSheet: (ref: SheetRef, name?: string) => () => void;
33
+ getSheets: () => ReadonlySet<SheetRef>;
27
34
  resolveByName: (name: string) => TrueSheetMethods;
28
35
  pushOpen: (entry: StackEntry) => void;
29
36
  popOpen: (entry: StackEntry) => void;
@@ -44,6 +51,7 @@ export interface TrueSheetProviderProps {
44
51
  }
45
52
 
46
53
  export function TrueSheetProvider({ children }: TrueSheetProviderProps) {
54
+ const sheetsRef = useRef<Set<SheetRef>>(new Set());
47
55
  const namedSheetsRef = useRef<Map<string, SheetRef>>(new Map());
48
56
  const stackRef = useRef<readonly StackEntry[]>(EMPTY_STACK);
49
57
  const listenersRef = useRef<Set<() => void>>(new Set());
@@ -72,17 +80,20 @@ export function TrueSheetProvider({ children }: TrueSheetProviderProps) {
72
80
  const notify = () => listenersRef.current.forEach((listener) => listener());
73
81
 
74
82
  return {
75
- registerByName: (name, ref) => {
76
- namedSheetsRef.current.set(name, ref);
83
+ registerSheet: (ref, name) => {
84
+ sheetsRef.current.add(ref);
85
+ if (name) namedSheetsRef.current.set(name, ref);
77
86
  return () => {
78
- if (namedSheetsRef.current.get(name) === ref) {
87
+ sheetsRef.current.delete(ref);
88
+ if (name && namedSheetsRef.current.get(name) === ref) {
79
89
  namedSheetsRef.current.delete(name);
80
90
  }
81
91
  };
82
92
  },
93
+ getSheets: () => sheetsRef.current,
83
94
  resolveByName: (name) => {
84
95
  const ref = namedSheetsRef.current.get(name);
85
- const methods = ref?.current;
96
+ const methods = ref?.current?.methods;
86
97
  if (!methods) {
87
98
  throw new Error(`TrueSheet: no sheet registered with name "${name}"`);
88
99
  }
@@ -145,11 +156,9 @@ export function useTrueSheet(): TrueSheetStaticMethods {
145
156
  resize: (name, index) => ctx.resolveByName(name).resize(index),
146
157
  dismiss: (name, animated) => ctx.resolveByName(name).dismiss(animated),
147
158
  dismissStack: (name, animated) => ctx.resolveByName(name).dismissStack(animated),
159
+ // Every sheet, not just the open stack — it misses a present() that hasn't committed yet
148
160
  dismissAll: async (animated) => {
149
- const stack = ctx.getStackSnapshot();
150
- await Promise.all(
151
- [...stack].reverse().map((entry) => entry.ref.current?.dismiss(animated))
152
- );
161
+ await Promise.all([...ctx.getSheets()].map((ref) => ref.current?.dismissQuietly(animated)));
153
162
  },
154
163
  };
155
164
  }, [ctx]);
@@ -158,8 +167,8 @@ export function useTrueSheet(): TrueSheetStaticMethods {
158
167
  export function useRegisterSheet(name: string | undefined, ref: SheetRef): void {
159
168
  const ctx = useContext(SheetContext);
160
169
  useEffect(() => {
161
- if (!ctx || !name) return;
162
- return ctx.registerByName(name, ref);
170
+ if (!ctx) return;
171
+ return ctx.registerSheet(ref, name);
163
172
  }, [ctx, name, ref]);
164
173
  }
165
174
 
@@ -211,7 +220,7 @@ export function useSheetStack(
211
220
  const idx = snapshot.findIndex((e) => e.ref === ref);
212
221
  if (idx < 0) return;
213
222
  const above = snapshot.slice(idx + 1);
214
- await Promise.all([...above].reverse().map((e) => e.ref.current?.dismiss(animated)));
223
+ await Promise.all([...above].reverse().map((e) => e.ref.current?.dismissQuietly(animated)));
215
224
  },
216
225
  [ctx, ref]
217
226
  );
@@ -0,0 +1,24 @@
1
+ import type { DetentBackground } from './TrueSheet.types';
2
+
3
+ export function normalizeDetentBackground(entry: DetentBackground | null | undefined) {
4
+ return typeof entry === 'string' ? { color: entry } : entry;
5
+ }
6
+
7
+ export function getDetentBackgroundIndex(
8
+ height: number,
9
+ detentHeights: readonly number[],
10
+ displayedIndex: number
11
+ ): number {
12
+ if (detentHeights.length === 0) return -1;
13
+
14
+ let nextIndex = Math.max(0, Math.min(displayedIndex, detentHeights.length - 1));
15
+ let distance = Math.abs(height - detentHeights[nextIndex]!);
16
+ for (let index = 0; index < detentHeights.length; index++) {
17
+ const nextDistance = Math.abs(height - detentHeights[index]!);
18
+ if (nextDistance < distance) {
19
+ nextIndex = index;
20
+ distance = nextDistance;
21
+ }
22
+ }
23
+ return nextIndex;
24
+ }
@@ -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
  });
@@ -1,12 +1,11 @@
1
- import type { ViewProps } from 'react-native';
2
- import type { WithDefault } from 'react-native/Libraries/Types/CodegenTypes';
1
+ import type { CodegenTypes, ViewProps } from 'react-native';
3
2
  import { codegenNativeComponent } from 'react-native';
4
3
 
5
4
  export interface NativeProps extends ViewProps {
6
5
  // Seeds the footer's first layout with the sheet's bottom safe-area inset
7
6
  // (insetAdjustment: 'automatic') so a late-set footer doesn't need a second
8
7
  // layout pass — and a visible sheet resize — once native pushes the inset
9
- autoBottomInset?: WithDefault<boolean, false>;
8
+ autoBottomInset?: CodegenTypes.WithDefault<boolean, false>;
10
9
  }
11
10
 
12
11
  // interfaceOnly: shadow node/state/descriptor are custom (common/cpp) so the
@@ -0,0 +1,12 @@
1
+ import type { ViewProps } from 'react-native';
2
+ import { codegenNativeComponent } from 'react-native';
3
+
4
+ export interface NativeProps extends ViewProps {
5
+ // No props needed - sized to the window from native
6
+ }
7
+
8
+ // interfaceOnly: shadow node/state/descriptor are custom (common/cpp) so the
9
+ // overlay is sized to the window
10
+ export default codegenNativeComponent<NativeProps>('TrueSheetOverlayView', {
11
+ interfaceOnly: true,
12
+ });
@@ -1,87 +1,98 @@
1
- import type { ColorValue, ProcessedColorValue, ViewProps } from 'react-native';
2
- import type {
3
- DirectEventHandler,
4
- Double,
5
- Int32,
6
- WithDefault,
7
- } from 'react-native/Libraries/Types/CodegenTypes';
1
+ import type { CodegenTypes, ColorValue, ProcessedColorValue, ViewProps } from 'react-native';
8
2
  import { codegenNativeComponent } from 'react-native';
9
3
 
10
4
  type GrabberOptionsType = Readonly<{
11
- width?: Double;
12
- height?: Double;
13
- topMargin?: Double;
14
- cornerRadius?: WithDefault<Double, -1>;
5
+ width?: CodegenTypes.Double;
6
+ height?: CodegenTypes.Double;
7
+ topMargin?: CodegenTypes.Double;
8
+ cornerRadius?: CodegenTypes.WithDefault<CodegenTypes.Double, -1>;
15
9
  color?: ProcessedColorValue | null;
16
- adaptive?: WithDefault<boolean, true>;
10
+ adaptive?: CodegenTypes.WithDefault<boolean, true>;
17
11
  }>;
18
12
 
19
13
  type AccessibilityOptionsType = Readonly<{
20
- grabberLabel?: WithDefault<string, 'Sheet Grabber'>;
21
- grabberHint?: WithDefault<string, 'Double-tap to expand. Swipe up or down to resize the sheet'>;
22
- expandedValue?: WithDefault<string, 'Expanded'>;
23
- collapsedValue?: WithDefault<string, 'Collapsed'>;
24
- detentValue?: WithDefault<string, 'Detent {index} of {count}'>;
25
- expandActionLabel?: WithDefault<string, 'Expand'>;
26
- collapseActionLabel?: WithDefault<string, 'Collapse'>;
27
- paneTitle?: WithDefault<string, 'Bottom sheet'>;
28
- }>;
29
-
30
- type BlurOptionsType = Readonly<{
31
- intensity?: WithDefault<Double, -1>;
32
- interaction?: WithDefault<boolean, true>;
14
+ grabberLabel?: CodegenTypes.WithDefault<string, 'Sheet Grabber'>;
15
+ grabberHint?: CodegenTypes.WithDefault<
16
+ string,
17
+ 'Double-tap to expand. Swipe up or down to resize the sheet'
18
+ >;
19
+ expandedValue?: CodegenTypes.WithDefault<string, 'Expanded'>;
20
+ collapsedValue?: CodegenTypes.WithDefault<string, 'Collapsed'>;
21
+ detentValue?: CodegenTypes.WithDefault<string, 'Detent {index} of {count}'>;
22
+ expandActionLabel?: CodegenTypes.WithDefault<string, 'Expand'>;
23
+ collapseActionLabel?: CodegenTypes.WithDefault<string, 'Collapse'>;
24
+ paneTitle?: CodegenTypes.WithDefault<string, 'Bottom sheet'>;
33
25
  }>;
34
26
 
35
27
  type ScrollEdgeEffect = 'automatic' | 'hard' | 'soft' | 'hidden';
36
28
 
37
29
  type ScrollableOptionsType = Readonly<{
38
- keyboardScrollOffset?: WithDefault<Double, 0>;
39
- scrollingExpandsSheet?: WithDefault<boolean, true>;
40
- topScrollEdgeEffect?: WithDefault<ScrollEdgeEffect, 'hidden'>;
41
- bottomScrollEdgeEffect?: WithDefault<ScrollEdgeEffect, 'hidden'>;
30
+ contentInsetAdjustment?: CodegenTypes.WithDefault<
31
+ 'automatic' | 'safe-area' | 'footer' | 'never',
32
+ 'automatic'
33
+ >;
34
+ keyboardScrollOffset?: CodegenTypes.WithDefault<CodegenTypes.Double, 0>;
35
+ keyboardOffset?: CodegenTypes.WithDefault<CodegenTypes.Double, 0>;
36
+ scrollingExpandsSheet?: CodegenTypes.WithDefault<boolean, true>;
37
+ topScrollEdgeEffect?: CodegenTypes.WithDefault<ScrollEdgeEffect, 'hidden'>;
38
+ bottomScrollEdgeEffect?: CodegenTypes.WithDefault<ScrollEdgeEffect, 'hidden'>;
42
39
  }>;
43
40
 
44
41
  type FooterOptionsType = Readonly<{
45
42
  // Named uniquely across option structs — codegen derives the enum name from
46
43
  // the field name, so a second `position` would redefine TrueSheetViewPosition
47
- footerPosition?: WithDefault<'relative' | 'absolute', 'relative'>;
48
- keyboardOffset?: WithDefault<Double, 0>;
44
+ footerPosition?: CodegenTypes.WithDefault<'relative' | 'absolute', 'relative'>;
45
+ keyboardOffset?: CodegenTypes.WithDefault<CodegenTypes.Double, 0>;
46
+ avoidKeyboard?: CodegenTypes.WithDefault<boolean, true>;
49
47
  }>;
50
48
 
51
49
  type HeaderOptionsType = Readonly<{
52
- position?: WithDefault<'relative' | 'absolute', 'relative'>;
50
+ position?: CodegenTypes.WithDefault<'relative' | 'absolute', 'relative'>;
51
+ }>;
52
+
53
+ type FoldOptionsType = Readonly<{
54
+ hingeDetentIndex?: CodegenTypes.WithDefault<CodegenTypes.Int32, -1>;
55
+ hingeBehavior?: CodegenTypes.WithDefault<'none' | 'avoid' | 'snap', 'none'>;
53
56
  }>;
54
57
 
55
58
  export interface DetentInfoEventPayload {
56
- index: Int32;
57
- position: Double;
58
- detent: Double;
59
+ index: CodegenTypes.Int32;
60
+ position: CodegenTypes.Double;
61
+ detent: CodegenTypes.Double;
59
62
  }
60
63
 
61
64
  export interface PositionChangeEventPayload {
62
- index: Double;
63
- position: Double;
64
- detent: Double;
65
+ index: CodegenTypes.Double;
66
+ position: CodegenTypes.Double;
67
+ detent: CodegenTypes.Double;
65
68
  realtime: boolean;
66
69
  }
67
70
 
68
71
  export interface NativeProps extends ViewProps {
69
72
  // Array properties
70
- detents?: ReadonlyArray<Double>;
73
+ detents?: ReadonlyArray<CodegenTypes.Double>;
74
+ detentBackgrounds?: ReadonlyArray<
75
+ Readonly<{
76
+ color?: ProcessedColorValue | null;
77
+ blur?: string;
78
+ }>
79
+ >;
71
80
 
72
81
  // Number properties - use 0 as default to avoid nil insertion
73
- maxContentHeight?: WithDefault<Double, 0>;
74
- maxContentWidth?: WithDefault<Double, 0>;
75
- cornerRadius?: WithDefault<Double, -1>;
76
- elevation?: WithDefault<Double, -1>;
82
+ maxContentHeight?: CodegenTypes.WithDefault<CodegenTypes.Double, 0>;
83
+ maxContentWidth?: CodegenTypes.WithDefault<CodegenTypes.Double, 0>;
84
+ cornerRadius?: CodegenTypes.WithDefault<CodegenTypes.Double, -1>;
85
+ elevation?: CodegenTypes.WithDefault<CodegenTypes.Double, -1>;
77
86
 
78
87
  // Color properties
79
88
  backgroundColor?: ColorValue;
80
- initialDetentIndex?: WithDefault<Int32, -1>;
81
- dimmedDetentIndex?: WithDefault<Int32, 0>;
89
+ initialDetentIndex?: CodegenTypes.WithDefault<CodegenTypes.Int32, -1>;
90
+ dimColor?: ColorValue;
91
+ dimOpacity?: CodegenTypes.WithDefault<CodegenTypes.Double, -1>;
92
+ dimmedDetentIndex?: CodegenTypes.WithDefault<CodegenTypes.Int32, 0>;
82
93
 
83
94
  // String properties - use empty string as default to avoid nil insertion
84
- backgroundBlur?: WithDefault<
95
+ backgroundBlur?: CodegenTypes.WithDefault<
85
96
  | 'none'
86
97
  | 'light'
87
98
  | 'dark'
@@ -107,42 +118,47 @@ export interface NativeProps extends ViewProps {
107
118
  'none'
108
119
  >;
109
120
 
110
- anchor?: WithDefault<'left' | 'center' | 'right', 'center'>;
111
- anchorOffset?: WithDefault<Double, 16>;
112
- insetAdjustment?: WithDefault<'automatic' | 'never', 'automatic'>;
113
-
114
- // Blur options
115
- blurOptions?: BlurOptionsType;
121
+ placement?: CodegenTypes.WithDefault<
122
+ 'automatic' | 'leading' | 'center' | 'trailing',
123
+ 'automatic'
124
+ >;
125
+ placementOffset?: CodegenTypes.WithDefault<CodegenTypes.Double, 16>;
126
+ insetAdjustment?: CodegenTypes.WithDefault<'automatic' | 'never', 'automatic'>;
127
+ dismissThreshold?: CodegenTypes.WithDefault<'half' | 'short', 'half'>;
116
128
 
117
129
  // Boolean properties - match defaults from TrueSheet.types.ts
118
- grabber?: WithDefault<boolean, true>;
130
+ grabber?: CodegenTypes.WithDefault<boolean, true>;
119
131
  grabberOptions?: GrabberOptionsType;
120
132
  accessibilityOptions?: AccessibilityOptionsType;
121
- dismissible?: WithDefault<boolean, true>;
122
- draggable?: WithDefault<boolean, true>;
123
- dimmed?: WithDefault<boolean, true>;
124
- initialDetentAnimated?: WithDefault<boolean, true>;
133
+ dismissible?: CodegenTypes.WithDefault<boolean, true>;
134
+ draggable?: CodegenTypes.WithDefault<boolean, true>;
135
+ dimmed?: CodegenTypes.WithDefault<boolean, true>;
136
+ initialDetentAnimated?: CodegenTypes.WithDefault<boolean, true>;
137
+ // React tag of the scrollable component within the content (see scrollableRef)
138
+ scrollableHandle?: CodegenTypes.WithDefault<CodegenTypes.Int32, -1>;
125
139
  scrollableOptions?: ScrollableOptionsType;
126
140
  headerOptions?: HeaderOptionsType;
127
141
  footerOptions?: FooterOptionsType;
128
- presentation?: WithDefault<'page' | 'form', 'page'>;
142
+ foldOptions?: FoldOptionsType;
143
+ presentation?: CodegenTypes.WithDefault<'page' | 'form', 'page'>;
129
144
 
130
145
  // Event handlers
131
- onMount?: DirectEventHandler<null>;
132
- onWillPresent?: DirectEventHandler<DetentInfoEventPayload>;
133
- onDidPresent?: DirectEventHandler<DetentInfoEventPayload>;
134
- onWillDismiss?: DirectEventHandler<null>;
135
- onDidDismiss?: DirectEventHandler<null>;
136
- onDetentChange?: DirectEventHandler<DetentInfoEventPayload>;
137
- onDragBegin?: DirectEventHandler<DetentInfoEventPayload>;
138
- onDragChange?: DirectEventHandler<DetentInfoEventPayload>;
139
- onDragEnd?: DirectEventHandler<DetentInfoEventPayload>;
140
- onPositionChange?: DirectEventHandler<PositionChangeEventPayload>;
141
- onWillFocus?: DirectEventHandler<null>;
142
- onDidFocus?: DirectEventHandler<null>;
143
- onWillBlur?: DirectEventHandler<null>;
144
- onDidBlur?: DirectEventHandler<null>;
145
- onVisibilityChange?: DirectEventHandler<Readonly<{ visible: boolean }>>;
146
+ onMount?: CodegenTypes.DirectEventHandler<null>;
147
+ onWillPresent?: CodegenTypes.DirectEventHandler<DetentInfoEventPayload>;
148
+ onDidPresent?: CodegenTypes.DirectEventHandler<DetentInfoEventPayload>;
149
+ onWillDismiss?: CodegenTypes.DirectEventHandler<null>;
150
+ onDidDismiss?: CodegenTypes.DirectEventHandler<null>;
151
+ onDismissAttempt?: CodegenTypes.DirectEventHandler<null>;
152
+ onDetentChange?: CodegenTypes.DirectEventHandler<DetentInfoEventPayload>;
153
+ onDragBegin?: CodegenTypes.DirectEventHandler<DetentInfoEventPayload>;
154
+ onDragChange?: CodegenTypes.DirectEventHandler<DetentInfoEventPayload>;
155
+ onDragEnd?: CodegenTypes.DirectEventHandler<DetentInfoEventPayload>;
156
+ onPositionChange?: CodegenTypes.DirectEventHandler<PositionChangeEventPayload>;
157
+ onWillFocus?: CodegenTypes.DirectEventHandler<null>;
158
+ onDidFocus?: CodegenTypes.DirectEventHandler<null>;
159
+ onWillBlur?: CodegenTypes.DirectEventHandler<null>;
160
+ onDidBlur?: CodegenTypes.DirectEventHandler<null>;
161
+ onVisibilityChange?: CodegenTypes.DirectEventHandler<Readonly<{ visible: boolean }>>;
146
162
  }
147
163
 
148
164
  export default codegenNativeComponent<NativeProps>('TrueSheetView', {
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './TrueSheet';
2
2
  export * from './TrueSheet.types';
3
3
  export { TrueSheetPeek } from './TrueSheetPeek';
4
+ export { TrueSheetOverlay } from './TrueSheetOverlay';
4
5
  export { TrueSheetProvider, useTrueSheet } from './TrueSheetProvider';
@@ -2,6 +2,7 @@ import React, { createElement, isValidElement, type ReactNode } from 'react';
2
2
  import { View, type ViewProps } from 'react-native';
3
3
 
4
4
  import type { TrueSheetProps, TrueSheetStaticMethods } from '../TrueSheet.types';
5
+ import { TrueSheetBackground } from '../TrueSheetBackground';
5
6
 
6
7
  interface TrueSheetState {
7
8
  shouldRenderNativeView: boolean;
@@ -54,8 +55,15 @@ export class TrueSheet extends React.Component<TrueSheetProps, TrueSheetState> {
54
55
  }
55
56
 
56
57
  render() {
57
- const { children, style } = this.props;
58
- return React.createElement(View, { style }, this.renderHeader(), children, this.renderFooter());
58
+ const { children, style, background, backgroundStyle } = this.props;
59
+ return React.createElement(
60
+ View,
61
+ { style },
62
+ React.createElement(TrueSheetBackground, { background, backgroundStyle }),
63
+ this.renderHeader(),
64
+ children,
65
+ this.renderFooter()
66
+ );
59
67
  }
60
68
  }
61
69
 
@@ -66,6 +74,13 @@ export function TrueSheetPeek({ children, ...rest }: ViewProps) {
66
74
  return React.createElement(View, rest, children);
67
75
  }
68
76
 
77
+ /**
78
+ * Mock TrueSheetOverlay component for testing.
79
+ */
80
+ export function TrueSheetOverlay({ children, ...rest }: ViewProps) {
81
+ return React.createElement(View, rest, children);
82
+ }
83
+
69
84
  /**
70
85
  * Mock TrueSheetProvider for testing.
71
86
  */
@@ -1,6 +1,5 @@
1
- import type { ParamListBase } from '@react-navigation/native';
2
-
3
- import type { TrueSheetNavigationProp } from '../navigation/types';
1
+ import type { ParamListBase } from '../navigation/expo-router/base-types';
2
+ import type { TrueSheetNavigationProp } from '../navigation/expo-router/types';
4
3
  import {
5
4
  TrueSheetActions,
6
5
  useTrueSheetNavigation as useTrueSheetNavigationMock,
@@ -22,8 +21,10 @@ export const Sheet = Object.assign(
22
21
  * Mock useTrueSheetNavigation hook for testing.
23
22
  */
24
23
  export const useTrueSheetNavigation = jest.fn(
24
+ // Double cast: the shared mock is typed against React Navigation, this entry
25
+ // against Expo Router's vendored copy — identical shapes, unrelated declarations
25
26
  <T extends ParamListBase = ParamListBase>(): TrueSheetNavigationProp<T> =>
26
- useTrueSheetNavigationMock() as TrueSheetNavigationProp<T>
27
+ useTrueSheetNavigationMock() as unknown as TrueSheetNavigationProp<T>
27
28
  );
28
29
 
29
30
  export { TrueSheetActions };
@@ -37,4 +38,4 @@ export type {
37
38
  TrueSheetNavigationProp,
38
39
  TrueSheetNavigationState,
39
40
  TrueSheetScreenProps,
40
- } from '../navigation/types';
41
+ } from '../navigation/expo-router/types';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Base navigation types for the React Navigation entry point.
3
+ *
4
+ * The Expo Router entry has its own copy sourced from `expo-router/react-navigation`
5
+ * (see `./expo-router/base-types`) because Expo Router apps never install
6
+ * `@react-navigation/*` — importing it there would silently resolve to `any`.
7
+ */
8
+ export type {
9
+ DefaultNavigatorOptions,
10
+ Descriptor,
11
+ NavigationAction,
12
+ NavigationHelpers,
13
+ NavigationProp,
14
+ NavigationState,
15
+ ParamListBase,
16
+ RouteProp,
17
+ Router,
18
+ StackActionHelpers,
19
+ StackRouterOptions,
20
+ } from '@react-navigation/core';