@lodev09/react-native-true-sheet 3.1.0-beta.5 → 3.1.0-beta.6
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.
|
@@ -157,15 +157,15 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
157
157
|
override fun viewControllerWillPresent(index: Int, position: Float, detent: Float) {
|
|
158
158
|
val surfaceId = UIManagerHelper.getSurfaceId(this)
|
|
159
159
|
eventDispatcher?.dispatchEvent(WillPresentEvent(surfaceId, id, index, position, detent))
|
|
160
|
+
|
|
161
|
+
// Enable touch event dispatching to React Native
|
|
162
|
+
viewController.eventDispatcher = eventDispatcher
|
|
163
|
+
containerView?.footerView?.eventDispatcher = eventDispatcher
|
|
160
164
|
}
|
|
161
165
|
|
|
162
166
|
override fun viewControllerDidPresent(index: Int, position: Float, detent: Float) {
|
|
163
167
|
val surfaceId = UIManagerHelper.getSurfaceId(this)
|
|
164
168
|
eventDispatcher?.dispatchEvent(DidPresentEvent(surfaceId, id, index, position, detent))
|
|
165
|
-
|
|
166
|
-
// Enable touch event dispatching to React Native
|
|
167
|
-
viewController.eventDispatcher = eventDispatcher
|
|
168
|
-
containerView?.footerView?.eventDispatcher = eventDispatcher
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
override fun viewControllerWillDismiss() {
|
|
@@ -66,6 +66,10 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
66
66
|
private const val GRABBER_TAG = "TrueSheetGrabber"
|
|
67
67
|
private const val DEFAULT_MAX_WIDTH = 640 // dp
|
|
68
68
|
private const val DEFAULT_CORNER_RADIUS = 16 // dp
|
|
69
|
+
|
|
70
|
+
// Animation durations from res/anim/true_sheet_slide_in.xml and true_sheet_slide_out.xml
|
|
71
|
+
private const val PRESENT_ANIMATION_DURATION = 200L
|
|
72
|
+
private const val DISMISS_ANIMATION_DURATION = 150L
|
|
69
73
|
}
|
|
70
74
|
|
|
71
75
|
// ====================================================================
|
|
@@ -115,6 +119,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
115
119
|
private val resolvedDetentPositions = mutableListOf<Int>()
|
|
116
120
|
|
|
117
121
|
private var isDragging = false
|
|
122
|
+
private var isDismissing = false
|
|
118
123
|
private var isReconfiguring = false
|
|
119
124
|
private var windowAnimation: Int = 0
|
|
120
125
|
private var lastEmittedPositionPx: Int = -1
|
|
@@ -272,6 +277,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
272
277
|
|
|
273
278
|
dialog = null
|
|
274
279
|
isDragging = false
|
|
280
|
+
isDismissing = false
|
|
275
281
|
isPresented = false
|
|
276
282
|
isDialogVisible = false
|
|
277
283
|
lastEmittedPositionPx = -1
|
|
@@ -286,43 +292,51 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
286
292
|
setupGrabber()
|
|
287
293
|
|
|
288
294
|
sheetContainer?.post {
|
|
289
|
-
val detentInfo = getDetentInfoForIndex(currentDetentIndex)
|
|
290
|
-
val detent = getDetentValueForIndex(detentInfo.index)
|
|
291
295
|
val positionPx = bottomSheetView?.let { ScreenUtils.getScreenY(it) } ?: screenHeight
|
|
292
296
|
|
|
293
|
-
delegate?.viewControllerDidPresent(detentInfo.index, detentInfo.position, detent)
|
|
294
|
-
|
|
295
297
|
// Store resolved position for initial detent
|
|
296
|
-
storeResolvedPosition(
|
|
298
|
+
storeResolvedPosition(currentDetentIndex)
|
|
297
299
|
emitChangePositionDelegate(positionPx, realtime = false)
|
|
298
300
|
|
|
301
|
+
positionFooter()
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Emit didPresent/didFocus after present animation completes
|
|
305
|
+
sheetContainer?.postDelayed({
|
|
306
|
+
val detentInfo = getDetentInfoForIndex(currentDetentIndex)
|
|
307
|
+
val detent = getDetentValueForIndex(detentInfo.index)
|
|
308
|
+
|
|
309
|
+
delegate?.viewControllerDidPresent(detentInfo.index, detentInfo.position, detent)
|
|
310
|
+
|
|
299
311
|
// Notify parent sheet that it has lost focus (after this sheet appeared)
|
|
300
312
|
parentSheetView?.viewControllerDidBlur()
|
|
301
313
|
|
|
314
|
+
// Emit didFocus with didPresent
|
|
315
|
+
delegate?.viewControllerDidFocus()
|
|
316
|
+
|
|
302
317
|
presentPromise?.invoke()
|
|
303
318
|
presentPromise = null
|
|
304
|
-
|
|
305
|
-
positionFooter()
|
|
306
|
-
}
|
|
319
|
+
}, PRESENT_ANIMATION_DURATION)
|
|
307
320
|
}
|
|
308
321
|
|
|
309
322
|
dialog.setOnCancelListener {
|
|
310
|
-
|
|
323
|
+
// Skip if already handled by STATE_HIDDEN (programmatic dismiss)
|
|
324
|
+
if (isDismissing) return@setOnCancelListener
|
|
311
325
|
|
|
312
|
-
//
|
|
313
|
-
|
|
314
|
-
|
|
326
|
+
// User-initiated dismiss (back button, tap outside)
|
|
327
|
+
isDismissing = true
|
|
328
|
+
emitWillDismissEvents()
|
|
315
329
|
|
|
316
|
-
|
|
317
|
-
|
|
330
|
+
// Emit off-screen position since onSlide isn't triggered for user-initiated dismiss
|
|
331
|
+
emitChangePositionDelegate(screenHeight, realtime = false)
|
|
318
332
|
|
|
319
|
-
//
|
|
320
|
-
|
|
321
|
-
|
|
333
|
+
// Emit didBlur/didDismiss after dismiss animation completes
|
|
334
|
+
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
|
|
335
|
+
emitDidDismissEvents()
|
|
336
|
+
}, DISMISS_ANIMATION_DURATION)
|
|
337
|
+
}
|
|
322
338
|
|
|
323
|
-
|
|
324
|
-
dismissPromise = null
|
|
325
|
-
delegate?.viewControllerDidDismiss(hadParent)
|
|
339
|
+
dialog.setOnDismissListener {
|
|
326
340
|
cleanupDialog()
|
|
327
341
|
}
|
|
328
342
|
}
|
|
@@ -348,7 +362,10 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
348
362
|
|
|
349
363
|
override fun onStateChanged(sheetView: View, newState: Int) {
|
|
350
364
|
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
|
|
351
|
-
|
|
365
|
+
// Mark as dismissing so setOnCancelListener skips emitting events
|
|
366
|
+
isDismissing = true
|
|
367
|
+
emitDidDismissEvents()
|
|
368
|
+
dialog.dismiss()
|
|
352
369
|
return
|
|
353
370
|
}
|
|
354
371
|
|
|
@@ -433,6 +450,34 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
433
450
|
rnScreensObserver = null
|
|
434
451
|
}
|
|
435
452
|
|
|
453
|
+
// ====================================================================
|
|
454
|
+
// MARK: - Dismiss Event Helpers
|
|
455
|
+
// ====================================================================
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Emits willBlur and willDismiss events, and notifies parent sheet of upcoming focus change.
|
|
459
|
+
*/
|
|
460
|
+
private fun emitWillDismissEvents() {
|
|
461
|
+
delegate?.viewControllerWillBlur()
|
|
462
|
+
delegate?.viewControllerWillDismiss()
|
|
463
|
+
parentSheetView?.viewControllerWillFocus()
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Emits didBlur and didDismiss events, notifies parent sheet, and invokes dismiss promise.
|
|
468
|
+
*/
|
|
469
|
+
private fun emitDidDismissEvents() {
|
|
470
|
+
val hadParent = parentSheetView != null
|
|
471
|
+
parentSheetView?.viewControllerDidFocus()
|
|
472
|
+
parentSheetView = null
|
|
473
|
+
|
|
474
|
+
delegate?.viewControllerDidBlur()
|
|
475
|
+
delegate?.viewControllerDidDismiss(hadParent)
|
|
476
|
+
|
|
477
|
+
dismissPromise?.invoke()
|
|
478
|
+
dismissPromise = null
|
|
479
|
+
}
|
|
480
|
+
|
|
436
481
|
// ====================================================================
|
|
437
482
|
// MARK: - Dialog Visibility (for stacking)
|
|
438
483
|
// ====================================================================
|
|
@@ -518,6 +563,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
518
563
|
|
|
519
564
|
delegate?.viewControllerWillPresent(detentInfo.index, detentInfo.position, detent)
|
|
520
565
|
|
|
566
|
+
// Emit willFocus with willPresent
|
|
567
|
+
delegate?.viewControllerWillFocus()
|
|
568
|
+
|
|
521
569
|
if (!animated) {
|
|
522
570
|
dialog.window?.setWindowAnimations(0)
|
|
523
571
|
}
|
|
@@ -527,15 +575,20 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
527
575
|
}
|
|
528
576
|
|
|
529
577
|
fun dismiss(animated: Boolean = true) {
|
|
578
|
+
emitWillDismissEvents()
|
|
579
|
+
|
|
530
580
|
this.post {
|
|
531
581
|
// Emit off-screen position (detent = 0 since sheet is fully hidden)
|
|
532
582
|
emitChangePositionDelegate(screenHeight, realtime = false)
|
|
533
583
|
}
|
|
534
584
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
post { dismiss() }
|
|
585
|
+
if (!animated) {
|
|
586
|
+
dialog?.window?.setWindowAnimations(0)
|
|
538
587
|
}
|
|
588
|
+
|
|
589
|
+
// Temporarily enable hideable to allow STATE_HIDDEN transition
|
|
590
|
+
behavior?.isHideable = true
|
|
591
|
+
behavior?.state = BottomSheetBehavior.STATE_HIDDEN
|
|
539
592
|
}
|
|
540
593
|
|
|
541
594
|
// ====================================================================
|
|
@@ -159,15 +159,20 @@
|
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
|
|
163
|
-
|
|
162
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
163
|
+
if ([self.delegate respondsToSelector:@selector(viewControllerWillPresentAtIndex:position:detent:)]) {
|
|
164
164
|
NSInteger index = self.currentDetentIndex;
|
|
165
165
|
CGFloat position = self.currentPosition;
|
|
166
166
|
CGFloat detent = [self detentValueForIndex:index];
|
|
167
167
|
|
|
168
168
|
[self.delegate viewControllerWillPresentAtIndex:index position:position detent:detent];
|
|
169
|
-
}
|
|
170
|
-
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Emit willFocus with willPresent
|
|
172
|
+
if ([self.delegate respondsToSelector:@selector(viewControllerWillFocus)]) {
|
|
173
|
+
[self.delegate viewControllerWillFocus];
|
|
174
|
+
}
|
|
175
|
+
});
|
|
171
176
|
}
|
|
172
177
|
|
|
173
178
|
[self setupTransitionTracker];
|
|
@@ -184,13 +189,19 @@
|
|
|
184
189
|
}
|
|
185
190
|
}
|
|
186
191
|
|
|
187
|
-
|
|
188
|
-
|
|
192
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
193
|
+
if ([self.delegate respondsToSelector:@selector(viewControllerDidPresentAtIndex:position:detent:)]) {
|
|
189
194
|
NSInteger index = [self currentDetentIndex];
|
|
190
195
|
CGFloat detent = [self detentValueForIndex:index];
|
|
191
196
|
[self.delegate viewControllerDidPresentAtIndex:index position:self.currentPosition detent:detent];
|
|
192
|
-
}
|
|
193
|
-
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Emit didFocus with didPresent
|
|
200
|
+
if ([self.delegate respondsToSelector:@selector(viewControllerDidFocus)]) {
|
|
201
|
+
[self.delegate viewControllerDidFocus];
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
194
205
|
[self setupGestureRecognizer];
|
|
195
206
|
_isPresented = YES;
|
|
196
207
|
}
|
|
@@ -204,9 +215,16 @@
|
|
|
204
215
|
[super viewWillDisappear:animated];
|
|
205
216
|
|
|
206
217
|
if (self.isDismissing) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
218
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
219
|
+
// Emit willBlur with willDismiss
|
|
220
|
+
if ([self.delegate respondsToSelector:@selector(viewControllerWillBlur)]) {
|
|
221
|
+
[self.delegate viewControllerWillBlur];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if ([self.delegate respondsToSelector:@selector(viewControllerWillDismiss)]) {
|
|
225
|
+
[self.delegate viewControllerWillDismiss];
|
|
226
|
+
}
|
|
227
|
+
});
|
|
210
228
|
|
|
211
229
|
// Notify the parent sheet (if any) that it is about to regain focus
|
|
212
230
|
if (_parentSheetController) {
|
|
@@ -235,6 +253,11 @@
|
|
|
235
253
|
_parentSheetController = nil;
|
|
236
254
|
}
|
|
237
255
|
|
|
256
|
+
// Emit didBlur with didDismiss
|
|
257
|
+
if ([self.delegate respondsToSelector:@selector(viewControllerDidBlur)]) {
|
|
258
|
+
[self.delegate viewControllerDidBlur];
|
|
259
|
+
}
|
|
260
|
+
|
|
238
261
|
if ([self.delegate respondsToSelector:@selector(viewControllerDidDismiss)]) {
|
|
239
262
|
[self.delegate viewControllerDidDismiss];
|
|
240
263
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lodev09/react-native-true-sheet",
|
|
3
|
-
"version": "3.1.0-beta.
|
|
3
|
+
"version": "3.1.0-beta.6",
|
|
4
4
|
"description": "The true native bottom sheet experience for your React Native Apps.",
|
|
5
5
|
"source": "./src/index.ts",
|
|
6
6
|
"main": "./lib/module/index.js",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"@eslint/js": "^9.35.0",
|
|
81
81
|
"@evilmartians/lefthook": "^2.0.4",
|
|
82
82
|
"@react-native/babel-preset": "^0.82.1",
|
|
83
|
-
"@react-native/eslint-config": "^0.
|
|
83
|
+
"@react-native/eslint-config": "^0.82.1",
|
|
84
84
|
"@release-it/conventional-changelog": "^10.0.1",
|
|
85
85
|
"@testing-library/react-native": "^13.3.3",
|
|
86
86
|
"@types/babel__core": "^7",
|
package/src/__mocks__/index.js
CHANGED
|
@@ -6,13 +6,13 @@ export class TrueSheet extends React.Component {
|
|
|
6
6
|
static instances = {};
|
|
7
7
|
|
|
8
8
|
// Static methods
|
|
9
|
-
static dismiss = jest.fn((name) => Promise.resolve());
|
|
10
|
-
static present = jest.fn((name, index = 0) => Promise.resolve());
|
|
9
|
+
static dismiss = jest.fn((name, animated = true) => Promise.resolve());
|
|
10
|
+
static present = jest.fn((name, index = 0, animated = true) => Promise.resolve());
|
|
11
11
|
static resize = jest.fn((name, index) => Promise.resolve());
|
|
12
12
|
|
|
13
13
|
// Instance methods
|
|
14
|
-
dismiss = jest.fn(() => Promise.resolve());
|
|
15
|
-
present = jest.fn((index = 0) => Promise.resolve());
|
|
14
|
+
dismiss = jest.fn((animated = true) => Promise.resolve());
|
|
15
|
+
present = jest.fn((index = 0, animated = true) => Promise.resolve());
|
|
16
16
|
resize = jest.fn((index) => Promise.resolve());
|
|
17
17
|
|
|
18
18
|
componentDidMount() {
|
|
@@ -30,9 +30,10 @@ export class TrueSheet extends React.Component {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
render() {
|
|
33
|
-
const { children, footer, style, ...rest } = this.props;
|
|
33
|
+
const { children, header, footer, style, ...rest } = this.props;
|
|
34
34
|
return (
|
|
35
35
|
<View style={style} {...rest}>
|
|
36
|
+
{header}
|
|
36
37
|
{children}
|
|
37
38
|
{footer}
|
|
38
39
|
</View>
|