@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
package/src/TrueSheet.tsx CHANGED
@@ -1,213 +1,293 @@
1
- import { PureComponent, Component, type RefObject, createRef, type ReactNode } from 'react'
2
1
  import {
3
- requireNativeComponent,
4
- Platform,
5
- findNodeHandle,
6
- View,
7
- type NativeMethods,
8
- type ViewStyle,
9
- type NativeSyntheticEvent,
10
- type LayoutChangeEvent,
11
- type ProcessedColorValue,
12
- processColor,
13
- } from 'react-native'
2
+ PureComponent,
3
+ type RefObject,
4
+ createRef,
5
+ type ReactNode,
6
+ type ComponentRef,
7
+ isValidElement,
8
+ createElement,
9
+ } from 'react';
14
10
 
15
11
  import type {
16
12
  TrueSheetProps,
17
13
  DragBeginEvent,
18
14
  DragChangeEvent,
19
15
  DragEndEvent,
16
+ DetentChangeEvent,
17
+ WillPresentEvent,
18
+ DidPresentEvent,
19
+ PositionChangeEvent,
20
+ DidDismissEvent,
21
+ WillDismissEvent,
22
+ MountEvent,
20
23
  SizeChangeEvent,
21
- PresentEvent,
22
- } from './TrueSheet.types'
23
- import { TrueSheetModule } from './TrueSheetModule'
24
- import { TrueSheetGrabber } from './TrueSheetGrabber'
25
- import { TrueSheetFooter } from './TrueSheetFooter'
24
+ } from './TrueSheet.types';
25
+ import TrueSheetViewNativeComponent from './fabric/TrueSheetViewNativeComponent';
26
+ import TrueSheetContainerViewNativeComponent from './fabric/TrueSheetContainerViewNativeComponent';
27
+ import TrueSheetContentViewNativeComponent from './fabric/TrueSheetContentViewNativeComponent';
28
+ import TrueSheetFooterViewNativeComponent from './fabric/TrueSheetFooterViewNativeComponent';
29
+
30
+ import TrueSheetModule from './specs/NativeTrueSheetModule';
31
+
32
+ import { Platform, processColor, StyleSheet, findNodeHandle } from 'react-native';
26
33
 
27
- const NATIVE_COMPONENT_NAME = 'TrueSheetView'
28
34
  const LINKING_ERROR =
29
35
  `The package '@lodev09/react-native-true-sheet' doesn't seem to be linked. Make sure: \n\n` +
30
36
  Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
31
37
  '- You rebuilt the app after installing the package\n' +
32
- '- You are not using Expo Go\n'
33
-
34
- export type ContainerSizeChangeEvent = NativeSyntheticEvent<{ width: number; height: number }>
38
+ '- You are not using Expo Go\n' +
39
+ '- You are using the new architecture (Fabric)\n';
35
40
 
