@lodev09/react-native-true-sheet 3.0.0-beta.8 → 3.0.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 (98) hide show
  1. package/README.md +13 -6
  2. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +29 -33
  3. package/android/src/main/java/com/lodev09/truesheet/TrueSheetModule.kt +3 -1
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +48 -43
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +387 -88
  6. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +22 -4
  7. package/android/src/main/java/com/lodev09/truesheet/core/RNScreensFragmentObserver.kt +0 -5
  8. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDialogObserver.kt +67 -0
  9. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetGrabberView.kt +44 -0
  10. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetDragEvents.kt +71 -0
  11. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetFocusEvents.kt +65 -0
  12. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +94 -0
  13. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetStateEvents.kt +56 -0
  14. package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +37 -33
  15. package/android/src/main/res/anim/true_sheet_slide_in.xml +13 -0
  16. package/android/src/main/res/anim/true_sheet_slide_out.xml +13 -0
  17. package/android/src/main/res/values/styles.xml +13 -1
  18. package/ios/TrueSheetContainerView.mm +4 -0
  19. package/ios/TrueSheetContentView.h +2 -1
  20. package/ios/TrueSheetContentView.mm +91 -11
  21. package/ios/TrueSheetView.mm +65 -41
  22. package/ios/TrueSheetViewController.h +21 -10
  23. package/ios/TrueSheetViewController.mm +330 -165
  24. package/ios/core/TrueSheetBlurView.h +24 -0
  25. package/ios/{utils/ConversionUtil.mm → core/TrueSheetBlurView.mm} +65 -3
  26. package/ios/events/TrueSheetDragEvents.h +39 -0
  27. package/ios/events/TrueSheetDragEvents.mm +62 -0
  28. package/ios/events/{OnPositionChangeEvent.h → TrueSheetFocusEvents.h} +8 -5
  29. package/ios/events/TrueSheetFocusEvents.mm +49 -0
  30. package/ios/events/TrueSheetLifecycleEvents.h +40 -0
  31. package/ios/events/TrueSheetLifecycleEvents.mm +71 -0
  32. package/ios/events/TrueSheetStateEvents.h +35 -0
  33. package/ios/events/TrueSheetStateEvents.mm +49 -0
  34. package/ios/utils/GestureUtil.h +7 -0
  35. package/ios/utils/GestureUtil.mm +12 -0
  36. package/lib/module/TrueSheet.js +65 -12
  37. package/lib/module/TrueSheet.js.map +1 -1
  38. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +15 -5
  39. package/lib/module/index.js +0 -1
  40. package/lib/module/index.js.map +1 -1
  41. package/lib/module/reanimated/ReanimatedTrueSheet.js +13 -7
  42. package/lib/module/reanimated/ReanimatedTrueSheet.js.map +1 -1
  43. package/lib/module/reanimated/ReanimatedTrueSheetProvider.js +4 -2
  44. package/lib/module/reanimated/ReanimatedTrueSheetProvider.js.map +1 -1
  45. package/lib/typescript/src/TrueSheet.d.ts +4 -0
  46. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  47. package/lib/typescript/src/TrueSheet.types.d.ts +58 -6
  48. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  49. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +14 -5
  50. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
  51. package/lib/typescript/src/index.d.ts +0 -1
  52. package/lib/typescript/src/index.d.ts.map +1 -1
  53. package/lib/typescript/src/reanimated/ReanimatedTrueSheet.d.ts.map +1 -1
  54. package/lib/typescript/src/reanimated/ReanimatedTrueSheetProvider.d.ts +8 -2
  55. package/lib/typescript/src/reanimated/ReanimatedTrueSheetProvider.d.ts.map +1 -1
  56. package/package.json +1 -1
  57. package/src/TrueSheet.tsx +80 -10
  58. package/src/TrueSheet.types.ts +65 -6
  59. package/src/__mocks__/index.js +0 -5
  60. package/src/fabric/TrueSheetViewNativeComponent.ts +15 -5
  61. package/src/index.ts +0 -1
  62. package/src/reanimated/ReanimatedTrueSheet.tsx +12 -7
  63. package/src/reanimated/ReanimatedTrueSheetProvider.tsx +11 -3
  64. package/android/src/main/java/com/lodev09/truesheet/events/DetentChangeEvent.kt +0 -26
  65. package/android/src/main/java/com/lodev09/truesheet/events/DidDismissEvent.kt +0 -20
  66. package/android/src/main/java/com/lodev09/truesheet/events/DidPresentEvent.kt +0 -26
  67. package/android/src/main/java/com/lodev09/truesheet/events/DragBeginEvent.kt +0 -26
  68. package/android/src/main/java/com/lodev09/truesheet/events/DragChangeEvent.kt +0 -26
  69. package/android/src/main/java/com/lodev09/truesheet/events/DragEndEvent.kt +0 -26
  70. package/android/src/main/java/com/lodev09/truesheet/events/MountEvent.kt +0 -20
  71. package/android/src/main/java/com/lodev09/truesheet/events/PositionChangeEvent.kt +0 -32
  72. package/android/src/main/java/com/lodev09/truesheet/events/WillDismissEvent.kt +0 -20
  73. package/android/src/main/java/com/lodev09/truesheet/events/WillPresentEvent.kt +0 -26
  74. package/ios/events/OnDetentChangeEvent.h +0 -28
  75. package/ios/events/OnDetentChangeEvent.mm +0 -30
  76. package/ios/events/OnDidDismissEvent.h +0 -26
  77. package/ios/events/OnDidDismissEvent.mm +0 -25
  78. package/ios/events/OnDidPresentEvent.h +0 -28
  79. package/ios/events/OnDidPresentEvent.mm +0 -30
  80. package/ios/events/OnDragBeginEvent.h +0 -28
  81. package/ios/events/OnDragBeginEvent.mm +0 -30
  82. package/ios/events/OnDragChangeEvent.h +0 -28
  83. package/ios/events/OnDragChangeEvent.mm +0 -30
  84. package/ios/events/OnDragEndEvent.h +0 -28
  85. package/ios/events/OnDragEndEvent.mm +0 -30
  86. package/ios/events/OnMountEvent.h +0 -26
  87. package/ios/events/OnMountEvent.mm +0 -25
  88. package/ios/events/OnPositionChangeEvent.mm +0 -32
  89. package/ios/events/OnWillDismissEvent.h +0 -26
  90. package/ios/events/OnWillDismissEvent.mm +0 -25
  91. package/ios/events/OnWillPresentEvent.h +0 -28
  92. package/ios/events/OnWillPresentEvent.mm +0 -30
  93. package/ios/utils/ConversionUtil.h +0 -24
  94. package/lib/module/TrueSheetGrabber.js +0 -51
  95. package/lib/module/TrueSheetGrabber.js.map +0 -1
  96. package/lib/typescript/src/TrueSheetGrabber.d.ts +0 -39
  97. package/lib/typescript/src/TrueSheetGrabber.d.ts.map +0 -1
  98. package/src/TrueSheetGrabber.tsx +0 -82
