@lodev09/react-native-true-sheet 4.0.0-beta.1 → 4.0.0-beta.10

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 (221) hide show
  1. package/README.md +25 -4
  2. package/android/build.gradle +22 -1
  3. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +24 -7
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +211 -182
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +5 -1
  6. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +14 -19
  7. package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayView.kt +106 -0
  8. package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayViewManager.kt +45 -0
  9. package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +2 -1
  10. package/android/src/main/java/com/lodev09/truesheet/TrueSheetStateUpdater.kt +18 -0
  11. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +82 -39
  12. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +275 -88
  13. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +26 -1
  14. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +75 -3
  15. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +56 -34
  16. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetOverlayRootView.kt +137 -0
  17. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +15 -0
  18. package/android/src/main/java/com/lodev09/truesheet/utils/KeyboardUtils.kt +1 -1
  19. package/android/src/main/java/com/lodev09/truesheet/utils/TouchEventDeduper.kt +31 -0
  20. package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +19 -7
  21. package/android/src/main/jni/TrueSheetSpec.h +1 -0
  22. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +0 -8
  23. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +54 -29
  24. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +6 -1
  25. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +1 -1
  26. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +6 -4
  27. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +7 -0
  28. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetLayoutUtils.h +46 -0
  29. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h +21 -0
  30. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.cpp +17 -0
  31. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.h +36 -0
  32. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +4 -35
  33. package/ios/TrueSheetContainerView.h +14 -7
  34. package/ios/TrueSheetContainerView.mm +10 -7
  35. package/ios/TrueSheetContentView.h +31 -15
  36. package/ios/TrueSheetContentView.mm +133 -179
  37. package/ios/TrueSheetFooterView.h +6 -0
  38. package/ios/TrueSheetFooterView.mm +48 -2
  39. package/ios/TrueSheetModule.h +2 -7
  40. package/ios/TrueSheetModule.mm +7 -16
  41. package/ios/TrueSheetOverlayView.h +32 -0
  42. package/ios/TrueSheetOverlayView.mm +161 -0
  43. package/ios/TrueSheetView.mm +171 -29
  44. package/ios/TrueSheetViewController.h +10 -0
  45. package/ios/TrueSheetViewController.mm +256 -74
  46. package/ios/core/TrueSheetGrabberView.mm +13 -3
  47. package/ios/core/TrueSheetOverlayContainerView.h +31 -0
  48. package/ios/core/TrueSheetOverlayContainerView.mm +35 -0
  49. package/ios/events/TrueSheetLifecycleEvents.h +2 -0
  50. package/ios/events/TrueSheetLifecycleEvents.mm +8 -0
  51. package/ios/tvos/TrueSheetStubs.mm +12 -0
  52. package/ios/utils/UIView+ScrollEdgeInteraction.h +1 -0
  53. package/ios/utils/UIView+ScrollEdgeInteraction.mm +24 -2
  54. package/lib/module/TrueSheet.js +149 -34
  55. package/lib/module/TrueSheet.js.map +1 -1
  56. package/lib/module/TrueSheet.web.js +122 -53
  57. package/lib/module/TrueSheet.web.js.map +1 -1
  58. package/lib/module/TrueSheetOverlay.js +30 -0
  59. package/lib/module/TrueSheetOverlay.js.map +1 -0
  60. package/lib/module/TrueSheetOverlay.web.js +35 -0
  61. package/lib/module/TrueSheetOverlay.web.js.map +1 -0
  62. package/lib/module/TrueSheetPeek.web.js +3 -3
  63. package/lib/module/TrueSheetPeek.web.js.map +1 -1
  64. package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  65. package/lib/module/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
  66. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +5 -0
  67. package/lib/module/index.js +1 -0
  68. package/lib/module/index.js.map +1 -1
  69. package/lib/module/mocks/index.js +10 -0
  70. package/lib/module/mocks/index.js.map +1 -1
  71. package/lib/module/mocks/navigation.js +31 -21
  72. package/lib/module/mocks/navigation.js.map +1 -1
  73. package/lib/module/mocks/navigationExpoRouter.js +21 -0
  74. package/lib/module/mocks/navigationExpoRouter.js.map +1 -0
  75. package/lib/module/mocks/reanimated.js +1 -1
  76. package/lib/module/mocks/reanimated.js.map +1 -1
  77. package/lib/module/navigation/{TrueSheetView.js → TrueSheetNavigatorContent.js} +14 -12
  78. package/lib/module/navigation/TrueSheetNavigatorContent.js.map +1 -0
  79. package/lib/module/navigation/actions.js +50 -0
  80. package/lib/module/navigation/actions.js.map +1 -0
  81. package/lib/module/navigation/base-types.js +2 -0
  82. package/lib/module/navigation/base-types.js.map +1 -0
  83. package/lib/module/navigation/{TrueSheetRouter.js → createTrueSheetRouter.js} +11 -24
  84. package/lib/module/navigation/createTrueSheetRouter.js.map +1 -0
  85. package/lib/module/navigation/expo-router/base-types.js +2 -0
  86. package/lib/module/navigation/expo-router/base-types.js.map +1 -0
  87. package/lib/module/navigation/expo-router/index.js +50 -0
  88. package/lib/module/navigation/expo-router/index.js.map +1 -0
  89. package/lib/module/navigation/expo-router/types.js +4 -0
  90. package/lib/module/navigation/expo-router/types.js.map +1 -0
  91. package/lib/module/navigation/index.js +43 -2
  92. package/lib/module/navigation/index.js.map +1 -1
  93. package/lib/module/navigation/navigator.js +10 -0
  94. package/lib/module/navigation/navigator.js.map +1 -0
  95. package/lib/module/navigation/screen/ReanimatedTrueSheetScreen.js +2 -2
  96. package/lib/module/navigation/screen/ReanimatedTrueSheetScreen.js.map +1 -1
  97. package/lib/module/navigation/screen/TrueSheetScreen.js +2 -2
  98. package/lib/module/navigation/screen/TrueSheetScreen.js.map +1 -1
  99. package/lib/module/navigation/screen/useSheetScreenState.js +5 -4
  100. package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -1
  101. package/lib/module/navigation/useTrueSheetNavigation.js +1 -1
  102. package/lib/module/navigation/useTrueSheetNavigation.js.map +1 -1
  103. package/lib/module/web/dom.js +10 -0
  104. package/lib/module/web/dom.js.map +1 -0
  105. package/lib/module/web/vaul/constants.js +4 -0
  106. package/lib/module/web/vaul/constants.js.map +1 -1
  107. package/lib/module/web/vaul/context.js.map +1 -1
  108. package/lib/module/web/vaul/helpers.js +10 -4
  109. package/lib/module/web/vaul/helpers.js.map +1 -1
  110. package/lib/module/web/vaul/index.js +35 -2
  111. package/lib/module/web/vaul/index.js.map +1 -1
  112. package/lib/typescript/src/TrueSheet.d.ts +6 -0
  113. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  114. package/lib/typescript/src/TrueSheet.types.d.ts +72 -1
  115. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  116. package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
  117. package/lib/typescript/src/TrueSheetOverlay.d.ts +8 -0
  118. package/lib/typescript/src/TrueSheetOverlay.d.ts.map +1 -0
  119. package/lib/typescript/src/TrueSheetOverlay.web.d.ts +8 -0
  120. package/lib/typescript/src/TrueSheetOverlay.web.d.ts.map +1 -0
  121. package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -1
  122. package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts +6 -0
  123. package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts.map +1 -0
  124. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +4 -0
  125. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
  126. package/lib/typescript/src/index.d.ts +1 -0
  127. package/lib/typescript/src/index.d.ts.map +1 -1
  128. package/lib/typescript/src/mocks/index.d.ts +4 -0
  129. package/lib/typescript/src/mocks/index.d.ts.map +1 -1
  130. package/lib/typescript/src/mocks/navigation.d.ts +5 -1
  131. package/lib/typescript/src/mocks/navigation.d.ts.map +1 -1
  132. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts +22 -0
  133. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts.map +1 -0
  134. package/lib/typescript/src/mocks/reanimated.d.ts.map +1 -1
  135. package/lib/typescript/src/navigation/TrueSheetNavigatorContent.d.ts +5 -0
  136. package/lib/typescript/src/navigation/TrueSheetNavigatorContent.d.ts.map +1 -0
  137. package/lib/typescript/src/navigation/actions.d.ts +71 -0
  138. package/lib/typescript/src/navigation/actions.d.ts.map +1 -0
  139. package/lib/typescript/src/navigation/base-types.d.ts +9 -0
  140. package/lib/typescript/src/navigation/base-types.d.ts.map +1 -0
  141. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts +3 -0
  142. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts.map +1 -0
  143. package/lib/typescript/src/navigation/expo-router/base-types.d.ts +8 -0
  144. package/lib/typescript/src/navigation/expo-router/base-types.d.ts.map +1 -0
  145. package/lib/typescript/src/navigation/expo-router/index.d.ts +37 -0
  146. package/lib/typescript/src/navigation/expo-router/index.d.ts.map +1 -0
  147. package/lib/typescript/src/navigation/expo-router/types.d.ts +44 -0
  148. package/lib/typescript/src/navigation/expo-router/types.d.ts.map +1 -0
  149. package/lib/typescript/src/navigation/index.d.ts +42 -3
  150. package/lib/typescript/src/navigation/index.d.ts.map +1 -1
  151. package/lib/typescript/src/navigation/navigator.d.ts +7 -0
  152. package/lib/typescript/src/navigation/navigator.d.ts.map +1 -0
  153. package/lib/typescript/src/navigation/screen/ReanimatedTrueSheetScreen.d.ts +1 -1
  154. package/lib/typescript/src/navigation/screen/ReanimatedTrueSheetScreen.d.ts.map +1 -1
  155. package/lib/typescript/src/navigation/screen/TrueSheetScreen.d.ts +1 -1
  156. package/lib/typescript/src/navigation/screen/TrueSheetScreen.d.ts.map +1 -1
  157. package/lib/typescript/src/navigation/screen/types.d.ts +3 -4
  158. package/lib/typescript/src/navigation/screen/types.d.ts.map +1 -1
  159. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +5 -6
  160. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -1
  161. package/lib/typescript/src/navigation/types.d.ts +65 -5
  162. package/lib/typescript/src/navigation/types.d.ts.map +1 -1
  163. package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts +1 -1
  164. package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts.map +1 -1
  165. package/lib/typescript/src/web/dom.d.ts +7 -0
  166. package/lib/typescript/src/web/dom.d.ts.map +1 -0
  167. package/lib/typescript/src/web/vaul/constants.d.ts +1 -0
  168. package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -1
  169. package/lib/typescript/src/web/vaul/context.d.ts +1 -0
  170. package/lib/typescript/src/web/vaul/context.d.ts.map +1 -1
  171. package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -1
  172. package/lib/typescript/src/web/vaul/index.d.ts +7 -1
  173. package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
  174. package/package.json +32 -9
  175. package/react-native.config.js +1 -0
  176. package/src/TrueSheet.tsx +184 -46
  177. package/src/TrueSheet.types.ts +77 -1
  178. package/src/TrueSheet.web.tsx +135 -64
  179. package/src/TrueSheetOverlay.tsx +24 -0
  180. package/src/TrueSheetOverlay.web.tsx +33 -0
  181. package/src/TrueSheetPeek.web.tsx +4 -3
  182. package/src/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  183. package/src/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
  184. package/src/fabric/TrueSheetViewNativeComponent.ts +5 -0
  185. package/src/index.ts +1 -0
  186. package/src/mocks/index.ts +7 -0
  187. package/src/mocks/navigation.ts +32 -25
  188. package/src/mocks/navigationExpoRouter.ts +41 -0
  189. package/src/mocks/reanimated.ts +8 -9
  190. package/src/navigation/{TrueSheetView.tsx → TrueSheetNavigatorContent.tsx} +24 -17
  191. package/src/navigation/actions.ts +78 -0
  192. package/src/navigation/base-types.ts +20 -0
  193. package/src/navigation/createTrueSheetRouter.ts +194 -0
  194. package/src/navigation/expo-router/base-types.ts +18 -0
  195. package/src/navigation/expo-router/index.ts +90 -0
  196. package/src/navigation/expo-router/types.ts +116 -0
  197. package/src/navigation/index.ts +68 -2
  198. package/src/navigation/navigator.ts +18 -0
  199. package/src/navigation/screen/ReanimatedTrueSheetScreen.tsx +2 -2
  200. package/src/navigation/screen/TrueSheetScreen.tsx +2 -2
  201. package/src/navigation/screen/types.ts +4 -6
  202. package/src/navigation/screen/useSheetScreenState.ts +10 -15
  203. package/src/navigation/types.ts +99 -16
  204. package/src/navigation/useTrueSheetNavigation.ts +1 -1
  205. package/src/web/dom.ts +9 -0
  206. package/src/web/vaul/constants.ts +4 -0
  207. package/src/web/vaul/context.ts +1 -0
  208. package/src/web/vaul/helpers.ts +13 -6
  209. package/src/web/vaul/index.tsx +46 -1
  210. package/lib/module/navigation/TrueSheetRouter.js.map +0 -1
  211. package/lib/module/navigation/TrueSheetView.js.map +0 -1
  212. package/lib/module/navigation/createTrueSheetNavigator.js +0 -63
  213. package/lib/module/navigation/createTrueSheetNavigator.js.map +0 -1
  214. package/lib/typescript/src/navigation/TrueSheetRouter.d.ts +0 -57
  215. package/lib/typescript/src/navigation/TrueSheetRouter.d.ts.map +0 -1
  216. package/lib/typescript/src/navigation/TrueSheetView.d.ts +0 -10
  217. package/lib/typescript/src/navigation/TrueSheetView.d.ts.map +0 -1
  218. package/lib/typescript/src/navigation/createTrueSheetNavigator.d.ts +0 -35
  219. package/lib/typescript/src/navigation/createTrueSheetNavigator.d.ts.map +0 -1
  220. package/src/navigation/TrueSheetRouter.ts +0 -234
  221. package/src/navigation/createTrueSheetNavigator.tsx +0 -93