36
- interface TrueSheetNativeViewProps extends Omit<TrueSheetProps, 'backgroundColor'> {
37
- contentHeight?: number
38
- footerHeight?: number
39
- background?: ProcessedColorValue | null
40
- scrollableHandle: number | null
41
- onContainerSizeChange: (event: ContainerSizeChangeEvent) => void
41
+ if (!TrueSheetModule) {
42
+ throw new Error(LINKING_ERROR);
42
43
  }
43
44
 
44
- type NativeRef = Component<TrueSheetNativeViewProps> & Readonly<NativeMethods>
45
+ type NativeRef = ComponentRef<typeof TrueSheetViewNativeComponent>;
45
46
 
46
47
  interface TrueSheetState {
47
- containerWidth?: number
48
- containerHeight?: number
49
- contentHeight?: number
50
- footerHeight?: number
51
- scrollableHandle: number | null
52
- }
53
-
54
- const TrueSheetNativeView = requireNativeComponent<TrueSheetNativeViewProps>(NATIVE_COMPONENT_NAME)
55
-
56
- if (!TrueSheetNativeView) {
57
- throw new Error(LINKING_ERROR)
48
+ shouldRenderNativeView: boolean;
49
+ containerWidth?: number;
50
+ containerHeight?: number;
58
51
  }
59
52
 
60
53
  export class TrueSheet extends PureComponent<TrueSheetProps, TrueSheetState> {
61
- displayName = 'TrueSheet'
54
+ displayName = 'TrueSheet';
55
+
56
+ private readonly nativeRef: RefObject<NativeRef | null>;
62
57
 
63
- private readonly ref: RefObject<NativeRef>
58
+ /**
59
+ * Map of sheet names against their instances.
60
+ */
61
+ private static readonly instances: { [name: string]: TrueSheet } = {};
64
62
 
65
63
  /**
66
- * Map of sheet names against their handle.
64
+ * Resolver to be called when mount event is received
67
65
  */
68
- private static readonly handles: { [name: string]: number } = {}
66
+ private presentationResolver: (() => void) | null = null;
69
67
 
70
68
  constructor(props: TrueSheetProps) {
71
- super(props)
69
+ super(props);
72
70
 
73
- this.ref = createRef<NativeRef>()
71
+ this.nativeRef = createRef<NativeRef>();
74
72
 
75
- this.onMount = this.onMount.bind(this)
76
- this.onDismiss = this.onDismiss.bind(this)
77
- this.onPresent = this.onPresent.bind(this)
78
- this.onSizeChange = this.onSizeChange.bind(this)
79
- this.onDragBegin = this.onDragBegin.bind(this)
80
- this.onDragChange = this.onDragChange.bind(this)
81
- this.onDragEnd = this.onDragEnd.bind(this)
82
- this.onContentLayout = this.onContentLayout.bind(this)
83
- this.onFooterLayout = this.onFooterLayout.bind(this)
84
- this.onContainerSizeChange = this.onContainerSizeChange.bind(this)
73
+ this.validateDetents();
74
+
75
+ // Lazy load by default, except when initialDetentIndex is set (for auto-presentation)
76
+ const shouldRenderImmediately =
77
+ props.initialDetentIndex !== undefined && props.initialDetentIndex >= 0;
85
78
 
86
79
  this.state = {
80
+ shouldRenderNativeView: shouldRenderImmediately,
87
81
  containerWidth: undefined,
88
82
  containerHeight: undefined,
89
- contentHeight: undefined,
90
- footerHeight: undefined,
91
- scrollableHandle: null,
83
+ };
84
+
85
+ this.onMount = this.onMount.bind(this);
86
+ this.onWillDismiss = this.onWillDismiss.bind(this);
87
+ this.onDidDismiss = this.onDidDismiss.bind(this);
88
+ this.onWillPresent = this.onWillPresent.bind(this);
89
+ this.onDidPresent = this.onDidPresent.bind(this);
90
+ this.onDetentChange = this.onDetentChange.bind(this);
91
+ this.onDragBegin = this.onDragBegin.bind(this);
92
+ this.onDragChange = this.onDragChange.bind(this);
93
+ this.onDragEnd = this.onDragEnd.bind(this);
94
+ this.onPositionChange = this.onPositionChange.bind(this);
95
+ this.onSizeChange = this.onSizeChange.bind(this);
96
+ }
97
+
98
+ private validateDetents(): void {
99
+ const { detents, initialDetentIndex } = this.props;
100
+
101
+ // Warn if detents length exceeds 3
102
+ if (detents && detents.length > 3) {
103
+ console.warn(
104
+ `TrueSheet: detents array has ${detents.length} items but maximum is 3. Only the first 3 will be used.`
105
+ );
106
+ }
107
+
108
+ // Warn for invalid detent fractions
109
+ if (detents) {
110
+ detents.forEach((detent, index) => {
111
+ if (detent !== 'auto' && typeof detent === 'number') {
112
+ if (detent <= 0 || detent > 1) {
113
+ console.warn(
114
+ `TrueSheet: detent at index ${index} (${detent}) should be between 0 and 1. It will be clamped.`
115
+ );
116
+ }
117
+ }
118
+ });
119
+ }
120
+
121
+ // Validate initialDetentIndex bounds
122
+ if (initialDetentIndex !== undefined && initialDetentIndex >= 0) {
123
+ const detentsLength = Math.min(detents?.length ?? 2, 3); // Max 3 detents
124
+ if (initialDetentIndex >= detentsLength) {
125
+ throw new Error(
126
+ `TrueSheet: initialDetentIndex (${initialDetentIndex}) is out of bounds. detents array has ${detentsLength} item(s)`
127
+ );
128
+ }
92
129
  }
93
130
  }
94
131
 
95
- private static getHandle(name: string) {
96
- const handle = TrueSheet.handles[name]
97
- if (!handle) {
98
- console.warn(`Could not get native view tag from "${name}". Check your name prop.`)
99
- return
132
+ private static getInstance(name: string) {
133
+ const instance = TrueSheet.instances[name];
134
+ if (!instance) {
135
+ console.warn(`Could not find TrueSheet instance with name "${name}". Check your name prop.`);
136
+ return;
100
137
  }
101
138
 
102
- return handle
139
+ return instance;
103
140
  }
104
141
 
105
- /**
106
- * Present the sheet by given `name`.
107
- * See `name` prop.
108
- */
109
- public static async present(name: string, index: number = 0) {
110
- const handle = TrueSheet.getHandle(name)
111
- if (!handle) return
142
+ private get handle(): number {
143
+ const nodeHandle = findNodeHandle(this.nativeRef.current);
144
+ if (nodeHandle == null || nodeHandle === -1) {
145
+ throw new Error('Could not get native view tag');
146
+ }
112
147
 
113
- await TrueSheetModule.present(handle, index)
148
+ return nodeHandle;
114
149
  }
115
150
 
116
151
  /**
117
- * Dismiss the sheet by given `name`.
118
- * See `name` prop.
152
+ * Present the sheet by given `name` (Promise-based)
153
+ * @param name - Sheet name (must match sheet's name prop)
154
+ * @param index - Detent index (default: 0)
155
+ * @returns Promise that resolves when sheet is fully presented
156
+ * @throws Error if sheet not found or presentation fails
119
157
  */
120
- public static async dismiss(name: string) {
121
- const handle = TrueSheet.getHandle(name)
122
- if (!handle) return
158
+ public static async present(name: string, index: number = 0): Promise<void> {
159
+ const instance = TrueSheet.getInstance(name);
160
+ if (!instance) {
161
+ throw new Error(`Sheet with name "${name}" not found`);
162
+ }
123
163
 
124
- await TrueSheetModule.dismiss(handle)
164
+ return instance.present(index);
125
165
  }
126
166
 
127
167
  /**
128
- * Resize the sheet by given `name`.
129
- * See `name` prop.
168
+ * Dismiss the sheet by given `name` (Promise-based)
169
+ * @param name - Sheet name
170
+ * @returns Promise that resolves when sheet is fully dismissed
171
+ * @throws Error if sheet not found or dismissal fails
130
172
  */
131
- public static async resize(name: string, index: number) {
132
- await TrueSheet.present(name, index)
173
+ public static async dismiss(name: string): Promise<void> {
174
+ const instance = TrueSheet.getInstance(name);
175
+ if (!instance) {
176
+ throw new Error(`Sheet with name "${name}" not found`);
177
+ }
178
+
179
+ return instance.dismiss();
133
180
  }
134
181
 
135
- private get handle(): number {
136
- const nodeHandle = findNodeHandle(this.ref.current)
137
- if (nodeHandle == null || nodeHandle === -1) {
138
- throw new Error('Could not get native view tag')
182
+ /**
183
+ * Resize the sheet by given `name` (Promise-based)
184
+ * @param name - Sheet name
185
+ * @param index - New detent index
186
+ * @returns Promise that resolves when resize is complete
187
+ * @throws Error if sheet not found
188
+ */
189
+ public static async resize(name: string, index: number): Promise<void> {
190
+ const instance = TrueSheet.getInstance(name);
191
+ if (!instance) {
192
+ throw new Error(`Sheet with name "${name}" not found`);
139
193
  }
140
194
 
141
- return nodeHandle
195
+ return instance.resize(index);
142
196
  }
143
197
 
144
- private updateState(): void {
145
- const scrollableHandle = this.props.scrollRef?.current
146
- ? findNodeHandle(this.props.scrollRef.current)
147
- : null
148
-
198
+ private registerInstance(): void {
149
199
  if (this.props.name) {
150
- TrueSheet.handles[this.props.name] = this.handle
200
+ TrueSheet.instances[this.props.name] = this;
151
201
  }
152
-
153
- this.setState({
154
- scrollableHandle,
155
- })
156
202
  }
157
203
 
158
- private onSizeChange(event: SizeChangeEvent): void {
159
- this.props.onSizeChange?.(event)
204
+ private unregisterInstance(): void {
205
+ if (this.props.name) {
206
+ delete TrueSheet.instances[this.props.name];
207
+ }
160
208
  }
161
209
 
162
- private onContainerSizeChange(event: ContainerSizeChangeEvent): void {
163
- this.setState({
164
- containerWidth: event.nativeEvent.width,
165
- containerHeight: event.nativeEvent.height,
166
- })
210
+ private onDetentChange(event: DetentChangeEvent): void {
211
+ this.props.onDetentChange?.(event);
167
212
  }
168
213
 
169
- private onPresent(event: PresentEvent): void {
170
- this.props.onPresent?.(event)
214
+ private onWillPresent(event: WillPresentEvent): void {
215
+ this.props.onWillPresent?.(event);
171
216
  }
172
217
 
173
- private onFooterLayout(event: LayoutChangeEvent): void {
174
- this.setState({
175
- footerHeight: event.nativeEvent.layout.height,
176
- })
218
+ private onDidPresent(event: DidPresentEvent): void {
219
+ this.props.onDidPresent?.(event);
177
220
  }
178
221
 
179
- private onContentLayout(event: LayoutChangeEvent): void {
180
- this.setState({
181
- contentHeight: event.nativeEvent.layout.height,
182
- })
222
+ private onWillDismiss(event: WillDismissEvent): void {
223
+ this.props.onWillDismiss?.(event);
183
224
  }
184
225
 
185
- private onDismiss(): void {
186
- this.props.onDismiss?.()
226
+ private onDidDismiss(event: DidDismissEvent): void {
227
+ // Clean up native view after dismiss for lazy loading
228
+ this.setState({ shouldRenderNativeView: false });
229
+ this.props.onDidDismiss?.(event);
187
230
  }
188
231
 
189
- private onMount(): void {
190
- this.props.onMount?.()
232
+ private onMount(event: MountEvent): void {
233
+ // Resolve the mount promise if waiting
234
+ if (this.presentationResolver) {
235
+ this.presentationResolver();
236
+ this.presentationResolver = null;
237
+ }
238
+
239
+ this.props.onMount?.(event);
191
240
  }
192
241
 
193
242
  private onDragBegin(event: DragBeginEvent): void {
194
- this.props.onDragBegin?.(event)
243
+ this.props.onDragBegin?.(event);
195
244
  }
196
245
 
197
246
  private onDragChange(event: DragChangeEvent): void {
198
- this.props.onDragChange?.(event)
247
+ this.props.onDragChange?.(event);
199
248
  }
200
249
 
201
250
  private onDragEnd(event: DragEndEvent): void {
202
- this.props.onDragEnd?.(event)
251
+ this.props.onDragEnd?.(event);
252
+ }
253
+
254
+ private onPositionChange(event: PositionChangeEvent): void {
255
+ this.props.onPositionChange?.(event);
256
+ }
257
+
258
+ private onSizeChange(event: SizeChangeEvent): void {
259
+ const { width, height } = event.nativeEvent;
260
+
261
+ // Update container view dimensions in state
262
+ this.setState({
263
+ containerWidth: width,
264
+ containerHeight: height,
265
+ });
266
+
267
+ // Internal handler only - not exposed to users
203
268
  }
204
269
 
205
270
  /**
206
- * Present the sheet. Optionally accepts a size `index`.
207
- * See `sizes` prop
271
+ * Present the Sheet by `index` (Promise-based)
272
+ * @param index - Detent index (default: 0)
208
273
  */
209
274
  public async present(index: number = 0): Promise<void> {
210
- await TrueSheetModule.present(this.handle, index)
275
+ const detentsLength = Math.min(this.props.detents?.length ?? 2, 3); // Max 3 detents
276
+ if (index < 0 || index >= detentsLength) {
277
+ throw new Error(
278
+ `TrueSheet: present index (${index}) is out of bounds. detents array has ${detentsLength} item(s)`
279
+ );
280
+ }
281
+
282
+ // Lazy load: render native view if not already rendered
283
+ if (!this.state.shouldRenderNativeView) {
284
+ await new Promise<void>((resolve) => {
285
+ this.presentationResolver = resolve;
286
+ this.setState({ shouldRenderNativeView: true });
287
+ });
288
+ }
289
+
290
+ return TrueSheetModule?.presentByRef(this.handle, index);
211
291
  }
212
292
 
213
293
  /**
@@ -215,113 +295,130 @@ export class TrueSheet extends PureComponent<TrueSheetProps, TrueSheetState> {
215
295
  * This is an alias of the `present(index)` method.
216
296
  */
217
297
  public async resize(index: number): Promise<void> {
218
- await this.present(index)
298
+ await this.present(index);
219
299
  }
220
300
 
221
301
  /**
222
302
  * Dismisses the Sheet
223
303
  */
224
304
  public async dismiss(): Promise<void> {
225
- await TrueSheetModule.dismiss(this.handle)
305
+ return TrueSheetModule?.dismissByRef(this.handle);
226
306
  }
227
307
 
228
308
  componentDidMount(): void {
229
- if (this.props.sizes && this.props.sizes.length > 3) {
230
- console.warn(
231
- 'TrueSheet only supports a maximum of 3 sizes; collapsed, half-expanded and expanded. Check your `sizes` prop.'
232
- )
233
- }
309
+ this.registerInstance();
310
+ }
311
+
312
+ componentDidUpdate(prevProps: TrueSheetProps): void {
313
+ this.registerInstance();
234
314
 
235
- this.updateState()
315
+ // Validate when detents prop changes
316
+ if (prevProps.detents !== this.props.detents) {
317
+ this.validateDetents();
318
+ }
236
319
  }
237
320
 
238
- componentDidUpdate(): void {
239
- this.updateState()
321
+ componentWillUnmount(): void {
322
+ this.unregisterInstance();
323
+
324
+ // Clean up presentation resolver
325
+ this.presentationResolver = null;
240
326
  }
241
327
 
242
328
  render(): ReactNode {
243
329
  const {
244
- sizes = ['medium', 'large'],
330
+ detents = [0.5, 1],
245
331
  backgroundColor = 'white',
246
332
  dismissible = true,
247
333
  grabber = true,
248
334
  dimmed = true,
249
- initialIndexAnimated = true,
250
- edgeToEdge = false,
335
+ initialDetentIndex = -1,
336
+ initialDetentAnimated = true,
251
337
  keyboardMode = 'resize',
252
- initialIndex,
253
338
  dimmedIndex,
254
- grabberProps,
255
339
  blurTint,
256
340
  cornerRadius,
257
341
  maxHeight,
258
- FooterComponent,
259
- style,
260
- contentContainerStyle,
342
+ edgeToEdgeFullScreen,
261
343
  children,
262
- ...rest
263
- } = this.props
344
+ style,
345
+ footer,
346
+ testID,
347
+ } = this.props;
348
+
349
+ // Trim to max 3 detents and clamp fractions
350
+ const resolvedDetents = detents.slice(0, 3).map((detent) => {
351
+ if (detent === 'auto' || detent === -1) return -1;
352
+
353
+ // Default to 0.1 if zero or below
354
+ if (detent <= 0) return 0.1;
355
+
356
+ // Clamp to maximum of 1
357
+ return Math.min(1, detent);
358
+ });
264
359
 
265
360
  return (
266
- <TrueSheetNativeView
267
- ref={this.ref}
268
- style={$nativeSheet}
269
- scrollableHandle={this.state.scrollableHandle}
270
- sizes={sizes}
361
+ <TrueSheetViewNativeComponent
362
+ ref={this.nativeRef}
363
+ style={styles.sheetView}
364
+ detents={resolvedDetents}
271
365
  blurTint={blurTint}
272
- background={processColor(backgroundColor)}
366
+ background={(processColor(backgroundColor) as number) ?? 0}
273
367
  cornerRadius={cornerRadius}
274
- contentHeight={this.state.contentHeight}
275
- footerHeight={this.state.footerHeight}
276
368
  grabber={grabber}
277
369
  dimmed={dimmed}
278
370
  dimmedIndex={dimmedIndex}
279
- edgeToEdge={edgeToEdge}
280
- initialIndex={initialIndex}
281
- initialIndexAnimated={initialIndexAnimated}
282
371
  keyboardMode={keyboardMode}
372
+ initialDetentIndex={initialDetentIndex}
373
+ initialDetentAnimated={initialDetentAnimated}
283
374
  dismissible={dismissible}
284
375
  maxHeight={maxHeight}
376
+ edgeToEdgeFullScreen={edgeToEdgeFullScreen}
285
377
  onMount={this.onMount}
286
- onPresent={this.onPresent}
287
- onDismiss={this.onDismiss}
288
- onSizeChange={this.onSizeChange}
378
+ onWillPresent={this.onWillPresent}
379
+ onDidPresent={this.onDidPresent}
380
+ onWillDismiss={this.onWillDismiss}
381
+ onDidDismiss={this.onDidDismiss}
382
+ onDetentChange={this.onDetentChange}
289
383
  onDragBegin={this.onDragBegin}
290
384
  onDragChange={this.onDragChange}
291
385
  onDragEnd={this.onDragEnd}
292
- onContainerSizeChange={this.onContainerSizeChange}
386
+ onPositionChange={this.onPositionChange}
387
+ onSizeChange={this.onSizeChange}
293
388
  >
294
- <View
295
- collapsable={false}
296
- style={[
297
- {
298
- overflow: Platform.select({ ios: undefined, android: 'hidden' }),
299
-
300
- // Update the width on JS side.
301
- // New Arch interop does not support updating it in native :/
389
+ {this.state.shouldRenderNativeView && (
390
+ <TrueSheetContainerViewNativeComponent
391
+ testID={testID}
392
+ style={{
302
393
  width: this.state.containerWidth,
303
394
  height: this.state.containerHeight,
304
- },
305
- style,
306
- ]}
307
- {...rest}
308
- >
309
- <View collapsable={false} onLayout={this.onContentLayout} style={contentContainerStyle}>
310
- {children}
311
- </View>
312
- <View collapsable={false} onLayout={this.onFooterLayout}>
313
- <TrueSheetFooter Component={FooterComponent} />
314
- </View>
315
- {Platform.OS === 'android' && <TrueSheetGrabber visible={grabber} {...grabberProps} />}
316
- </View>
317
- </TrueSheetNativeView>
318
- )
395
+ }}
396
+ collapsable={false}
397
+ >
398
+ <TrueSheetContentViewNativeComponent style={style} collapsable={false}>
399
+ {children}
400
+ </TrueSheetContentViewNativeComponent>
401
+ {footer && (
402
+ <TrueSheetFooterViewNativeComponent style={styles.footer} collapsable={false}>
403
+ {isValidElement(footer) ? footer : createElement(footer)}
404
+ </TrueSheetFooterViewNativeComponent>
405
+ )}
406
+ </TrueSheetContainerViewNativeComponent>
407
+ )}
408
+ </TrueSheetViewNativeComponent>
409
+ );
319
410
  }
320
411
  }
321
412
 
322
- const $nativeSheet: ViewStyle = {
323
- position: 'absolute',
324
- width: '100%',
325
- left: -9999,
326
- zIndex: -9999,
327
- }
413
+ const styles = StyleSheet.create({
414
+ sheetView: {
415
+ position: 'absolute',
416
+ width: '100%',
417
+ zIndex: -9999,
418
+ },
419
+ footer: {
420
+ position: 'absolute',
421
+ left: 0,
422
+ right: 0,
423
+ },
424
+ });