package/src/TrueSheet.tsx CHANGED
@@ -20,6 +20,10 @@ import type {
20
20
  DidDismissEvent,
21
21
  WillDismissEvent,
22
22
  MountEvent,
23
+ WillFocusEvent,
24
+ DidFocusEvent,
25
+ WillBlurEvent,
26
+ DidBlurEvent,
23
27
  } from './TrueSheet.types';
24
28
  import TrueSheetViewNativeComponent from './fabric/TrueSheetViewNativeComponent';
25
29
  import TrueSheetContainerViewNativeComponent from './fabric/TrueSheetContainerViewNativeComponent';
@@ -29,7 +33,7 @@ import TrueSheetFooterViewNativeComponent from './fabric/TrueSheetFooterViewNati
29
33
 
30
34
  import TrueSheetModule from './specs/NativeTrueSheetModule';
31
35
 
32
- import { Platform, processColor, StyleSheet, findNodeHandle } from 'react-native';
36
+ import { Platform, processColor, StyleSheet, findNodeHandle, View } from 'react-native';
33
37
 
34
38
  const LINKING_ERROR =
35
39
  `The package '@lodev09/react-native-true-sheet' doesn't seem to be linked. Make sure: \n\n` +
@@ -38,6 +42,9 @@ const LINKING_ERROR =
38
42
  '- You are not using Expo Go\n' +
39
43
  '- You are using the new architecture (Fabric)\n';
40
44
 
45
+ // Material Design 3 minimum touch target
46
+ const ANDROID_HITBOX_HEIGHT = 48;
47
+
41
48
  if (!TrueSheetModule) {
42
49
  throw new Error(LINKING_ERROR);
43
50
  }
@@ -88,6 +95,10 @@ export class TrueSheet extends PureComponent<TrueSheetProps, TrueSheetState> {
88
95
  this.onDragChange = this.onDragChange.bind(this);
89
96
  this.onDragEnd = this.onDragEnd.bind(this);
90
97
  this.onPositionChange = this.onPositionChange.bind(this);
98
+ this.onWillFocus = this.onWillFocus.bind(this);
99
+ this.onDidFocus = this.onDidFocus.bind(this);
100
+ this.onWillBlur = this.onWillBlur.bind(this);
101
+ this.onDidBlur = this.onDidBlur.bind(this);
91
102
  }
