@lodev09/react-native-true-sheet 2.0.5 → 4.0.0-beta.0

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 (194) hide show
  1. package/README.md +36 -8
  2. package/RNTrueSheet.podspec +20 -0
  3. package/android/build.gradle +26 -14
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +108 -0
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerViewManager.kt +21 -0
  6. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +46 -0
  7. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +21 -0
  8. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +47 -0
  9. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterViewManager.kt +21 -0
  10. package/android/src/main/java/com/lodev09/truesheet/TrueSheetModule.kt +165 -0
  11. package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +36 -4
  12. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +257 -299
  13. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +855 -0
  14. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +104 -82
  15. package/android/src/main/java/com/lodev09/truesheet/events/DetentChangeEvent.kt +26 -0
  16. package/android/src/main/java/com/lodev09/truesheet/events/DidDismissEvent.kt +20 -0
  17. package/android/src/main/java/com/lodev09/truesheet/events/DidPresentEvent.kt +26 -0
  18. package/android/src/main/java/com/lodev09/truesheet/events/DragBeginEvent.kt +26 -0
  19. package/android/src/main/java/com/lodev09/truesheet/events/DragChangeEvent.kt +26 -0
  20. package/android/src/main/java/com/lodev09/truesheet/events/DragEndEvent.kt +26 -0
  21. package/android/src/main/java/com/lodev09/truesheet/events/MountEvent.kt +20 -0
  22. package/android/src/main/java/com/lodev09/truesheet/events/PositionChangeEvent.kt +32 -0
  23. package/android/src/main/java/com/lodev09/truesheet/events/SizeChangeEvent.kt +27 -0
  24. package/android/src/main/java/com/lodev09/truesheet/events/WillDismissEvent.kt +20 -0
  25. package/android/src/main/java/com/lodev09/truesheet/events/WillPresentEvent.kt +26 -0
  26. package/android/src/main/java/com/lodev09/truesheet/{core/Utils.kt → utils/ScreenUtils.kt} +47 -17
  27. package/android/src/main/res/values/styles.xml +8 -0
  28. package/ios/TrueSheetComponentDescriptor.h +24 -0
  29. package/ios/TrueSheetContainerView.h +47 -0
  30. package/ios/TrueSheetContainerView.mm +117 -0
  31. package/ios/TrueSheetContentView.h +37 -0
  32. package/ios/TrueSheetContentView.mm +114 -0
  33. package/ios/TrueSheetFooterView.h +27 -0
  34. package/ios/TrueSheetFooterView.mm +101 -0
  35. package/ios/TrueSheetModule.h +44 -0
  36. package/ios/TrueSheetModule.mm +133 -0
  37. package/ios/TrueSheetView.h +53 -0
  38. package/ios/TrueSheetView.mm +433 -0
  39. package/ios/TrueSheetViewController.h +53 -0
  40. package/ios/TrueSheetViewController.mm +649 -0
  41. package/ios/events/OnDetentChangeEvent.h +28 -0
  42. package/ios/events/OnDetentChangeEvent.mm +30 -0
  43. package/ios/events/OnDidDismissEvent.h +26 -0
  44. package/ios/events/OnDidDismissEvent.mm +25 -0
  45. package/ios/events/OnDidPresentEvent.h +28 -0
  46. package/ios/events/OnDidPresentEvent.mm +30 -0
  47. package/ios/events/OnDragBeginEvent.h +28 -0
  48. package/ios/events/OnDragBeginEvent.mm +30 -0
  49. package/ios/events/OnDragChangeEvent.h +28 -0
  50. package/ios/events/OnDragChangeEvent.mm +30 -0
  51. package/ios/events/OnDragEndEvent.h +28 -0
  52. package/ios/events/OnDragEndEvent.mm +30 -0
  53. package/ios/events/OnMountEvent.h +26 -0
  54. package/ios/events/OnMountEvent.mm +25 -0
  55. package/ios/events/OnPositionChangeEvent.h +29 -0
  56. package/ios/events/OnPositionChangeEvent.mm +32 -0
  57. package/ios/events/OnSizeChangeEvent.h +28 -0
  58. package/ios/events/OnSizeChangeEvent.mm +30 -0
  59. package/ios/events/OnWillDismissEvent.h +26 -0
  60. package/ios/events/OnWillDismissEvent.mm +25 -0
  61. package/ios/events/OnWillPresentEvent.h +28 -0
  62. package/ios/events/OnWillPresentEvent.mm +30 -0
  63. package/ios/utils/GestureUtil.h +25 -0
  64. package/ios/utils/GestureUtil.mm +26 -0
  65. package/ios/utils/LayoutUtil.h +44 -0
  66. package/ios/utils/LayoutUtil.mm +50 -0
  67. package/ios/utils/WindowUtil.h +27 -0
  68. package/ios/utils/WindowUtil.mm +42 -0
  69. package/lib/module/TrueSheet.js +231 -135
  70. package/lib/module/TrueSheet.js.map +1 -1
  71. package/lib/module/TrueSheetGrabber.js +16 -14
  72. package/lib/module/TrueSheetGrabber.js.map +1 -1
  73. package/lib/module/fabric/TrueSheetContainerViewNativeComponent.ts +8 -0
  74. package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +8 -0
  75. package/lib/module/fabric/TrueSheetFooterViewNativeComponent.ts +8 -0
  76. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +63 -0
  77. package/lib/module/index.js +1 -0
  78. package/lib/module/index.js.map +1 -1
  79. package/lib/module/reanimated/ReanimatedTrueSheet.js +87 -0
  80. package/lib/module/reanimated/ReanimatedTrueSheet.js.map +1 -0
  81. package/lib/module/reanimated/ReanimatedTrueSheetProvider.js +72 -0
  82. package/lib/module/reanimated/ReanimatedTrueSheetProvider.js.map +1 -0
  83. package/lib/module/reanimated/index.js +6 -0
  84. package/lib/module/reanimated/index.js.map +1 -0
  85. package/lib/module/reanimated/useReanimatedPositionChangeHandler.js +19 -0
  86. package/lib/module/reanimated/useReanimatedPositionChangeHandler.js.map +1 -0
  87. package/lib/module/specs/NativeTrueSheetModule.js +12 -0
  88. package/lib/module/specs/NativeTrueSheetModule.js.map +1 -0
  89. package/lib/typescript/package.json +1 -0
  90. package/lib/typescript/src/TrueSheet.d.ts +79 -0
  91. package/lib/typescript/src/TrueSheet.d.ts.map +1 -0
  92. package/lib/typescript/src/TrueSheet.types.d.ts +260 -0
  93. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -0
  94. package/lib/typescript/{commonjs/src → src}/TrueSheetGrabber.d.ts +1 -1
  95. package/lib/typescript/src/TrueSheetGrabber.d.ts.map +1 -0
  96. package/lib/typescript/src/fabric/TrueSheetContainerViewNativeComponent.d.ts +6 -0
  97. package/lib/typescript/src/fabric/TrueSheetContainerViewNativeComponent.d.ts.map +1 -0
  98. package/lib/typescript/src/fabric/TrueSheetContentViewNativeComponent.d.ts +6 -0
  99. package/lib/typescript/src/fabric/TrueSheetContentViewNativeComponent.d.ts.map +1 -0
  100. package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts +6 -0
  101. package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts.map +1 -0
  102. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +44 -0
  103. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -0
  104. package/lib/typescript/{commonjs/src → src}/index.d.ts +1 -0
  105. package/lib/typescript/src/index.d.ts.map +1 -0
  106. package/lib/typescript/src/reanimated/ReanimatedTrueSheet.d.ts +43 -0
  107. package/lib/typescript/src/reanimated/ReanimatedTrueSheet.d.ts.map +1 -0
  108. package/lib/typescript/src/reanimated/ReanimatedTrueSheetProvider.d.ts +57 -0
  109. package/lib/typescript/src/reanimated/ReanimatedTrueSheetProvider.d.ts.map +1 -0
  110. package/lib/typescript/src/reanimated/index.d.ts +4 -0
  111. package/lib/typescript/src/reanimated/index.d.ts.map +1 -0
  112. package/lib/typescript/src/reanimated/useReanimatedPositionChangeHandler.d.ts +6 -0
  113. package/lib/typescript/src/reanimated/useReanimatedPositionChangeHandler.d.ts.map +1 -0
  114. package/lib/typescript/src/specs/NativeTrueSheetModule.d.ts +34 -0
  115. package/lib/typescript/src/specs/NativeTrueSheetModule.d.ts.map +1 -0
  116. package/package.json +104 -75
  117. package/react-native.config.js +17 -0
  118. package/src/TrueSheet.tsx +285 -188
  119. package/src/TrueSheet.types.ts +119 -106
  120. package/src/TrueSheetGrabber.tsx +29 -28
  121. package/src/__mocks__/index.js +60 -12
  122. package/src/fabric/TrueSheetContainerViewNativeComponent.ts +8 -0
  123. package/src/fabric/TrueSheetContentViewNativeComponent.ts +8 -0
  124. package/src/fabric/TrueSheetFooterViewNativeComponent.ts +8 -0
  125. package/src/fabric/TrueSheetViewNativeComponent.ts +63 -0
  126. package/src/index.ts +4 -3
  127. package/src/reanimated/ReanimatedTrueSheet.tsx +95 -0
  128. package/src/reanimated/ReanimatedTrueSheetProvider.tsx +92 -0
  129. package/src/reanimated/index.ts +3 -0
  130. package/src/reanimated/useReanimatedPositionChangeHandler.ts +26 -0
  131. package/src/specs/NativeTrueSheetModule.ts +38 -0
  132. package/TrueSheet.podspec +0 -49
  133. package/android/src/main/java/com/lodev09/truesheet/TrueSheetDialog.kt +0 -400
  134. package/android/src/main/java/com/lodev09/truesheet/TrueSheetEvent.kt +0 -22
  135. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewModule.kt +0 -63
  136. package/android/src/main/java/com/lodev09/truesheet/core/KeyboardManager.kt +0 -58
  137. package/android/src/main/java/com/lodev09/truesheet/core/RootSheetView.kt +0 -102
  138. package/ios/Extensions/UIBlurEffect+withTint.swift +0 -62
  139. package/ios/Extensions/UIView+pinTo.swift +0 -74
  140. package/ios/Extensions/UIViewController+detentForSize.swift +0 -134
  141. package/ios/TrueSheet-Bridging-Header.h +0 -14
  142. package/ios/TrueSheetEvent.swift +0 -48
  143. package/ios/TrueSheetView.swift +0 -461
  144. package/ios/TrueSheetViewController.swift +0 -275
  145. package/ios/TrueSheetViewManager.m +0 -53
  146. package/ios/TrueSheetViewManager.swift +0 -48
  147. package/ios/Utils/Logger.swift +0 -39
  148. package/ios/Utils/Promise.swift +0 -25
  149. package/lib/commonjs/TrueSheet.js +0 -258
  150. package/lib/commonjs/TrueSheet.js.map +0 -1
  151. package/lib/commonjs/TrueSheet.types.js +0 -6
  152. package/lib/commonjs/TrueSheet.types.js.map +0 -1
  153. package/lib/commonjs/TrueSheetFooter.js +0 -19
  154. package/lib/commonjs/TrueSheetFooter.js.map +0 -1
  155. package/lib/commonjs/TrueSheetGrabber.js +0 -54
  156. package/lib/commonjs/TrueSheetGrabber.js.map +0 -1
  157. package/lib/commonjs/TrueSheetModule.js +0 -19
  158. package/lib/commonjs/TrueSheetModule.js.map +0 -1
  159. package/lib/commonjs/__mocks__/index.js +0 -52
  160. package/lib/commonjs/__mocks__/index.js.map +0 -1
  161. package/lib/commonjs/index.js +0 -39
  162. package/lib/commonjs/index.js.map +0 -1
  163. package/lib/module/TrueSheetFooter.js +0 -14
  164. package/lib/module/TrueSheetFooter.js.map +0 -1
  165. package/lib/module/TrueSheetModule.js +0 -15
  166. package/lib/module/TrueSheetModule.js.map +0 -1
  167. package/lib/module/__mocks__/index.js +0 -21
  168. package/lib/module/__mocks__/index.js.map +0 -1
  169. package/lib/typescript/commonjs/package.json +0 -1
  170. package/lib/typescript/commonjs/src/TrueSheet.d.ts +0 -70
  171. package/lib/typescript/commonjs/src/TrueSheet.d.ts.map +0 -1
  172. package/lib/typescript/commonjs/src/TrueSheet.types.d.ts +0 -241
  173. package/lib/typescript/commonjs/src/TrueSheet.types.d.ts.map +0 -1
  174. package/lib/typescript/commonjs/src/TrueSheetFooter.d.ts +0 -7
  175. package/lib/typescript/commonjs/src/TrueSheetFooter.d.ts.map +0 -1
  176. package/lib/typescript/commonjs/src/TrueSheetGrabber.d.ts.map +0 -1
  177. package/lib/typescript/commonjs/src/TrueSheetModule.d.ts +0 -2
  178. package/lib/typescript/commonjs/src/TrueSheetModule.d.ts.map +0 -1
  179. package/lib/typescript/commonjs/src/index.d.ts.map +0 -1
  180. package/lib/typescript/module/src/TrueSheet.d.ts +0 -70
  181. package/lib/typescript/module/src/TrueSheet.d.ts.map +0 -1
  182. package/lib/typescript/module/src/TrueSheet.types.d.ts +0 -241
  183. package/lib/typescript/module/src/TrueSheet.types.d.ts.map +0 -1
  184. package/lib/typescript/module/src/TrueSheetFooter.d.ts +0 -7
  185. package/lib/typescript/module/src/TrueSheetFooter.d.ts.map +0 -1
  186. package/lib/typescript/module/src/TrueSheetGrabber.d.ts +0 -39
  187. package/lib/typescript/module/src/TrueSheetGrabber.d.ts.map +0 -1
  188. package/lib/typescript/module/src/TrueSheetModule.d.ts +0 -2
  189. package/lib/typescript/module/src/TrueSheetModule.d.ts.map +0 -1
  190. package/lib/typescript/module/src/index.d.ts +0 -4
  191. package/lib/typescript/module/src/index.d.ts.map +0 -1
  192. package/src/TrueSheetFooter.tsx +0 -17
  193. package/src/TrueSheetModule.ts +0 -19
  194. /package/lib/{typescript/module → module}/package.json +0 -0