@@ -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;
@@ -133,6 +137,7 @@ export interface NativeProps extends ViewProps {
133
137
  onDidPresent?: DirectEventHandler<DetentInfoEventPayload>;
134
138
  onWillDismiss?: DirectEventHandler<null>;
135
139
  onDidDismiss?: DirectEventHandler<null>;
140
+ onDismissAttempt?: DirectEventHandler<null>;
136
141
  onDetentChange?: DirectEventHandler<DetentInfoEventPayload>;
137
142
  onDragBegin?: DirectEventHandler<DetentInfoEventPayload>;
138
143
  onDragChange?: DirectEventHandler<DetentInfoEventPayload>;
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';
@@ -66,6 +66,13 @@ export function TrueSheetPeek({ children, ...rest }: ViewProps) {
66
66
  return React.createElement(View, rest, children);
67
67
  }
68
68
 
69
+ /**
70
+ * Mock TrueSheetOverlay component for testing.
71
+ */
72
+ export function TrueSheetOverlay({ children, ...rest }: ViewProps) {
73
+ return React.createElement(View, rest, children);
74
+ }
75
+
69
76
  /**
70
77
  * Mock TrueSheetProvider for testing.
71
78
  */
@@ -12,6 +12,11 @@ export const createTrueSheetNavigator = jest.fn(() => ({
12
12
  Group: jest.fn(() => null),
13
13
  }));
14
14
 
15
+ /**
16
+ * Mock createTrueSheetScreen for testing the static API.
17
+ */
18
+ export const createTrueSheetScreen = jest.fn((config: object) => config);
19
+
15
20
  /**
16
21
  * Mock TrueSheetActions for testing.
17
22
  */
@@ -35,32 +40,34 @@ export const TrueSheetActions = {
35
40
  /**
36
41
  * Mock useTrueSheetNavigation hook for testing.
37
42
  */
38
- export const useTrueSheetNavigation = jest.fn(
39
- <T extends ParamListBase = ParamListBase>() =>
40
- ({
41
- navigate: jest.fn(),
42
- goBack: jest.fn(),
43
- reset: jest.fn(),
44
- setParams: jest.fn(),
45
- dispatch: jest.fn(),
46
- isFocused: jest.fn(() => true),
47
- canGoBack: jest.fn(() => true),
48
- getId: jest.fn(),
49
- getParent: jest.fn(),
50
- getState: jest.fn(),
51
- addListener: jest.fn(() => jest.fn()),
52
- removeListener: jest.fn(),
53
- setOptions: jest.fn(),
54
- push: jest.fn(),
55
- pop: jest.fn(),
56
- popTo: jest.fn(),
57
- popToTop: jest.fn(),
58
- replace: jest.fn(),
59
- resize: jest.fn(),
60
- }) as unknown as TrueSheetNavigationProp<T>
61
- );
43
+ export const useTrueSheetNavigation = jest.fn(<T extends ParamListBase = ParamListBase>() => {
44
+ // Partial drops react-navigation's private-brand member (PrivateValueStore),
45
+ // which no mock can implement — the members themselves stay type-checked.
46
+ const navigation: Partial<TrueSheetNavigationProp<T>> = {
47
+ navigate: jest.fn(),
48
+ goBack: jest.fn(),
49
+ reset: jest.fn(),
50
+ setParams: jest.fn(),
51
+ dispatch: jest.fn(),
52
+ isFocused: jest.fn(() => true),
53
+ canGoBack: jest.fn(() => true),
54
+ getId: jest.fn(),
55
+ getParent: jest.fn(),
56
+ getState: jest.fn(),
57
+ addListener: jest.fn(() => jest.fn()),
58
+ removeListener: jest.fn(),
59
+ setOptions: jest.fn(),
60
+ push: jest.fn(),
61
+ pop: jest.fn(),
62
+ popTo: jest.fn(),
63
+ popToTop: jest.fn(),
64
+ replace: jest.fn(),
65
+ resize: jest.fn(),
66
+ };
67
+ return navigation as TrueSheetNavigationProp<T>;
68
+ });
62
69
 
63
- export type { TrueSheetActionType } from '../navigation/TrueSheetRouter';
70
+ export type { TrueSheetActionType } from '../navigation/actions';
64
71
  export type { DetentInfoEventPayload, PositionChangeEventPayload } from '../TrueSheet.types';
65
72
  export type {
66
73
  TrueSheetNavigationEventMap,
@@ -0,0 +1,41 @@
1
+ import type { ParamListBase } from '../navigation/expo-router/base-types';
2
+ import type { TrueSheetNavigationProp } from '../navigation/expo-router/types';
3
+ import {
4
+ TrueSheetActions,
5
+ useTrueSheetNavigation as useTrueSheetNavigationMock,
6
+ } from './navigation';
7
+
8
+ /**
9
+ * Mock Sheet layout for testing.
10
+ * Import from '@lodev09/react-native-true-sheet/navigation/expo-router/mock' in your test setup.
11
+ */
12
+ export const Sheet = Object.assign(
13
+ jest.fn(({ children }: { children: React.ReactNode }) => children),
14
+ {
15
+ Screen: jest.fn(() => null),
16
+ Protected: jest.fn(() => null),
17
+ }
18
+ );
19
+
20
+ /**
21
+ * Mock useTrueSheetNavigation hook for testing.
22
+ */
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
26
+ <T extends ParamListBase = ParamListBase>(): TrueSheetNavigationProp<T> =>
27
+ useTrueSheetNavigationMock() as unknown as TrueSheetNavigationProp<T>
28
+ );
29
+
30
+ export { TrueSheetActions };
31
+
32
+ export type { TrueSheetActionType } from '../navigation/actions';
33
+ export type { DetentInfoEventPayload, PositionChangeEventPayload } from '../TrueSheet.types';
34
+ export type {
35
+ TrueSheetNavigationEventMap,
36
+ TrueSheetNavigationHelpers,
37
+ TrueSheetNavigationOptions,
38
+ TrueSheetNavigationProp,
39
+ TrueSheetNavigationState,
40
+ TrueSheetScreenProps,
41
+ } from '../navigation/expo-router/types';
@@ -14,15 +14,14 @@ interface MockReanimatedTrueSheetContextValue {
14
14
  animatedDetent: SharedValue<number>;
15
15
  }
16
16
 
17
- const createMockSharedValue = <T>(initialValue: T): SharedValue<T> =>
18
- ({
19
- value: initialValue,
20
- get: () => initialValue,
21
- set: () => {},
22
- addListener: () => () => {},
23
- removeListener: () => {},
24
- modify: () => {},
25
- }) as unknown as SharedValue<T>;
17
+ const createMockSharedValue = <T>(initialValue: T): SharedValue<T> => ({
18
+ value: initialValue,
19
+ get: () => initialValue,
20
+ set: () => {},
21
+ addListener: () => {},
22
+ removeListener: () => {},
23
+ modify: () => {},
24
+ });
26
25
 
27
26
  /**
28
27
  * Mock ReanimatedTrueSheet component for testing.
@@ -1,9 +1,9 @@
1
- import type { ParamListBase } from '@react-navigation/core';
1
+ import type { NavigatorArgs } from 'standard-navigation';
2
2
 
3
3
  import type {
4
- TrueSheetDescriptorMap,
5
- TrueSheetNavigationHelpers,
6
- TrueSheetNavigationState,
4
+ TrueSheetNavigationOptions,
5
+ TrueSheetNavigatorContentExtraProps,
6
+ TrueSheetStandardEventMap,
7
7
  } from './types';
8
8
  import { TrueSheetScreen, type TrueSheetScreenProps } from './screen';
9
9
 
@@ -22,15 +22,22 @@ const DEFAULT_DETENTS: ('auto' | number)[] = ['auto'];
22
22
  const clampDetentIndex = (index: number, detentsLength: number): number =>
23
23
  Math.min(index, Math.max(detentsLength - 1, 0));
24
24
 
25
- interface TrueSheetViewProps {
26
- state: TrueSheetNavigationState<ParamListBase>;
27
- navigation: TrueSheetNavigationHelpers;
28
- descriptors: TrueSheetDescriptorMap;
29
- }
25
+ export type TrueSheetNavigatorContentProps = NavigatorArgs<
26
+ TrueSheetNavigationOptions,
27
+ TrueSheetStandardEventMap
28
+ > &
29
+ TrueSheetNavigatorContentExtraProps;
30
30
 
31
- export const TrueSheetView = ({ state, navigation, descriptors }: TrueSheetViewProps) => {
32
- // First route is the base screen, rest are sheets
33
- const [baseRoute, ...sheetRoutes] = state.routes;
31
+ export const TrueSheetNavigatorContent = ({
32
+ routes,
33
+ dispatch,
34
+ descriptors,
35
+ emitter,
36
+ }: TrueSheetNavigatorContentProps) => {
37
+ // First route is the base screen, rest are sheets.
38
+ // Rendered from the raw routes (not the standard state) so custom fields are
39
+ // available and preloaded routes never present as sheets.
40
+ const [baseRoute, ...sheetRoutes] = routes;
34
41
 
35
42
  const baseDescriptor = baseRoute ? descriptors[baseRoute.key] : null;
36
43
 
@@ -47,7 +54,7 @@ export const TrueSheetView = ({ state, navigation, descriptors }: TrueSheetViewP
47
54
  return null;
48
55
  }
49
56
 
50
- const { options, navigation: screenNavigation, render } = descriptor;
57
+ const { options, render } = descriptor;
51
58
  const {
52
59
  detentIndex = 0,
53
60
  detents = DEFAULT_DETENTS,
@@ -67,8 +74,8 @@ export const TrueSheetView = ({ state, navigation, descriptors }: TrueSheetViewP
67
74
  detentIndex={resolvedIndex}
68
75
  resizeKey={route.resizeKey}
69
76
  detents={detents}
70
- navigation={screenNavigation}
71
- emit={navigation.emit}
77
+ dispatch={dispatch}
78
+ emit={emitter.emit}
72
79
  positionChangeHandler={positionChangeHandler}
73
80
  {...sheetProps}
74
81
  >
@@ -85,8 +92,8 @@ export const TrueSheetView = ({ state, navigation, descriptors }: TrueSheetViewP
85
92
  detentIndex={resolvedIndex}
86
93
  resizeKey={route.resizeKey}
87
94
  detents={detents}
88
- navigation={screenNavigation}
89
- emit={navigation.emit}
95
+ dispatch={dispatch}
96
+ emit={emitter.emit}
90
97
  positionChangeHandler={positionChangeHandler}
91
98
  {...sheetProps}
92
99
  >
@@ -0,0 +1,78 @@
1
+ export type TrueSheetActionType =
2
+ | {
3
+ type: 'PUSH';
4
+ payload: { name: string; params?: object };
5
+ source?: string;
6
+ target?: string;
7
+ }
8
+ | {
9
+ type: 'POP';
10
+ payload: { count: number };
11
+ source?: string;
12
+ target?: string;
13
+ }
14
+ | {
15
+ type: 'POP_TO';
16
+ payload: { name: string; params?: object; merge?: boolean };
17
+ source?: string;
18
+ target?: string;
19
+ }
20
+ | {
21
+ type: 'POP_TO_TOP';
22
+ source?: string;
23
+ target?: string;
24
+ }
25
+ | {
26
+ type: 'REPLACE';
27
+ payload: { name: string; params?: object };
28
+ source?: string;
29
+ target?: string;
30
+ }
31
+ | {
32
+ type: 'GO_BACK';
33
+ source?: string;
34
+ target?: string;
35
+ }
36
+ | {
37
+ type: 'RESIZE';
38
+ index: number;
39
+ source?: string;
40
+ target?: string;
41
+ }
42
+ | {
43
+ type: 'DISMISS';
44
+ source?: string;
45
+ target?: string;
46
+ }
47
+ | {
48
+ type: 'REMOVE';
49
+ source?: string;
50
+ target?: string;
51
+ };
52
+
53
+ /**
54
+ * Action creators for the TrueSheet navigator.
55
+ * Plain objects matching `StackActions` shapes, plus sheet-specific actions.
56
+ */
57
+ export const TrueSheetActions = {
58
+ push: (name: string, params?: object): TrueSheetActionType => ({
59
+ type: 'PUSH',
60
+ payload: { name, params },
61
+ }),
62
+ pop: (count: number = 1): TrueSheetActionType => ({
63
+ type: 'POP',
64
+ payload: { count },
65
+ }),
66
+ popTo: (name: string, params?: object, options?: { merge?: boolean }): TrueSheetActionType => ({
67
+ type: 'POP_TO',
68
+ payload: { name, params, merge: options?.merge },
69
+ }),
70
+ popToTop: (): TrueSheetActionType => ({ type: 'POP_TO_TOP' }),
71
+ replace: (name: string, params?: object): TrueSheetActionType => ({
72
+ type: 'REPLACE',
73
+ payload: { name, params },
74
+ }),
75
+ resize: (index: number): TrueSheetActionType => ({ type: 'RESIZE', index }),
76
+ dismiss: (): TrueSheetActionType => ({ type: 'DISMISS' }),
77
+ remove: (): TrueSheetActionType => ({ type: 'REMOVE' }),
78
+ };
@@ -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';
@@ -0,0 +1,194 @@
1
+ import { TrueSheetActions } from './actions';
2
+ import type { TrueSheetRouter, TrueSheetRouterFactory, TrueSheetRouterOptions } from './types';
3
+
4
+ const uid = () => Math.random().toString(36).slice(2, 10);
5
+
6
+ const ensureBaseRoute = <T extends { routes: { name: string }[] }>(
7
+ state: T,
8
+ baseRouteName: string | undefined,
9
+ routeParamList: Record<string, object | undefined> | undefined
10
+ ): T & { index: number; routes: T['routes'] } => {
11
+ if (!baseRouteName) {
12
+ return state as T & { index: number; routes: T['routes'] };
13
+ }
14
+
15
+ const hasBaseRoute = state.routes.some((r) => r.name === baseRouteName);
16
+
17
+ if (!hasBaseRoute) {
18
+ const baseRoute = {
19
+ key: `${baseRouteName}-${uid()}`,
20
+ name: baseRouteName,
21
+ params: routeParamList?.[baseRouteName],
22
+ };
23
+
24
+ return {
25
+ ...state,
26
+ index: state.routes.length,
27
+ routes: [baseRoute, ...state.routes],
28
+ } as T & { index: number; routes: T['routes'] };
29
+ }
30
+
31
+ return state as T & { index: number; routes: T['routes'] };
32
+ };
33
+
34
+ export const createTrueSheetRouter =
35
+ (stackRouter: TrueSheetRouterFactory) =>
36
+ (routerOptions: TrueSheetRouterOptions): TrueSheetRouter => {
37
+ const baseRouter = stackRouter(routerOptions);
38
+
39
+ return {
40
+ ...baseRouter,
41
+ type: 'true-sheet',
42
+
43
+ getInitialState(options) {
44
+ const state = baseRouter.getInitialState(options);
45
+ const baseRouteName = routerOptions.initialRouteName ?? options.routeNames[0];
46
+ const stateWithBaseRoute = ensureBaseRoute(state, baseRouteName, options.routeParamList);
47
+
48
+ return {
49
+ ...stateWithBaseRoute,
50
+ stale: false,
51
+ type: 'true-sheet',
52
+ key: `true-sheet-${uid()}`,
53
+ };
54
+ },
55
+
56
+ getStateForAction(state, action, options) {
57
+ switch (action.type) {
58
+ case 'RESIZE': {
59
+ const routeIndex =
60
+ action.target === state.key && action.source
61
+ ? state.routes.findIndex((r) => r.key === action.source)
62
+ : state.index;
63
+
64
+ return {
65
+ ...state,
66
+ routes: state.routes.map((route, i) =>
67
+ i === routeIndex
68
+ ? {
69
+ ...route,
70
+ resizeIndex: action.index,
71
+ resizeKey: (route.resizeKey ?? 0) + 1,
72
+ }
73
+ : route
74
+ ),
75
+ };
76
+ }
77
+
78
+ case 'GO_BACK':
79
+ case 'DISMISS': {
80
+ return this.getStateForAction(state, TrueSheetActions.pop(1), options);
81
+ }
82
+
83
+ case 'POP': {
84
+ // Only base screen remains - let parent navigator handle it
85
+ if (state.routes.length <= 1) {
86
+ return null;
87
+ }
88
+
89
+ const count =
90
+ 'payload' in action && typeof action.payload?.count === 'number'
91
+ ? action.payload.count
92
+ : 1;
93
+
94
+ // Calculate how many routes we can actually pop (don't pop base screen)
95
+ const maxPopCount = state.routes.length - 1;
96
+ const actualCount = Math.min(count, maxPopCount);
97
+
98
+ // Base screen - let parent navigator handle it
99
+ if (actualCount <= 0) {
100
+ return null;
101
+ }
102
+
103
+ // Target index is the route we want to stay on (land on after pop)
104
+ // closingIndex is the first route to be dismissed (the one after target)
105
+ const targetIndex = state.routes.length - 1 - actualCount;
106
+ const closingIndex = targetIndex + 1;
107
+
108
+ // Mark only the bottom-most route to pop as closing
109
+ // The sheet's dismiss() will handle dismissing sheets above it first
110
+ return {
111
+ ...state,
112
+ index: closingIndex,
113
+ routes: state.routes.map((route, i) =>
114
+ i === closingIndex ? { ...route, closing: true } : route
115
+ ),
116
+ };
117
+ }
118
+
119
+ case 'POP_TO_TOP': {
120
+ const popCount = state.routes.length - 1;
121
+ return this.getStateForAction(state, TrueSheetActions.pop(popCount), options);
122
+ }
123
+
124
+ case 'POP_TO': {
125
+ const targetName =
126
+ 'payload' in action && typeof action.payload?.name === 'string'
127
+ ? action.payload.name
128
+ : null;
129
+
130
+ if (!targetName) {
131
+ return null;
132
+ }
133
+
134
+ const targetIndex = state.routes.findIndex((r) => r.name === targetName);
135
+
136
+ // Target not found or is the current route
137
+ if (targetIndex === -1 || targetIndex >= state.index) {
138
+ return null;
139
+ }
140
+
141
+ const popCount = state.routes.length - 1 - targetIndex;
142
+ return this.getStateForAction(state, TrueSheetActions.pop(popCount), options);
143
+ }
144
+
145
+ case 'REMOVE': {
146
+ // Actually remove the closing route and all routes above it
147
+ const routeKey = action.source;
148
+ const routeIndex = routeKey
149
+ ? state.routes.findIndex((r) => r.key === routeKey)
150
+ : state.routes.findIndex((r) => r.closing);
151
+
152
+ if (routeIndex === -1) {
153
+ return state;
154
+ }
155
+
156
+ // Remove the route and all routes above it (they were dismissed together)
157
+ const routes = state.routes.filter((_, i) => i < routeIndex);
158
+
159
+ return {
160
+ ...state,
161
+ index: Math.min(state.index, routes.length - 1),
162
+ routes,
163
+ };
164
+ }
165
+
166
+ default:
167
+ return baseRouter.getStateForAction(state, action, options);
168
+ }
169
+ },
170
+
171
+ getRehydratedState(partialState, { routeNames, routeParamList, routeGetIdList }) {
172
+ if (partialState.stale === false) {
173
+ return partialState;
174
+ }
175
+
176
+ const state = baseRouter.getRehydratedState(partialState, {
177
+ routeNames,
178
+ routeParamList,
179
+ routeGetIdList,
180
+ });
181
+
182
+ const baseRouteName = routerOptions.initialRouteName ?? routeNames[0];
183
+ const stateWithBaseRoute = ensureBaseRoute(state, baseRouteName, routeParamList);
184
+
185
+ return {
186
+ ...stateWithBaseRoute,
187
+ type: 'true-sheet',
188
+ key: `true-sheet-${uid()}`,
189
+ };
190
+ },
191
+
192
+ actionCreators: TrueSheetActions,
193
+ };
194
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Base navigation types for the Expo Router entry point.
3
+ *
4
+ * Sourced from Expo Router's vendored React Navigation so apps that only install
5
+ * `expo-router` still get real types — see `../base-types` for the React Navigation copy.
6
+ */
7
+ export type {
8
+ DefaultNavigatorOptions,
9
+ NavigationAction,
10
+ NavigationHelpers,
11
+ NavigationProp,
12
+ NavigationState,
13
+ ParamListBase,
14
+ RouteProp,
15
+ Router,
16
+ StackActionHelpers,
17
+ StackRouterOptions,
18
+ } from 'expo-router/react-navigation';
@@ -0,0 +1,90 @@
1
+ import { StackRouter, unstable_integrateWithRouter, useNavigation } from 'expo-router';
2
+
3
+ import { createTrueSheetRouter } from '../createTrueSheetRouter';
4
+ import { trueSheetNavigator } from '../navigator';
5
+ import type { ParamListBase } from './base-types';
6
+ import type {
7
+ StackRouterFactory,
8
+ TrueSheetNavigationOptions,
9
+ TrueSheetNavigationProp,
10
+ TrueSheetNavigationState,
11
+ TrueSheetNavigatorContentExtraProps,
12
+ TrueSheetNavigatorProps,
13
+ TrueSheetRouterFactory,
14
+ TrueSheetRouterOptions,
15
+ TrueSheetStandardEventMap,
16
+ } from './types';
17
+
18
+ // expo-router's vendored StackRouter is structurally identical to @react-navigation's.
19
+ // Pinned to this entry's own router types: `createTrueSheetRouter` is shared and typed
20
+ // against React Navigation, which resolves to `any` in apps that only install expo-router.
21
+ const sheetRouter: TrueSheetRouterFactory = createTrueSheetRouter(
22
+ StackRouter as StackRouterFactory
23
+ );
24
+
25
+ const SheetNavigator = unstable_integrateWithRouter<
26
+ TrueSheetNavigationOptions,
27
+ TrueSheetNavigationState<ParamListBase>,
28
+ TrueSheetStandardEventMap,
29
+ TrueSheetNavigatorContentExtraProps,
30
+ TrueSheetRouterOptions
31
+ >(trueSheetNavigator, sheetRouter, {
32
+ createProps: ({ state, dispatch }) => ({
33
+ routes: state.routes,
34
+ dispatch,
35
+ }),
36
+ });
37
+
38
+ // Public props: `routes`/`dispatch` are injected by `createProps` (optional here
39
+ // so the navigator type stays comparable), and expo-router intersects the event
40
+ // map with an index signature which breaks precisely typed `screenListeners`
41
+ // callbacks - replace it with the exact map
42
+ type SheetProps = Omit<
43
+ React.ComponentProps<typeof SheetNavigator>,
44
+ 'screenListeners' | 'routes' | 'dispatch'
45
+ > &
46
+ Partial<Pick<React.ComponentProps<typeof SheetNavigator>, 'routes' | 'dispatch'>> &
47
+ Pick<TrueSheetNavigatorProps, 'screenListeners'>;
48
+
49
+ /**
50
+ * TrueSheet navigator layout for Expo Router.
51
+ *
52
+ * @example
53
+ * ```tsx
54
+ * // app/sheet/_layout.tsx
55
+ * import { Sheet } from '@lodev09/react-native-true-sheet/navigation/expo-router';
56
+ *
57
+ * export default function SheetLayout() {
58
+ * return (
59
+ * <Sheet initialRouteName="(home)">
60
+ * <Sheet.Screen name="details" options={{ detents: [0.5, 1] }} />
61
+ * </Sheet>
62
+ * );
63
+ * }
64
+ * ```
65
+ */
66
+ export const Sheet = SheetNavigator as React.ComponentType<SheetProps> & {
67
+ Screen: typeof SheetNavigator.Screen;
68
+ Protected: typeof SheetNavigator.Protected;
69
+ };
70
+
71
+ /**
72
+ * Hook to access TrueSheet navigation with the resize helper.
73
+ * Built on expo-router's `useNavigation`.
74
+ */
75
+ export const useTrueSheetNavigation = <
76
+ T extends ParamListBase = ParamListBase,
77
+ >(): TrueSheetNavigationProp<T> => useNavigation<TrueSheetNavigationProp<T>>();
78
+
79
+ export { TrueSheetActions, type TrueSheetActionType } from '../actions';
80
+
81
+ export type { DetentInfoEventPayload, PositionChangeEventPayload } from '../../TrueSheet.types';
82
+
83
+ export type {
84
+ TrueSheetNavigationEventMap,
85
+ TrueSheetNavigationHelpers,
86
+ TrueSheetNavigationOptions,
87
+ TrueSheetNavigationProp,
88
+ TrueSheetNavigationState,
89
+ TrueSheetScreenProps,
90
+ } from './types';