92
103
 
93
104
  private validateDetents(): void {
@@ -250,6 +261,22 @@ export class TrueSheet extends PureComponent<TrueSheetProps, TrueSheetState> {
250
261
  this.props.onPositionChange?.(event);
251
262
  }
252
263
 
264
+ private onWillFocus(event: WillFocusEvent): void {
265
+ this.props.onWillFocus?.(event);
266
+ }
267
+
268
+ private onDidFocus(event: DidFocusEvent): void {
269
+ this.props.onDidFocus?.(event);
270
+ }
271
+
272
+ private onWillBlur(event: WillBlurEvent): void {
273
+ this.props.onWillBlur?.(event);
274
+ }
275
+
276
+ private onDidBlur(event: DidBlurEvent): void {
277
+ this.props.onDidBlur?.(event);
278
+ }
279
+
253
280
  /**
254
281
  * Present the Sheet by `index` (Promise-based)
255
282
  * @param index - Detent index (default: 0)
@@ -313,6 +340,7 @@ export class TrueSheet extends PureComponent<TrueSheetProps, TrueSheetState> {
313
340
  detents = [0.5, 1],
314
341
  backgroundColor,
315
342
  dismissible = true,
343
+ draggable = true,
316
344
  grabber = true,
317
345
  dimmed = true,
318
346
  initialDetentIndex = -1,
@@ -323,13 +351,12 @@ export class TrueSheet extends PureComponent<TrueSheetProps, TrueSheetState> {
323
351
  cornerRadius,
324
352
  maxHeight,
325
353
  edgeToEdgeFullScreen,
326
- fitScrollView = false,
354
+ scrollable = false,
327
355
  pageSizing = true,
328
356
  children,
329
357
  style,
330
358
  header,
331
359
  footer,
332
- testID,
333
360
  ...rest
334
361
  } = this.props;
335
362
 
@@ -344,6 +371,18 @@ export class TrueSheet extends PureComponent<TrueSheetProps, TrueSheetState> {
344
371
  return Math.min(1, detent);
345
372
  });
346
373
 
374
+ const containerStyle =
375
+ this.props.scrollable &&
376
+ Platform.select({
377
+ android: styles.scrollableAndroidContainer,
378
+ });
379
+
380
+ const contentStyle =
381
+ this.props.scrollable &&
382
+ Platform.select({
383
+ android: styles.scrollableAndroidContent,
384
+ });
385
+
347
386
  return (
348
387
  <TrueSheetViewNativeComponent
349
388
  {...rest}
@@ -360,9 +399,10 @@ export class TrueSheet extends PureComponent<TrueSheetProps, TrueSheetState> {
360
399
  initialDetentIndex={initialDetentIndex}
361
400
  initialDetentAnimated={initialDetentAnimated}
362
401
  dismissible={dismissible}
402
+ draggable={draggable}
363
403
  maxHeight={maxHeight}
364
404
  edgeToEdgeFullScreen={edgeToEdgeFullScreen}
365
- fitScrollView={fitScrollView}
405
+ scrollable={scrollable}
366
406
  pageSizing={pageSizing}
367
407
  onMount={this.onMount}
368
408
  onWillPresent={this.onWillPresent}
@@ -374,22 +414,29 @@ export class TrueSheet extends PureComponent<TrueSheetProps, TrueSheetState> {
374
414
  onDragChange={this.onDragChange}
375
415
  onDragEnd={this.onDragEnd}
376
416
  onPositionChange={this.onPositionChange}
417
+ onWillFocus={this.onWillFocus}
418
+ onDidFocus={this.onDidFocus}
419
+ onWillBlur={this.onWillBlur}
420
+ onDidBlur={this.onDidBlur}
377
421
  >
378
422
  {this.state.shouldRenderNativeView && (
379
- <TrueSheetContainerViewNativeComponent testID={testID} collapsable={false}>
423
+ <TrueSheetContainerViewNativeComponent style={containerStyle}>
380
424
  {header && (
381
- <TrueSheetHeaderViewNativeComponent collapsable={false}>
425
+ <TrueSheetHeaderViewNativeComponent>
382
426
  {isValidElement(header) ? header : createElement(header)}
383
427
  </TrueSheetHeaderViewNativeComponent>
384
428
  )}
385
- <TrueSheetContentViewNativeComponent style={style} collapsable={false}>
429
+ <TrueSheetContentViewNativeComponent style={[style, contentStyle]}>
386
430
  {children}
387
431
  </TrueSheetContentViewNativeComponent>
388
432
  {footer && (
389
- <TrueSheetFooterViewNativeComponent style={styles.footer} collapsable={false}>
433
+ <TrueSheetFooterViewNativeComponent style={styles.footer}>
390
434
  {isValidElement(footer) ? footer : createElement(footer)}
391
435
  </TrueSheetFooterViewNativeComponent>
392
436
  )}
437
+ {Platform.OS === 'android' && grabber && draggable && (
438
+ <View collapsable={false} style={styles.grabberHitbox} />
439
+ )}
393
440
  </TrueSheetContainerViewNativeComponent>
394
441
  )}
395
442
  </TrueSheetViewNativeComponent>
@@ -399,13 +446,36 @@ export class TrueSheet extends PureComponent<TrueSheetProps, TrueSheetState> {
399
446
 
400
447
  const styles = StyleSheet.create({
401
448
  sheetView: {
402
- position: 'absolute',
403
- width: '100%',
404
449
  zIndex: -9999,
450
+ position: 'absolute',
451
+ top: 0,
452
+ left: 0,
453
+ right: 0,
454
+
455
+ // Android needs a fixed bottom to avoid jumping content
456
+ bottom: Platform.select({ android: 0 }),
457
+ },
458
+ scrollableAndroidContainer: {
459
+ position: 'absolute',
460
+ top: 0,
461
+ left: 0,
462
+ right: 0,
463
+ bottom: 0,
464
+ },
465
+ scrollableAndroidContent: {
466
+ flexGrow: 1,
467
+ flexBasis: 0,
405
468
  },
406
469
  footer: {
407
470
  position: 'absolute',
408
471
  left: 0,
409
472
  right: 0,
410
473
  },
474
+ grabberHitbox: {
475
+ position: 'absolute',
476
+ top: 0,
477
+ left: 0,
478
+ right: 0,
479
+ height: ANDROID_HITBOX_HEIGHT,
480
+ },
411
481
  });
@@ -10,14 +10,18 @@ export interface DetentInfoEventPayload {
10
10
  * The Y position of the sheet relative to the screen.
11
11
  */
12
12
  position: number;
13
+ /**
14
+ * The detent value (0-1) for the current index.
15
+ */
16
+ detent: number;
13
17
  }