@@ -0,0 +1,95 @@
1
+ import { forwardRef } from 'react';
2
+ import Animated, {
3
+ type WithSpringConfig,
4
+ type WithTimingConfig,
5
+ withSpring,
6
+ withTiming,
7
+ Easing,
8
+ } from 'react-native-reanimated';
9
+ import { Platform } from 'react-native';
10
+
11
+ import { TrueSheet } from '../TrueSheet';
12
+ import type { TrueSheetProps, PositionChangeEvent } from '../TrueSheet.types';
13
+ import { useReanimatedTrueSheet } from './ReanimatedTrueSheetProvider';
14
+ import { useReanimatedPositionChangeHandler } from './useReanimatedPositionChangeHandler';
15
+
16
+ const SPRING_CONFIG: WithSpringConfig = {
17
+ damping: 500,
18
+ stiffness: 1000,
19
+ mass: 3,
20
+ overshootClamping: true,
21
+ };
22
+
23
+ const TIMING_CONFIG: WithTimingConfig = {
24
+ duration: 300,
25
+ easing: Easing.bezier(0.25, 0.1, 0.25, 1),
26
+ };
27
+
28
+ interface ReanimatedTrueSheetProps extends TrueSheetProps {
29
+ /**
30
+ * Worklet version of `onPositionChange`
31
+ *
32
+ * @see {@link TrueSheetProps.onPositionChange}
33
+ */
34
+ onPositionChange?: TrueSheetProps['onPositionChange'];
35
+ }
36
+
37
+ // Create animated version of TrueSheet
38
+ const AnimatedTrueSheet = Animated.createAnimatedComponent(TrueSheet);
39
+
40
+ /**
41
+ * Reanimated-enabled version of TrueSheet that automatically syncs position with the provider's shared value.
42
+ * Must be used within a ReanimatedTrueSheetProvider.
43
+ *
44
+ * NOTE: `onPositionChange` is now under UI thread.
45
+ * Make sure you add `worklet` if you want to override this.
46
+ *
47
+ * @example
48
+ * ```tsx
49
+ * import { ReanimatedTrueSheet, ReanimatedTrueSheetProvider } from '@lodev09/react-native-true-sheet'
50
+ *
51
+ * function MyScreen() {
52
+ * const sheetRef = useRef<TrueSheet>(null)
53
+ *
54
+ * return (
55
+ * <ReanimatedTrueSheetProvider>
56
+ * <View>
57
+ * <ReanimatedTrueSheet
58
+ * ref={sheetRef}
59
+ * detents={[0.25, 0.5, 1]}
60
+ * initialDetentIndex={1}
61
+ * >
62
+ * <Text>Sheet Content</Text>
63
+ * </ReanimatedTrueSheet>
64
+ * </View>
65
+ * </ReanimatedTrueSheetProvider>
66
+ * )
67
+ * }
68
+ * ```
69
+ */
70
+ export const ReanimatedTrueSheet = forwardRef<TrueSheet, ReanimatedTrueSheetProps>((props, ref) => {
71
+ const { onPositionChange, ...rest } = props;
72
+
73
+ const { animatedPosition, animatedIndex } = useReanimatedTrueSheet();
74
+
75
+ const positionChangeHandler = useReanimatedPositionChangeHandler((payload) => {
76
+ 'worklet';
77
+
78
+ // When transitioning=true, we animate the position manually for smooth transitions.
79
+ // This is used when native can't provide real-time position updates during animations.
80
+ if (payload.transitioning) {
81
+ if (Platform.OS === 'android') {
82
+ animatedPosition.value = withTiming(payload.position, TIMING_CONFIG);
83
+ } else {
84
+ animatedPosition.value = withSpring(payload.position, SPRING_CONFIG);
85
+ }
86
+ } else {
87
+ animatedPosition.value = payload.position;
88
+ }
89
+
90
+ animatedIndex.value = payload.index;
91
+ onPositionChange?.({ nativeEvent: payload } as PositionChangeEvent);
92
+ });
93
+
94
+ return <AnimatedTrueSheet ref={ref} onPositionChange={positionChangeHandler} {...rest} />;
95
+ });
@@ -0,0 +1,92 @@
1
+ import { createContext, useContext, useMemo, type ReactNode } from 'react';
2
+ import { useWindowDimensions } from 'react-native';
3
+ import { useSharedValue, type SharedValue } from 'react-native-reanimated';
4
+
5
+ export interface ReanimatedTrueSheetContextValue {
6
+ /**
7
+ * Shared value representing the current sheet position (Y offset from bottom)
8
+ */
9
+ animatedPosition: SharedValue<number>;
10
+ /**
11
+ * Shared value representing the current detent index
12
+ */
13
+ animatedIndex: SharedValue<number>;
14
+ }
15
+
16
+ const ReanimatedTrueSheetContext = createContext<ReanimatedTrueSheetContextValue | null>(null);
17
+
18
+ export interface ReanimatedTrueSheetProviderProps {
19
+ children: ReactNode;
20
+ }
21
+
22
+ /**
23
+ * Provider component that manages shared values for Reanimated TrueSheet.
24
+ * Wrap your app or component tree with this provider to enable Reanimated integration.
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * import { ReanimatedTrueSheetProvider } from '@lodev09/react-native-true-sheet'
29
+ *
30
+ * function App() {
31
+ * return (
32
+ * <ReanimatedTrueSheetProvider>
33
+ * <YourApp />
34
+ * </ReanimatedTrueSheetProvider>
35
+ * )
36
+ * }
37
+ * ```
38
+ */
39
+ export const ReanimatedTrueSheetProvider = ({ children }: ReanimatedTrueSheetProviderProps) => {
40
+ const { height } = useWindowDimensions();
41
+ const animatedPosition = useSharedValue(height);
42
+ const animatedIndex = useSharedValue(-1);
43
+
44
+ const value = useMemo(
45
+ () => ({
46
+ animatedPosition,
47
+ animatedIndex,
48
+ }),
49
+ [animatedPosition, animatedIndex]
50
+ );
51
+
52
+ return (
53
+ <ReanimatedTrueSheetContext.Provider value={value}>
54
+ {children}
55
+ </ReanimatedTrueSheetContext.Provider>
56
+ );
57
+ };
58
+
59
+ /**
60
+ * Hook to access the Reanimated TrueSheet context.
61
+ * Returns the shared values for sheet position and detent index that can be used in animations.
62
+ *
63
+ * @throws Error if used outside of ReanimatedTrueSheetProvider
64
+ *
65
+ * @example
66
+ * ```tsx
67
+ * import { useReanimatedTrueSheet } from '@lodev09/react-native-true-sheet'
68
+ * import { useAnimatedStyle } from 'react-native-reanimated'
69
+ *
70
+ * function MyComponent() {
71
+ * const { animatedPosition, animatedIndex } = useReanimatedTrueSheet()
72
+ *
73
+ * const animatedStyle = useAnimatedStyle(() => ({
74
+ * transform: [{ translateY: -animatedPosition.value }]
75
+ * }))
76
+ *
77
+ * return <Animated.View style={animatedStyle}>...</Animated.View>
78
+ * }
79
+ * ```
80
+ */
81
+ export const useReanimatedTrueSheet = (): ReanimatedTrueSheetContextValue => {
82
+ const context = useContext(ReanimatedTrueSheetContext);
83
+
84
+ if (!context) {
85
+ throw new Error(
86
+ 'useReanimatedTrueSheet must be used within a ReanimatedTrueSheetProvider. ' +
87
+ 'Make sure to wrap your component tree with <ReanimatedTrueSheetProvider>.'
88
+ );
89
+ }
90
+
91
+ return context;
92
+ };
@@ -0,0 +1,3 @@
1
+ export * from './ReanimatedTrueSheet';
2
+ export * from './ReanimatedTrueSheetProvider';
3
+ export * from './useReanimatedPositionChangeHandler';
@@ -0,0 +1,26 @@
1
+ import { useEvent, useHandler } from 'react-native-reanimated';
2
+ import type { PositionChangeEvent, PositionChangeEventPayload } from '../TrueSheet.types';
3
+ import type { DependencyList } from 'react-native-reanimated/lib/typescript/hook/commonTypes';
4
+
5
+ type PositionChangeHandler = (
6
+ payload: PositionChangeEventPayload,
7
+ context: Record<string, unknown>
8
+ ) => void;
9
+
10
+ export const useReanimatedPositionChangeHandler = (
11
+ handler: PositionChangeHandler,
12
+ dependencies: DependencyList = []
13
+ ) => {
14
+ const { context, doDependenciesDiffer } = useHandler({ onPositionChange: handler }, dependencies);
15
+
16
+ return useEvent<PositionChangeEvent>(
17
+ (event) => {
18
+ 'worklet';
19
+ if (handler && event.eventName.endsWith('onPositionChange')) {
20
+ handler(event, context);
21
+ }
22
+ },
23
+ ['onPositionChange'],
24
+ doDependenciesDiffer
25
+ );
26
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * TurboModule spec for TrueSheet imperative API
3
+ * Provides promise-based async operations with proper error handling
4
+ *
5
+ * @format
6
+ */
7
+
8
+ import type { TurboModule } from 'react-native';
9
+ import { TurboModuleRegistry } from 'react-native';
10
+
11
+ interface Spec extends TurboModule {
12
+ /**
13
+ * Present a sheet by reference
14
+ * @param viewTag - Native view tag of the sheet component
15
+ * @param index - Detent index to present at
16
+ * @returns Promise that resolves when sheet is fully presented
17
+ * @throws PRESENT_FAILED if presentation fails
18
+ */
19
+ presentByRef(viewTag: number, index: number): Promise<void>;
20
+
21
+ /**
22
+ * Dismiss a sheet by reference
23
+ * @param viewTag - Native view tag of the sheet component
24
+ * @returns Promise that resolves when sheet is fully dismissed
25
+ * @throws DISMISS_FAILED if dismissal fails
26
+ */
27
+ dismissByRef(viewTag: number): Promise<void>;
28
+
29
+ /**
30
+ * Resize a sheet to a different index by reference
31
+ * @param viewTag - Native view tag of the sheet component
32
+ * @param index - New detent index
33
+ * @returns Promise that resolves when resize is complete
34
+ */
35
+ resizeByRef(viewTag: number, index: number): Promise<void>;
36
+ }
37
+
38
+ export default TurboModuleRegistry.get<Spec>('TrueSheetModule');
package/TrueSheet.podspec DELETED
@@ -1,49 +0,0 @@
1
- require "json"
2
-
3
- package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
- folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
5
-
6
- Pod::Spec.new do |s|
7
- s.name = "TrueSheet"
8
- s.version = package["version"]
9
- s.summary = package["description"]
10
- s.homepage = package["homepage"]
11
- s.license = package["license"]
12
- s.authors = package["author"]
13
-
14
- s.platforms = { :ios => "13.4" }
15
- s.source = { :git => "https://github.com/lodev09/react-native-true-sheet.git", :tag => "#{s.version}" }
16
-
17
- s.source_files = "ios/**/*.{h,m,mm,swift}"
18
-
19
- if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
20
- s.pod_target_xcconfig = {
21
- # Detect if new arch is enabled in Swift code
22
- "OTHER_SWIFT_FLAGS" => "-DRCT_NEW_ARCH_ENABLED"
23
- }
24
- end
25
-
26
- # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
27
- # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
28
- if respond_to?(:install_modules_dependencies, true)
29
- install_modules_dependencies(s)
30
- else
31
- s.dependency "React-Core"
32
-
33
- # Don't install the dependencies when we run `pod install` in the old architecture.
34
- if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
35
- s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
36
- s.pod_target_xcconfig = s.pod_target_xcconfig | {
37
- "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
38
- "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
39
- "CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
40
- }
41
- s.dependency "React-RCTFabric"
42
- s.dependency "React-Codegen"
43
- s.dependency "RCT-Folly"
44
- s.dependency "RCTRequired"
45
- s.dependency "RCTTypeSafety"
46
- s.dependency "ReactCommon/turbomodule/core"
47
- end
48
- end
49
- end
@@ -1,400 +0,0 @@
1
- package com.lodev09.truesheet
2
-
3
- import android.annotation.SuppressLint
4
- import android.graphics.Color
5
- import android.graphics.drawable.ShapeDrawable
6
- import android.graphics.drawable.shapes.RoundRectShape
7
- import android.view.View
8
- import android.view.ViewGroup
9
- import android.view.WindowManager
10
- import com.facebook.react.uimanager.ThemedReactContext
11
- import com.google.android.material.bottomsheet.BottomSheetBehavior
12
- import com.google.android.material.bottomsheet.BottomSheetDialog
13
- import com.lodev09.truesheet.core.KeyboardManager
14
- import com.lodev09.truesheet.core.RootSheetView
15
- import com.lodev09.truesheet.core.Utils
16
-
17
- data class SizeInfo(val index: Int, val value: Float)
18
-
19
- @SuppressLint("ClickableViewAccessibility")
20
- class TrueSheetDialog(private val reactContext: ThemedReactContext, private val rootSheetView: RootSheetView) :
21
- BottomSheetDialog(reactContext) {
22
-
23
- private var keyboardManager = KeyboardManager(reactContext)
24
- private var windowAnimation: Int = 0
25
-
26
- // First child of the rootSheetView
27
- private val containerView: ViewGroup?
28
- get() = if (rootSheetView.childCount > 0) {
29
- rootSheetView.getChildAt(0) as? ViewGroup
30
- } else {
31
- null
32
- }
33
-
34
- private val sheetContainerView: ViewGroup?
35
- get() = rootSheetView.parent?.let { it as? ViewGroup }
36
-
37
- /**
38
- * Specify whether the sheet background is dimmed.
39
- * Set to `false` to allow interaction with the background components.
40
- */
41
- var dimmed = true
42
-
43
- /**
44
- * The size index that the sheet should start to dim the background.
45
- * This is ignored if `dimmed` is set to `false`.
46
- */
47
- var dimmedIndex = 0
48
-
49
- /**
50
- * The maximum window height
51
- */
52
- var maxScreenHeight = 0
53
-
54
- var contentHeight = 0
55
- var footerHeight = 0
56
- var maxSheetHeight: Int? = null
57
-
58
- var edgeToEdge: Boolean = false
59
- set(value) {
60
- field = value
61
- maxScreenHeight = Utils.screenHeight(reactContext, value)
62
- }
63
-
64
- var dismissible: Boolean = true
65
- set(value) {
66
- field = value
67
- setCanceledOnTouchOutside(value)
68
- setCancelable(value)
69
-
70
- behavior.isHideable = value
71
- }
72
-
73
- var cornerRadius: Float = 0f
74
- var backgroundColor: Int = Color.WHITE
75
-
76
- // 1st child is the content view
77
- val contentView: ViewGroup?
78
- get() = containerView?.getChildAt(0) as? ViewGroup
79
-
80
- // 2nd child is the footer view
81
- val footerView: ViewGroup?
82
- get() = containerView?.getChildAt(1) as? ViewGroup
83
-
84
- var sizes: Array<Any> = arrayOf("medium", "large")
85
-
86
- init {
87
- setContentView(rootSheetView)
88
-
89
- sheetContainerView?.setBackgroundColor(backgroundColor)
90
- sheetContainerView?.clipToOutline = true
91
-
92
- // Setup window params to adjust layout based on Keyboard state
93
- window?.apply {
94
- // Store current windowAnimation value to toggle later
95
- windowAnimation = attributes.windowAnimations
96
- }
97
-
98
- // Update the usable sheet height
99
- maxScreenHeight = Utils.screenHeight(reactContext, edgeToEdge)
100
- }
101
-
102
- override fun getEdgeToEdgeEnabled(): Boolean = edgeToEdge || super.getEdgeToEdgeEnabled()
103
-
104
- override fun onStart() {
105
- super.onStart()
106
-
107
- if (edgeToEdge) {
108
- window?.apply {
109
- setFlags(
110
- WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
111
- WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
112
- )
113
-
114
- decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
115
- }
116
- }
117
- }
118
-
119
- /**
120
- * Setup background color and corner radius.
121
- */
122
- fun setupBackground() {
123
- val outerRadii = floatArrayOf(
124
- cornerRadius,
125
- cornerRadius,
126
- cornerRadius,
127
- cornerRadius,
128
- 0f,
129
- 0f,
130
- 0f,
131
- 0f
132
- )
133
-
134
- val background = ShapeDrawable(RoundRectShape(outerRadii, null, null))
135
-
136
- // Use current background color
137
- background.paint.color = backgroundColor
138
- sheetContainerView?.background = background
139
- }
140
-
141
- /**
142
- * Setup dimmed sheet.
143
- * `dimmedIndex` will further customize the dimming behavior.
144
- */
145
- fun setupDimmedBackground(sizeIndex: Int) {
146
- window?.apply {
147
- val view = findViewById<View>(com.google.android.material.R.id.touch_outside)
148
-
149
- if (dimmed && sizeIndex >= dimmedIndex) {
150
- // Remove touch listener
151
- view.setOnTouchListener(null)
152
-
153
- // Add the dimmed background
154
- setFlags(
155
- WindowManager.LayoutParams.FLAG_DIM_BEHIND,
156
- WindowManager.LayoutParams.FLAG_DIM_BEHIND
157
- )
158
-
159
- setCanceledOnTouchOutside(dismissible)
160
- } else {
161
- // Override the background touch and pass it to the components outside
162
- view.setOnTouchListener { v, event ->
163
- event.setLocation(event.rawX - v.x, event.rawY - v.y)
164
- reactContext.currentActivity?.dispatchTouchEvent(event)
165
- false
166
- }
167
-
168
- // Remove the dimmed background
169
- clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
170
-
171
- setCanceledOnTouchOutside(false)
172
- }
173
- }
174
- }
175
-
176
- fun resetAnimation() {
177
- window?.apply {
178
- setWindowAnimations(windowAnimation)
179
- }
180
- }
181
-
182
- /**
183
- * Present the sheet.
184
- */
185
- fun present(sizeIndex: Int, animated: Boolean = true) {
186
- setupDimmedBackground(sizeIndex)
187
- if (isShowing) {
188
- setStateForSizeIndex(sizeIndex)
189
- } else {
190
- configure()
191
- setStateForSizeIndex(sizeIndex)
192
-
193
- if (!animated) {
194
- // Disable animation
195
- window?.setWindowAnimations(0)
196
- }
197
-
198
- show()
199
- }
200
- }
201
-
202
- fun positionFooter() {
203
- footerView?.let { footer ->
204
- sheetContainerView?.let { container ->
205
- footer.y = (maxScreenHeight - container.top - footerHeight).toFloat()
206
- }
207
- }
208
- }
209
-
210
- /**
211
- * Set the state based for the given size index.
212
- */
213
- private fun setStateForSizeIndex(index: Int) {
214
- behavior.state = getStateForSizeIndex(index)
215
- }
216
-
217
- /**
218
- * Get the height value based on the size config value.
219
- */
220
- private fun getSizeHeight(size: Any): Int {
221
- val height: Int =
222
- when (size) {
223
- is Double -> Utils.toPixel(size).toInt()
224
-
225
- is Int -> Utils.toPixel(size.toDouble()).toInt()
226
-
227
- is String -> {
228
- when (size) {
229
- "auto" -> contentHeight + footerHeight
230
-
231
- "large" -> maxScreenHeight
232
-
233
- "medium" -> (maxScreenHeight * 0.50).toInt()
234
-
235
- "small" -> (maxScreenHeight * 0.25).toInt()
236
-
237
- else -> {
238
- if (size.endsWith('%')) {
239
- val percent = size.trim('%').toDoubleOrNull()
240
- if (percent == null) {
241
- 0
242
- } else {
243
- ((percent / 100) * maxScreenHeight).toInt()
244
- }
245
- } else {
246
- val fixedHeight = size.toDoubleOrNull()
247
- if (fixedHeight == null) {
248
- 0
249
- } else {
250
- Utils.toPixel(fixedHeight).toInt()
251
- }
252
- }
253
- }
254
- }
255
- }
256
-
257
- else -> (maxScreenHeight * 0.5).toInt()
258
- }
259
-
260
- return maxSheetHeight?.let { minOf(height, it, maxScreenHeight) } ?: minOf(height, maxScreenHeight)
261
- }
262
-
263
- /**
264
- * Determines the state based from the given size index.
265
- */
266
- private fun getStateForSizeIndex(index: Int) =
267
- when (sizes.size) {
268
- 1 -> BottomSheetBehavior.STATE_EXPANDED
269
-
270
- 2 -> {
271
- when (index) {
272
- 0 -> BottomSheetBehavior.STATE_COLLAPSED
273
- 1 -> BottomSheetBehavior.STATE_EXPANDED
274
- else -> BottomSheetBehavior.STATE_HIDDEN
275
- }
276
- }
277
-
278
- 3 -> {
279
- when (index) {
280
- 0 -> BottomSheetBehavior.STATE_COLLAPSED
281
- 1 -> BottomSheetBehavior.STATE_HALF_EXPANDED
282
- 2 -> BottomSheetBehavior.STATE_EXPANDED
283
- else -> BottomSheetBehavior.STATE_HIDDEN
284
- }
285
- }
286
-
287
- else -> BottomSheetBehavior.STATE_HIDDEN
288
- }
289
-
290
- /**
291
- * Handle keyboard state changes and adjust maxScreenHeight (sheet max height) accordingly.
292
- * Also update footer's Y position.
293
- */
294
- fun registerKeyboardManager() {
295
- keyboardManager.registerKeyboardListener(object : KeyboardManager.OnKeyboardChangeListener {
296
- override fun onKeyboardStateChange(isVisible: Boolean, visibleHeight: Int?) {
297
- maxScreenHeight = when (isVisible) {
298
- true -> visibleHeight ?: 0
299
- else -> Utils.screenHeight(reactContext, edgeToEdge)
300
- }
301
-
302
- positionFooter()
303
- }
304
- })
305
- }
306
-
307
- fun setOnSizeChangeListener(listener: (w: Int, h: Int) -> Unit) {
308
- rootSheetView.sizeChangeListener = listener
309
- }
310
-
311
- /**
312
- * Remove keyboard listener.
313
- */
314
- fun unregisterKeyboardManager() {
315
- keyboardManager.unregisterKeyboardListener()
316
- }
317
-
318
- /**
319
- * Configure the sheet based from the size preference.
320
- */
321
- fun configure() {
322
- // Configure sheet sizes
323
- behavior.apply {
324
- skipCollapsed = false
325
- isFitToContents = true
326
-
327
- // m3 max width 640dp
328
- maxWidth = Utils.toPixel(640.0).toInt()
329
-
330
- when (sizes.size) {
331
- 1 -> {
332
- maxHeight = getSizeHeight(sizes[0])
333
- skipCollapsed = true
334
- }
335
-
336
- 2 -> {
337
- setPeekHeight(getSizeHeight(sizes[0]), isShowing)
338
- maxHeight = getSizeHeight(sizes[1])
339
- }
340
-
341
- 3 -> {
342
- // Enables half expanded
343
- isFitToContents = false
344
-
345
- setPeekHeight(getSizeHeight(sizes[0]), isShowing)
346
-
347
- halfExpandedRatio = minOf(getSizeHeight(sizes[1]).toFloat() / maxScreenHeight.toFloat(), 1.0f)
348
- maxHeight = getSizeHeight(sizes[2])
349
- }
350
- }
351
- }
352
- }
353
-
354
- /**
355
- * Get the SizeInfo data by state.
356
- */
357
- fun getSizeInfoForState(state: Int): SizeInfo? =
358
- when (sizes.size) {
359
- 1 -> {
360
- when (state) {
361
- BottomSheetBehavior.STATE_EXPANDED -> SizeInfo(0, Utils.toDIP(behavior.maxHeight.toFloat()))
362
- else -> null
363
- }
364
- }
365
-
366
- 2 -> {
367
- when (state) {
368
- BottomSheetBehavior.STATE_COLLAPSED -> SizeInfo(0, Utils.toDIP(behavior.peekHeight.toFloat()))
369
- BottomSheetBehavior.STATE_EXPANDED -> SizeInfo(1, Utils.toDIP(behavior.maxHeight.toFloat()))
370
- else -> null
371
- }
372
- }
373
-
374
- 3 -> {
375
- when (state) {
376
- BottomSheetBehavior.STATE_COLLAPSED -> SizeInfo(0, Utils.toDIP(behavior.peekHeight.toFloat()))
377
-
378
- BottomSheetBehavior.STATE_HALF_EXPANDED -> {
379
- val height = behavior.halfExpandedRatio * maxScreenHeight
380
- SizeInfo(1, Utils.toDIP(height))
381
- }
382
-
383
- BottomSheetBehavior.STATE_EXPANDED -> SizeInfo(2, Utils.toDIP(behavior.maxHeight.toFloat()))
384
-
385
- else -> null
386
- }
387
- }
388
-
389
- else -> null
390
- }
391
-
392
- /**
393
- * Get SizeInfo data for given size index.
394
- */
395
- fun getSizeInfoForIndex(index: Int) = getSizeInfoForState(getStateForSizeIndex(index)) ?: SizeInfo(0, 0f)
396
-
397
- companion object {
398
- const val TAG = "TrueSheetView"
399
- }
400
- }