14
18
 
15
19
  export interface PositionChangeEventPayload extends DetentInfoEventPayload {
16
20
  /**
17
- * Workaround for cases where we can't get real-time position from native.
18
- * When true, manually animate the position in JS.
21
+ * Indicates whether the position value is real-time (e.g., during drag or animation tracking).
22
+ * When false, the position should be animated in JS.
19
23
  */
20
- transitioning: boolean;
24
+ realtime: boolean;
21
25
  }
22
26
 
23
27
  export type MountEvent = NativeSyntheticEvent<null>;
@@ -30,6 +34,10 @@ export type DragBeginEvent = NativeSyntheticEvent<DetentInfoEventPayload>;
30
34
  export type DragChangeEvent = NativeSyntheticEvent<DetentInfoEventPayload>;
31
35
  export type DragEndEvent = NativeSyntheticEvent<DetentInfoEventPayload>;
32
36
  export type PositionChangeEvent = NativeSyntheticEvent<PositionChangeEventPayload>;
37
+ export type DidFocusEvent = NativeSyntheticEvent<null>;
38
+ export type DidBlurEvent = NativeSyntheticEvent<null>;
39
+ export type WillFocusEvent = NativeSyntheticEvent<null>;
40
+ export type WillBlurEvent = NativeSyntheticEvent<null>;
33
41
 
34
42
  /**
35
43
  * Blur style mapped to native values in IOS.
@@ -106,6 +114,9 @@ export interface TrueSheetProps extends ViewProps {
106
114
  * detents={['auto', 0.6, 1]}
107
115
  * ```
108
116
  *
117
+ * @note It's recommended to sort detents from smallest to largest.
118
+ * When using `auto`, place it at the first index if you plan to adjust content dynamically.
119
+ *
109
120
  * @default [0.5, 1]
110
121
  */
111
122
  detents?: SheetDetent[];
@@ -149,6 +160,14 @@ export interface TrueSheetProps extends ViewProps {
149
160
  */
150
161
  dismissible?: boolean;
151
162
 
163
+ /**
164
+ * Enables or disables dragging the sheet to resize it.
165
+ * When disabled, the sheet becomes static and can only be resized programmatically.
166
+ *
167
+ * @default true
168
+ */
169
+ draggable?: boolean;
170
+
152
171
  /**
153
172
  * Main sheet background color.
154
173
  * Uses system default when not provided.
@@ -192,6 +211,25 @@ export interface TrueSheetProps extends ViewProps {
192
211
  */
193
212
  blurTint?: BlurTint;
194
213
 
214
+ /**
215
+ * The intensity of the blur effect (0-100).
216
+ * Only applies when `blurTint` is set.
217
+ * Uses system default if not provided.
218
+ *
219
+ * @platform ios
220
+ */
221
+ blurIntensity?: number;
222
+
223
+ /**
224
+ * Enables or disables user interaction on the blur view.
225
+ * Disabling this can help with visual artifacts (flash) on iOS 18+
226
+ * when touching the sheet content with blur enabled.
227
+ * Uses system default (true) if not provided.
228
+ *
229
+ * @platform ios
230
+ */
231
+ blurInteraction?: boolean;
232
+
195
233
  /**
196
234
  * Overrides `large` or `100%` height.
197
235
  * Also sets the maximum height for 'auto' detents.
@@ -219,14 +257,15 @@ export interface TrueSheetProps extends ViewProps {
219
257
  footer?: ComponentType<unknown> | ReactElement;
220
258
 
221
259
  /**
222
- * Automatically pins ScrollView or FlatList to fit within the sheet's available space.
260
+ * On iOS, automatically pins ScrollView or FlatList to fit within the sheet's available space.
223
261
  * When enabled, the ScrollView's top edge will be pinned below any top sibling views,
224
262
  * and its left, right, and bottom edges will be pinned to the container.
225
263
  *
226
- * @platform ios
264
+ * On Android, it adds additional style to the content for scrollable to work.
265
+ *
227
266
  * @default false
228
267
  */
229
- fitScrollView?: boolean;
268
+ scrollable?: boolean;
230
269
 
231
270
  /**
232
271
  * Determines how the software keyboard will impact the layout of the sheet.
@@ -294,4 +333,24 @@ export interface TrueSheetProps extends ViewProps {
294
333
  * Comes with the detent info.
295
334
  */
296
335
  onPositionChange?: (event: PositionChangeEvent) => void;
336
+
337
+ /**
338
+ * Called when the sheet is about to regain focus because a sheet presented on top of it is being dismissed.
339
+ */
340
+ onWillFocus?: (event: WillFocusEvent) => void;
341
+
342
+ /**
343
+ * Called when the sheet regains focus after a sheet presented on top of it is dismissed.
344
+ */
345
+ onDidFocus?: (event: DidFocusEvent) => void;
346
+
347
+ /**
348
+ * Called when the sheet is about to lose focus because another sheet is being presented on top of it.
349
+ */
350
+ onWillBlur?: (event: WillBlurEvent) => void;
351
+
352
+ /**
353
+ * Called when the sheet loses focus because another sheet is presented on top of it.
354
+ */
355
+ onDidBlur?: (event: DidBlurEvent) => void;
297
356
  }
@@ -47,11 +47,6 @@ export class ReanimatedTrueSheet extends TrueSheet {
47
47
  }
48
48
  }
49
49
 
50
- // Mock TrueSheetGrabber
51
- export const TrueSheetGrabber = ({ style, ...props }) => (
52
- <View style={style} {...props} testID="true-sheet-grabber" />
53
- );
54
-
55
50
  // Mock ReanimatedTrueSheetProvider
56
51
  export const ReanimatedTrueSheetProvider = ({ children }) => <>{children}</>;
57
52
 
@@ -4,24 +4,25 @@ import type {
4
4
  Double,
5
5
  Int32,
6
6
  WithDefault,
7
- Float,
8
7
  } from 'react-native/Libraries/Types/CodegenTypes';
9
8
  import { codegenNativeComponent } from 'react-native';
10
9
 
11
10
  export interface DetentInfoEventPayload {
12
11
  index: Int32;
13
12
  position: Double;
13
+ detent: Double;
14
14
  }
15
15
 
16
16
  export interface PositionChangeEventPayload {
17
- index: Int32;
17
+ index: Double;
18
18
  position: Double;
19
- transitioning: boolean;
19
+ detent: Double;
20
+ realtime: boolean;
20
21
  }
21
22
 
22
23
  export interface NativeProps extends ViewProps {
23
24
  // Array properties
24
- detents?: ReadonlyArray<Float>;
25
+ detents?: ReadonlyArray<Double>;
25
26
 
26
27
  // Number properties - use 0 as default to avoid nil insertion
27
28
  maxHeight?: WithDefault<Double, 0>;
@@ -34,13 +35,18 @@ export interface NativeProps extends ViewProps {
34
35
  blurTint?: WithDefault<string, ''>;
35
36
  keyboardMode?: WithDefault<'resize' | 'pan', 'resize'>;
36
37
 
38
+ // Blur properties - use -1 as default to indicate "not set" (use system default)
39
+ blurIntensity?: WithDefault<Double, -1>;
40
+ blurInteraction?: WithDefault<boolean, true>;
41
+
37
42
  // Boolean properties - match defaults from TrueSheet.types.ts
38
43
  grabber?: WithDefault<boolean, true>;
39
44
  dismissible?: WithDefault<boolean, true>;
45
+ draggable?: WithDefault<boolean, true>;
40
46
  dimmed?: WithDefault<boolean, true>;
41
47
  initialDetentAnimated?: WithDefault<boolean, true>;
42
48
  edgeToEdgeFullScreen?: WithDefault<boolean, false>;
43
- fitScrollView?: WithDefault<boolean, false>;
49
+ scrollable?: WithDefault<boolean, false>;
44
50
  pageSizing?: WithDefault<boolean, true>;
45
51
 
46
52
  // Event handlers
@@ -54,6 +60,10 @@ export interface NativeProps extends ViewProps {
54
60
  onDragChange?: DirectEventHandler<DetentInfoEventPayload>;
55
61
  onDragEnd?: DirectEventHandler<DetentInfoEventPayload>;
56
62
  onPositionChange?: DirectEventHandler<PositionChangeEventPayload>;
63
+ onWillFocus?: DirectEventHandler<null>;
64
+ onDidFocus?: DirectEventHandler<null>;
65
+ onWillBlur?: DirectEventHandler<null>;
66
+ onDidBlur?: DirectEventHandler<null>;
57
67
  }
58
68
 
59
69
  export default codegenNativeComponent<NativeProps>('TrueSheetView', {
package/src/index.ts CHANGED
@@ -1,4 +1,3 @@
1
1
  export * from './TrueSheet';
2
2
  export * from './TrueSheet.types';
3
- export * from './TrueSheetGrabber';
4
3
  export * from './reanimated';
@@ -70,24 +70,29 @@ const AnimatedTrueSheet = Animated.createAnimatedComponent(TrueSheet);
70
70
  export const ReanimatedTrueSheet = forwardRef<TrueSheet, ReanimatedTrueSheetProps>((props, ref) => {
71
71
  const { onPositionChange, ...rest } = props;
72
72
 
73
- const { animatedPosition, animatedIndex } = useReanimatedTrueSheet();
73
+ const { animatedPosition, animatedIndex, animatedDetent } = useReanimatedTrueSheet();
74
74
 
75
75
  const positionChangeHandler = useReanimatedPositionChangeHandler((payload) => {
76
76
  'worklet';
77
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) {
78
+ if (payload.realtime) {
79
+ // Update directly when we have real-time values (during drag or animation tracking)
80
+ animatedPosition.value = payload.position;
81
+ animatedIndex.value = payload.index;
82
+ animatedDetent.value = payload.detent;
83
+ } else {
84
+ // Animate position, index, and detent when not real-time
81
85
  if (Platform.OS === 'android') {
82
86
  animatedPosition.value = withTiming(payload.position, TIMING_CONFIG);
87
+ animatedIndex.value = withTiming(payload.index, TIMING_CONFIG);
88
+ animatedDetent.value = withTiming(payload.detent, TIMING_CONFIG);
83
89
  } else {
84
90
  animatedPosition.value = withSpring(payload.position, SPRING_CONFIG);
91
+ animatedIndex.value = withSpring(payload.index, SPRING_CONFIG);
92
+ animatedDetent.value = withSpring(payload.detent, SPRING_CONFIG);
85
93
  }
86
- } else {
87
- animatedPosition.value = payload.position;
88
94
  }
89
95
 
90
- animatedIndex.value = payload.index;
91
96
  onPositionChange?.({ nativeEvent: payload } as PositionChangeEvent);
92
97
  });
93
98
 
@@ -4,13 +4,19 @@ import { useSharedValue, type SharedValue } from 'react-native-reanimated';
4
4
 
5
5
  export interface ReanimatedTrueSheetContextValue {
6
6
  /**
7
- * Shared value representing the current sheet position (Y offset from bottom)
7
+ * Shared value representing the current sheet position (Y offset from top of screen)
8
8
  */
9
9
  animatedPosition: SharedValue<number>;
10
10
  /**
11
- * Shared value representing the current detent index
11
+ * Shared value representing the current detent index as a continuous float.
12
+ * Interpolates smoothly between -1 (off-screen) and the target detent index.
12
13
  */
13
14
  animatedIndex: SharedValue<number>;
15
+ /**
16
+ * Shared value representing the current detent value (0-1 fraction of screen height).
17
+ * Interpolates smoothly between detent values as the sheet is dragged.
18
+ */
19
+ animatedDetent: SharedValue<number>;
14
20
  }
15
21
 
16
22
  const ReanimatedTrueSheetContext = createContext<ReanimatedTrueSheetContextValue | null>(null);
@@ -40,13 +46,15 @@ export const ReanimatedTrueSheetProvider = ({ children }: ReanimatedTrueSheetPro
40
46
  const { height } = useWindowDimensions();
41
47
  const animatedPosition = useSharedValue(height);
42
48
  const animatedIndex = useSharedValue(-1);
49
+ const animatedDetent = useSharedValue(0);
43
50
 
44
51
  const value = useMemo(
45
52
  () => ({
46
53
  animatedPosition,
47
54
  animatedIndex,
55
+ animatedDetent,
48
56
  }),
49
- [animatedPosition, animatedIndex]
57
+ [animatedPosition, animatedIndex, animatedDetent]
50
58
  );
51
59
 
52
60
  return (
@@ -1,26 +0,0 @@
1
- package com.lodev09.truesheet.events
2
-
3
- import com.facebook.react.bridge.Arguments
4
- import com.facebook.react.bridge.WritableMap
5
- import com.facebook.react.uimanager.events.Event
6
-
7
- /**
8
- * Fired when the active detent changes
9
- * Payload: { index: number, position: number }
10
- */
11
- class DetentChangeEvent(surfaceId: Int, viewId: Int, private val index: Int, private val position: Float) :
12
- Event<DetentChangeEvent>(surfaceId, viewId) {
13
-
14
- override fun getEventName(): String = EVENT_NAME
15
-
16
- override fun getEventData(): WritableMap =
17
- Arguments.createMap().apply {
18
- putInt("index", index)
19
- putDouble("position", position.toDouble())
20
- }
21
-
22
- companion object {
23
- const val EVENT_NAME = "topDetentChange"
24
- const val REGISTRATION_NAME = "onDetentChange"
25
- }
26
- }
@@ -1,20 +0,0 @@
1
- package com.lodev09.truesheet.events
2
-
3
- import com.facebook.react.bridge.Arguments
4
- import com.facebook.react.bridge.WritableMap
5
- import com.facebook.react.uimanager.events.Event
6
-
7
- /**
8
- * Fired after the sheet dismissal is complete
9
- */
10
- class DidDismissEvent(surfaceId: Int, viewId: Int) : Event<DidDismissEvent>(surfaceId, viewId) {
11
-
12
- override fun getEventName(): String = EVENT_NAME
13
-
14
- override fun getEventData(): WritableMap = Arguments.createMap()
15
-
16
- companion object {
17
- const val EVENT_NAME = "topDidDismiss"
18
- const val REGISTRATION_NAME = "onDidDismiss"
19
- }
20
- }
@@ -1,26 +0,0 @@
1
- package com.lodev09.truesheet.events
2
-
3
- import com.facebook.react.bridge.Arguments
4
- import com.facebook.react.bridge.WritableMap
5
- import com.facebook.react.uimanager.events.Event
6
-
7
- /**
8
- * Fired after the sheet presentation is complete
9
- * Payload: { index: number, position: number }
10
- */
11
- class DidPresentEvent(surfaceId: Int, viewId: Int, private val index: Int, private val position: Float) :
12
- Event<DidPresentEvent>(surfaceId, viewId) {
13
-
14
- override fun getEventName(): String = EVENT_NAME
15
-
16
- override fun getEventData(): WritableMap =
17
- Arguments.createMap().apply {
18
- putInt("index", index)
19
- putDouble("position", position.toDouble())
20
- }
21
-
22
- companion object {
23
- const val EVENT_NAME = "topDidPresent"
24
- const val REGISTRATION_NAME = "onDidPresent"
25
- }
26
- }
@@ -1,26 +0,0 @@
1
- package com.lodev09.truesheet.events
2
-
3
- import com.facebook.react.bridge.Arguments
4
- import com.facebook.react.bridge.WritableMap
5
- import com.facebook.react.uimanager.events.Event
6
-
7
- /**
8
- * Fired when user starts dragging the sheet
9
- * Payload: { index: number, position: number }
10
- */
11
- class DragBeginEvent(surfaceId: Int, viewId: Int, private val index: Int, private val position: Float) :
12
- Event<DragBeginEvent>(surfaceId, viewId) {
13
-
14
- override fun getEventName(): String = EVENT_NAME
15
-
16
- override fun getEventData(): WritableMap =
17
- Arguments.createMap().apply {
18
- putInt("index", index)
19
- putDouble("position", position.toDouble())
20
- }
21
-
22
- companion object {
23
- const val EVENT_NAME = "topDragBegin"
24
- const val REGISTRATION_NAME = "onDragBegin"
25
- }
26
- }
@@ -1,26 +0,0 @@
1
- package com.lodev09.truesheet.events
2
-
3
- import com.facebook.react.bridge.Arguments
4
- import com.facebook.react.bridge.WritableMap
5
- import com.facebook.react.uimanager.events.Event
6
-
7
- /**
8
- * Fired continuously while user is dragging the sheet
9
- * Payload: { index: number, position: number }
10
- */
11
- class DragChangeEvent(surfaceId: Int, viewId: Int, private val index: Int, private val position: Float) :
12
- Event<DragChangeEvent>(surfaceId, viewId) {
13
-
14
- override fun getEventName(): String = EVENT_NAME
15
-
16
- override fun getEventData(): WritableMap =
17
- Arguments.createMap().apply {
18
- putInt("index", index)
19
- putDouble("position", position.toDouble())
20
- }
21
-
22
- companion object {
23
- const val EVENT_NAME = "topDragChange"
24
- const val REGISTRATION_NAME = "onDragChange"
25
- }
26
- }
@@ -1,26 +0,0 @@
1
- package com.lodev09.truesheet.events
2
-
3
- import com.facebook.react.bridge.Arguments
4
- import com.facebook.react.bridge.WritableMap
5
- import com.facebook.react.uimanager.events.Event
6
-
7
- /**
8
- * Fired when user stops dragging the sheet
9
- * Payload: { index: number, position: number }
10
- */
11
- class DragEndEvent(surfaceId: Int, viewId: Int, private val index: Int, private val position: Float) :
12
- Event<DragEndEvent>(surfaceId, viewId) {
13
-
14
- override fun getEventName(): String = EVENT_NAME
15
-
16
- override fun getEventData(): WritableMap =
17
- Arguments.createMap().apply {
18
- putInt("index", index)
19
- putDouble("position", position.toDouble())
20
- }
21
-
22
- companion object {
23
- const val EVENT_NAME = "topDragEnd"
24
- const val REGISTRATION_NAME = "onDragEnd"
25
- }
26
- }
@@ -1,20 +0,0 @@
1
- package com.lodev09.truesheet.events
2
-
3
- import com.facebook.react.bridge.Arguments
4
- import com.facebook.react.bridge.WritableMap
5
- import com.facebook.react.uimanager.events.Event
6
-
7
- /**
8
- * Fired when the sheet component is mounted and ready
9
- */
10
- class MountEvent(surfaceId: Int, viewId: Int) : Event<MountEvent>(surfaceId, viewId) {
11
-
12
- override fun getEventName(): String = EVENT_NAME
13
-
14
- override fun getEventData(): WritableMap = Arguments.createMap()
15
-
16
- companion object {
17
- const val EVENT_NAME = "topMount"
18
- const val REGISTRATION_NAME = "onMount"
19
- }
20
- }
@@ -1,32 +0,0 @@
1
- package com.lodev09.truesheet.events
2
-
3
- import com.facebook.react.bridge.Arguments
4
- import com.facebook.react.bridge.WritableMap
5
- import com.facebook.react.uimanager.events.Event
6
-
7
- /**
8
- * Fired continuously for position updates during drag and animation
9
- * Payload: { index: number, position: number, transitioning: boolean }
10
- */
11
- class PositionChangeEvent(
12
- surfaceId: Int,
13
- viewId: Int,
14
- private val index: Int,
15
- private val position: Float,
16
- private val transitioning: Boolean = false
17
- ) : Event<PositionChangeEvent>(surfaceId, viewId) {
18
-
19
- override fun getEventName(): String = EVENT_NAME
20
-
21
- override fun getEventData(): WritableMap =
22
- Arguments.createMap().apply {
23
- putInt("index", index)
24
- putDouble("position", position.toDouble())
25
- putBoolean("transitioning", transitioning)
26
- }
27
-
28
- companion object {
29
- const val EVENT_NAME = "topPositionChange"
30
- const val REGISTRATION_NAME = "onPositionChange"
31
- }
32
- }