@sdcx/bottom-sheet 0.1.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.
@@ -0,0 +1,599 @@
1
+ package com.reactnative.bottomsheet;
2
+
3
+
4
+ import static com.reactnative.bottomsheet.BottomSheetState.COLLAPSED;
5
+ import static com.reactnative.bottomsheet.BottomSheetState.DRAGGING;
6
+ import static com.reactnative.bottomsheet.BottomSheetState.EXPANDED;
7
+ import static com.reactnative.bottomsheet.BottomSheetState.HIDDEN;
8
+ import static com.reactnative.bottomsheet.BottomSheetState.SETTLING;
9
+ import static java.lang.Math.max;
10
+
11
+ import android.annotation.SuppressLint;
12
+ import android.graphics.Rect;
13
+ import android.view.MotionEvent;
14
+ import android.view.VelocityTracker;
15
+ import android.view.View;
16
+ import android.view.ViewGroup;
17
+ import android.view.ViewTreeObserver;
18
+
19
+ import androidx.annotation.NonNull;
20
+ import androidx.annotation.Nullable;
21
+ import androidx.annotation.VisibleForTesting;
22
+ import androidx.core.math.MathUtils;
23
+ import androidx.core.util.Pools;
24
+ import androidx.core.view.NestedScrollingParent;
25
+ import androidx.core.view.NestedScrollingParentHelper;
26
+ import androidx.core.view.ViewCompat;
27
+ import androidx.customview.widget.ViewDragHelper;
28
+
29
+ import com.facebook.react.bridge.ReactContext;
30
+ import com.facebook.react.uimanager.PointerEvents;
31
+ import com.facebook.react.uimanager.ReactPointerEventsView;
32
+ import com.facebook.react.uimanager.ThemedReactContext;
33
+ import com.facebook.react.uimanager.UIManagerHelper;
34
+ import com.facebook.react.uimanager.events.Event;
35
+ import com.facebook.react.uimanager.events.EventDispatcher;
36
+ import com.facebook.react.uimanager.events.NativeGestureUtil;
37
+ import com.facebook.react.views.view.ReactViewGroup;
38
+
39
+ import java.lang.ref.WeakReference;
40
+
41
+ @SuppressLint("ViewConstructor")
42
+ public class BottomSheet extends ReactViewGroup implements NestedScrollingParent, ReactPointerEventsView {
43
+
44
+ private static final String TAG = "ReactBottomSheet";
45
+
46
+ private final ReactContext reactContext;
47
+
48
+ public BottomSheet(ThemedReactContext reactContext) {
49
+ super(reactContext);
50
+ this.reactContext = reactContext;
51
+ this.nestedScrollingParentHelper = new NestedScrollingParentHelper(this);
52
+ }
53
+
54
+ private BottomSheetState state = COLLAPSED;
55
+
56
+ private SettleRunnable settleRunnable = null;
57
+
58
+ private int peekHeight;
59
+
60
+ private int expandedOffset;
61
+
62
+ private int collapsedOffset;
63
+
64
+ private View contentView;
65
+
66
+ @Nullable
67
+ private WeakReference<View> nestedScrollingChildRef;
68
+
69
+ private boolean touchingScrollingChild;
70
+
71
+ @Nullable
72
+ private ViewDragHelper viewDragHelper;
73
+
74
+ private final NestedScrollingParentHelper nestedScrollingParentHelper;
75
+
76
+ private boolean ignoreEvents;
77
+
78
+ private int lastNestedScrollDy;
79
+
80
+ private VelocityTracker velocityTracker;
81
+
82
+ private int activePointerId;
83
+
84
+ private int initialY;
85
+
86
+ private int contentHeight = -1;
87
+
88
+ @Override
89
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
90
+ if (viewDragHelper == null) {
91
+ viewDragHelper = ViewDragHelper.create(this, dragCallback);
92
+ }
93
+
94
+ layoutChild();
95
+ }
96
+
97
+ private void layoutChild() {
98
+ int count = getChildCount();
99
+ if (count == 1) {
100
+ View child = getChildAt(0);
101
+ if (contentView == null) {
102
+ contentView = child;
103
+ }
104
+
105
+ contentHeight = contentView.getHeight();
106
+ calculateOffset();
107
+
108
+ getViewTreeObserver().removeOnPreDrawListener(preDrawListener);
109
+ int top = contentView.getTop();
110
+ if (state == COLLAPSED) {
111
+ child.offsetTopAndBottom(collapsedOffset - top);
112
+ } else if (state == EXPANDED) {
113
+ child.offsetTopAndBottom(expandedOffset -top);
114
+ } else if (state == HIDDEN) {
115
+ child.offsetTopAndBottom(getHeight() - top);
116
+ }
117
+ getViewTreeObserver().addOnPreDrawListener(preDrawListener);
118
+
119
+ dispatchOnSlide(child.getTop());
120
+ }
121
+ }
122
+
123
+ @Override
124
+ protected void onDetachedFromWindow() {
125
+ super.onDetachedFromWindow();
126
+ getViewTreeObserver().removeOnPreDrawListener(preDrawListener);
127
+ }
128
+
129
+ ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() {
130
+ @Override
131
+ public boolean onPreDraw() {
132
+ if (contentHeight != -1 && contentHeight != contentView.getHeight()) {
133
+ layoutChild();
134
+ }
135
+ return true;
136
+ }
137
+ };
138
+
139
+ private void calculateOffset() {
140
+ expandedOffset = Math.max(0, getHeight() - contentView.getHeight());
141
+ collapsedOffset = Math.max(getHeight() - peekHeight, expandedOffset);
142
+ }
143
+
144
+ public void setPeekHeight(int peekHeight) {
145
+ this.peekHeight = max(peekHeight, 0);
146
+ if (contentView != null) {
147
+ calculateOffset();
148
+ if (state == COLLAPSED) {
149
+ settleToState(contentView, state);
150
+ }
151
+ }
152
+ }
153
+
154
+ public void setState(BottomSheetState state) {
155
+ if (state == this.state) {
156
+ return;
157
+ }
158
+
159
+ if (contentView == null) {
160
+ // The view is not laid out yet; modify mState and let onLayoutChild handle it later
161
+ if (state == COLLAPSED || state == EXPANDED || state == HIDDEN) {
162
+ this.state = state;
163
+ }
164
+ return;
165
+ }
166
+ settleToState(contentView, state);
167
+ }
168
+
169
+ @Nullable
170
+ @VisibleForTesting
171
+ View findScrollingChild(View view) {
172
+ if (ViewCompat.isNestedScrollingEnabled(view)) {
173
+ if (!view.canScrollHorizontally(1) && !view.canScrollHorizontally(-1)) {
174
+ return view;
175
+ }
176
+ }
177
+
178
+ if (view instanceof ViewGroup) {
179
+ ViewGroup group = (ViewGroup) view;
180
+ for (int i = 0, count = group.getChildCount(); i < count; i++) {
181
+ View child = group.getChildAt(i);
182
+ if (child.getVisibility() == VISIBLE) {
183
+ View scrollingChild = findScrollingChild(child);
184
+ if (scrollingChild != null) {
185
+ return scrollingChild;
186
+ }
187
+ }
188
+ }
189
+ }
190
+ return null;
191
+ }
192
+
193
+ public PointerEvents getPointerEvents() {
194
+ return PointerEvents.BOX_NONE;
195
+ }
196
+
197
+ @Override
198
+ public boolean onInterceptTouchEvent(MotionEvent event) {
199
+ if (shouldInterceptTouchEvent(event)) {
200
+ NativeGestureUtil.notifyNativeGestureStarted(this, event);
201
+ return true;
202
+ }
203
+ return false;
204
+ }
205
+
206
+ private boolean shouldInterceptTouchEvent(MotionEvent event) {
207
+ int action = event.getActionMasked();
208
+ // Record the velocity
209
+ if (action == MotionEvent.ACTION_DOWN) {
210
+ reset();
211
+ }
212
+ if (velocityTracker == null) {
213
+ velocityTracker = VelocityTracker.obtain();
214
+ }
215
+ velocityTracker.addMovement(event);
216
+ switch (action) {
217
+ case MotionEvent.ACTION_UP:
218
+ case MotionEvent.ACTION_CANCEL:
219
+ touchingScrollingChild = false;
220
+ activePointerId = MotionEvent.INVALID_POINTER_ID;
221
+ // Reset the ignore flag
222
+ if (ignoreEvents) {
223
+ ignoreEvents = false;
224
+ return false;
225
+ }
226
+ break;
227
+ case MotionEvent.ACTION_DOWN:
228
+ nestedScrollingChildRef = new WeakReference<>(findScrollingChild(contentView));
229
+ int initialX = (int) event.getX();
230
+ initialY = (int) event.getY();
231
+ // Only intercept nested scrolling events here if the view not being moved by the
232
+ // ViewDragHelper.
233
+ if (state != SETTLING) {
234
+ View scroll = nestedScrollingChildRef != null ? nestedScrollingChildRef.get() : null;
235
+ if (scroll != null && isPointInChildBounds(scroll, initialX, initialY)) {
236
+ activePointerId = event.getPointerId(event.getActionIndex());
237
+ touchingScrollingChild = true;
238
+ }
239
+ }
240
+
241
+ ignoreEvents = activePointerId == MotionEvent.INVALID_POINTER_ID
242
+ && !isPointInChildBounds(contentView, initialX, initialY);
243
+ break;
244
+ default: // fall out
245
+ }
246
+
247
+ if (!ignoreEvents && viewDragHelper != null && viewDragHelper.shouldInterceptTouchEvent(event)) {
248
+ return true;
249
+ }
250
+
251
+ // We have to handle cases that the ViewDragHelper does not capture the bottom sheet because
252
+ // it is not the top most view of its parent. This is not necessary when the touch event is
253
+ // happening over the scrolling content as nested scrolling logic handles that case.
254
+ View scroll = nestedScrollingChildRef != null ? nestedScrollingChildRef.get() : null;
255
+ return action == MotionEvent.ACTION_MOVE
256
+ && scroll != null
257
+ && !ignoreEvents
258
+ && state != DRAGGING
259
+ && !isPointInChildBounds(scroll, (int) event.getX(), (int) event.getY())
260
+ && viewDragHelper != null
261
+ && Math.abs(initialY - event.getY()) > viewDragHelper.getTouchSlop();
262
+ }
263
+
264
+ @Override
265
+ public boolean onTouchEvent(MotionEvent event) {
266
+ int action = event.getActionMasked();
267
+ if (state == DRAGGING && action == MotionEvent.ACTION_DOWN) {
268
+ return true;
269
+ }
270
+ if (viewDragHelper != null) {
271
+ viewDragHelper.processTouchEvent(event);
272
+ }
273
+ // Record the velocity
274
+ if (action == MotionEvent.ACTION_DOWN) {
275
+ reset();
276
+ }
277
+ if (velocityTracker == null) {
278
+ velocityTracker = VelocityTracker.obtain();
279
+ }
280
+ velocityTracker.addMovement(event);
281
+ if (viewDragHelper != null && action == MotionEvent.ACTION_MOVE && !ignoreEvents) {
282
+ if (Math.abs(initialY - event.getY()) > viewDragHelper.getTouchSlop()) {
283
+ viewDragHelper.captureChildView(contentView, event.getPointerId(event.getActionIndex()));
284
+ }
285
+ }
286
+
287
+ return !ignoreEvents;
288
+ }
289
+
290
+ @Override
291
+ public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int nestedScrollAxes) {
292
+ return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
293
+ }
294
+
295
+ @Override
296
+ public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes) {
297
+ lastNestedScrollDy = 0;
298
+ nestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes);
299
+ }
300
+
301
+ @Override
302
+ public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed) {
303
+ View scrollingChild = nestedScrollingChildRef != null ? nestedScrollingChildRef.get() : null;
304
+ if (target != scrollingChild) {
305
+ return;
306
+ }
307
+
308
+ View child = contentView;
309
+ int currentTop = child.getTop();
310
+ int newTop = currentTop - dy;
311
+ if (dy > 0) { // Upward
312
+ if (newTop < expandedOffset) {
313
+ consumed[1] = currentTop - expandedOffset;
314
+ ViewCompat.offsetTopAndBottom(child, -consumed[1]);
315
+ setStateInternal(EXPANDED);
316
+ } else {
317
+ consumed[1] = dy;
318
+ ViewCompat.offsetTopAndBottom(child, -dy);
319
+ setStateInternal(DRAGGING);
320
+ }
321
+ } else if (dy < 0) { // Downward
322
+ if (!target.canScrollVertically(-1)) {
323
+ if (newTop <= collapsedOffset) {
324
+ consumed[1] = dy;
325
+ ViewCompat.offsetTopAndBottom(child, -dy);
326
+ setStateInternal(DRAGGING);
327
+ } else {
328
+ consumed[1] = currentTop - collapsedOffset;
329
+ ViewCompat.offsetTopAndBottom(child, -consumed[1]);
330
+ setStateInternal(COLLAPSED);
331
+ }
332
+ }
333
+ }
334
+ if (currentTop != child.getTop()) {
335
+ dispatchOnSlide(child.getTop());
336
+ }
337
+ lastNestedScrollDy = dy;
338
+ }
339
+
340
+ @Override
341
+ public int getNestedScrollAxes() {
342
+ return nestedScrollingParentHelper.getNestedScrollAxes();
343
+ }
344
+
345
+ @Override
346
+ public void onStopNestedScroll(@NonNull View target) {
347
+ nestedScrollingParentHelper.onStopNestedScroll(target);
348
+ View child = contentView;
349
+
350
+ if (child.getTop() == expandedOffset) {
351
+ setStateInternal(EXPANDED);
352
+ return;
353
+ }
354
+
355
+ if (nestedScrollingChildRef == null || target != nestedScrollingChildRef.get()) {
356
+ return;
357
+ }
358
+
359
+ int top;
360
+ BottomSheetState targetState;
361
+
362
+ if (lastNestedScrollDy > 0) {
363
+ top = expandedOffset;
364
+ targetState = EXPANDED;
365
+ } else if (lastNestedScrollDy == 0) {
366
+ int currentTop = child.getTop();
367
+ if (Math.abs(currentTop - collapsedOffset) < Math.abs(currentTop - expandedOffset)) {
368
+ top = collapsedOffset;
369
+ targetState = COLLAPSED;
370
+ } else {
371
+ top = expandedOffset;
372
+ targetState = EXPANDED;
373
+ }
374
+ } else {
375
+ top = collapsedOffset;
376
+ targetState = COLLAPSED;
377
+ }
378
+
379
+ startSettlingAnimation(child, targetState, top, false);
380
+ }
381
+
382
+ @Override
383
+ public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
384
+ // Overridden to prevent the default consumption of the entire scroll distance.
385
+ }
386
+
387
+ @Override
388
+ public boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY) {
389
+ if (nestedScrollingChildRef != null) {
390
+ return target == nestedScrollingChildRef.get() && (state != EXPANDED);
391
+ } else {
392
+ return false;
393
+ }
394
+ }
395
+
396
+ @Override
397
+ public boolean onNestedFling(@NonNull View target, float velocityX, float velocityY, boolean consumed) {
398
+ return false;
399
+ }
400
+
401
+ private void reset() {
402
+ activePointerId = ViewDragHelper.INVALID_POINTER;
403
+ if (velocityTracker != null) {
404
+ velocityTracker.recycle();
405
+ velocityTracker = null;
406
+ }
407
+ }
408
+
409
+ private static final Pools.Pool<Rect> sRectPool = new Pools.SynchronizedPool<>(12);
410
+
411
+ private static Rect acquireTempRect() {
412
+ Rect rect = sRectPool.acquire();
413
+ if (rect == null) {
414
+ rect = new Rect();
415
+ }
416
+ return rect;
417
+ }
418
+
419
+ private static void releaseTempRect(@NonNull Rect rect) {
420
+ rect.setEmpty();
421
+ sRectPool.release(rect);
422
+ }
423
+
424
+ public boolean isPointInChildBounds(@NonNull View child, int x, int y) {
425
+ final Rect r = acquireTempRect();
426
+ child.getDrawingRect(r);
427
+ offsetDescendantRectToMyCoords(child, r);
428
+ try {
429
+ return r.contains(x, y);
430
+ } finally {
431
+ releaseTempRect(r);
432
+ }
433
+ }
434
+
435
+ private final ViewDragHelper.Callback dragCallback = new ViewDragHelper.Callback() {
436
+ @Override
437
+ public boolean tryCaptureView(@NonNull View child, int pointerId) {
438
+ if (state == DRAGGING) {
439
+ return false;
440
+ }
441
+ if (touchingScrollingChild) {
442
+ return false;
443
+ }
444
+ if (state == EXPANDED && activePointerId == pointerId) {
445
+ View scroll = nestedScrollingChildRef != null ? nestedScrollingChildRef.get() : null;
446
+ if (scroll != null && scroll.canScrollVertically(-1)) {
447
+ // Let the content scroll up
448
+ return false;
449
+ }
450
+ }
451
+ return contentView == child;
452
+ }
453
+
454
+ @Override
455
+ public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
456
+ dispatchOnSlide(top);
457
+ }
458
+
459
+ @Override
460
+ public void onViewDragStateChanged(int state) {
461
+ if (state == ViewDragHelper.STATE_DRAGGING) {
462
+ setStateInternal(DRAGGING);
463
+ }
464
+ }
465
+
466
+ @Override
467
+ public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
468
+ int top;
469
+ BottomSheetState targetState;
470
+ if (yvel < 0) { // Moving up
471
+ top = expandedOffset;
472
+ targetState = EXPANDED;
473
+ } else if (yvel == 0.f || Math.abs(xvel) > Math.abs(yvel)) {
474
+ // If the Y velocity is 0 or the swipe was mostly horizontal indicated by the X velocity
475
+ // being greater than the Y velocity, settle to the nearest correct height.
476
+ int currentTop = releasedChild.getTop();
477
+ if (Math.abs(currentTop - collapsedOffset) < Math.abs(currentTop - expandedOffset)) {
478
+ top = collapsedOffset;
479
+ targetState = COLLAPSED;
480
+ } else {
481
+ top = expandedOffset;
482
+ targetState = EXPANDED;
483
+ }
484
+ } else { // Moving Down
485
+ top = collapsedOffset;
486
+ targetState = COLLAPSED;
487
+ }
488
+
489
+ startSettlingAnimation(releasedChild, targetState, top, true);
490
+ }
491
+
492
+ @Override
493
+ public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
494
+ return MathUtils.clamp(top, expandedOffset, collapsedOffset);
495
+ }
496
+
497
+ @Override
498
+ public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
499
+ return child.getLeft();
500
+ }
501
+
502
+ @Override
503
+ public int getViewVerticalDragRange(@NonNull View child) {
504
+ return collapsedOffset;
505
+ }
506
+ };
507
+
508
+ void dispatchOnSlide(int top) {
509
+ if (contentView != null) {
510
+ sentEvent(new OffsetChangedEvent(UIManagerHelper.getSurfaceId(reactContext), getId(), top, expandedOffset, collapsedOffset));
511
+ }
512
+ }
513
+
514
+ void settleToState(@NonNull View child, BottomSheetState state) {
515
+ int top;
516
+ if (state == COLLAPSED) {
517
+ top = collapsedOffset;
518
+ } else if (state == EXPANDED) {
519
+ top = expandedOffset;
520
+ } else if (state == HIDDEN) {
521
+ top = getHeight();
522
+ } else {
523
+ throw new IllegalArgumentException("Illegal state argument: " + state);
524
+ }
525
+ startSettlingAnimation(child, state, top, false);
526
+ }
527
+
528
+ void startSettlingAnimation(View child, BottomSheetState state, int top, boolean settleFromViewDragHelper) {
529
+ boolean startedSettling =
530
+ viewDragHelper != null
531
+ && (settleFromViewDragHelper
532
+ ? viewDragHelper.settleCapturedViewAt(child.getLeft(), top)
533
+ : viewDragHelper.smoothSlideViewTo(child, child.getLeft(), top));
534
+ if (startedSettling) {
535
+ setStateInternal(SETTLING);
536
+ // STATE_SETTLING won't animate the material shape, so do that here with the target state.
537
+ // updateDrawableForTargetState(state);
538
+ if (settleRunnable == null) {
539
+ // If the singleton SettleRunnable instance has not been instantiated, create it.
540
+ settleRunnable = new SettleRunnable(child, state);
541
+ }
542
+ // If the SettleRunnable has not been posted, post it with the correct state.
543
+ if (!settleRunnable.isPosted) {
544
+ settleRunnable.targetState = state;
545
+ ViewCompat.postOnAnimation(child, settleRunnable);
546
+ settleRunnable.isPosted = true;
547
+ } else {
548
+ // Otherwise, if it has been posted, just update the target state.
549
+ settleRunnable.targetState = state;
550
+ }
551
+ } else {
552
+ setStateInternal(state);
553
+ }
554
+ }
555
+
556
+ void setStateInternal(BottomSheetState state) {
557
+ if (this.state == state) {
558
+ return;
559
+ }
560
+ this.state = state;
561
+
562
+ if (state == COLLAPSED || state == EXPANDED || state == HIDDEN) {
563
+ sentEvent(new StateChangedEvent(UIManagerHelper.getSurfaceId(reactContext), getId(), state.name().toLowerCase()));
564
+ }
565
+ }
566
+
567
+ private class SettleRunnable implements Runnable {
568
+
569
+ private final View view;
570
+
571
+ private boolean isPosted;
572
+
573
+ BottomSheetState targetState;
574
+
575
+ SettleRunnable(View view, BottomSheetState targetState) {
576
+ this.view = view;
577
+ this.targetState = targetState;
578
+ }
579
+
580
+ @Override
581
+ public void run() {
582
+ if (viewDragHelper != null && viewDragHelper.continueSettling(true)) {
583
+ ViewCompat.postOnAnimation(view, this);
584
+ } else {
585
+ setStateInternal(targetState);
586
+ }
587
+ this.isPosted = false;
588
+ }
589
+ }
590
+
591
+ void sentEvent(Event<?> event) {
592
+ int viewId = getId();
593
+ EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, viewId);
594
+ if (eventDispatcher != null) {
595
+ eventDispatcher.dispatchEvent(event);
596
+ }
597
+ }
598
+
599
+ }
@@ -0,0 +1,45 @@
1
+ package com.reactnative.bottomsheet;
2
+
3
+ import androidx.annotation.NonNull;
4
+
5
+ import com.facebook.react.common.MapBuilder;
6
+ import com.facebook.react.uimanager.PixelUtil;
7
+ import com.facebook.react.uimanager.ThemedReactContext;
8
+ import com.facebook.react.uimanager.ViewGroupManager;
9
+ import com.facebook.react.uimanager.annotations.ReactProp;
10
+
11
+ import java.util.Map;
12
+
13
+ public class BottomSheetManager extends ViewGroupManager<BottomSheet> {
14
+
15
+ @NonNull
16
+ @Override
17
+ public String getName() {
18
+ return "BottomSheet";
19
+ }
20
+
21
+ @NonNull
22
+ @Override
23
+ protected BottomSheet createViewInstance(@NonNull ThemedReactContext reactContext) {
24
+ return new BottomSheet(reactContext);
25
+ }
26
+
27
+ @Override
28
+ public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
29
+ return MapBuilder.<String, Object>builder()
30
+ .put(StateChangedEvent.Name, MapBuilder.of("registrationName", StateChangedEvent.JSEventName))
31
+ .put(OffsetChangedEvent.Name, MapBuilder.of("registrationName", OffsetChangedEvent.JSEventName))
32
+ .build();
33
+ }
34
+
35
+ @ReactProp(name = "peekHeight", defaultInt = 200)
36
+ public void setPeekHeight(BottomSheet view, int dp) {
37
+ view.setPeekHeight((int) (PixelUtil.toPixelFromDIP(dp) + 0.5));
38
+ }
39
+
40
+ @ReactProp(name = "state")
41
+ public void setState(BottomSheet view, String state) {
42
+ view.setState(BottomSheetState.valueOf(state.toUpperCase()));
43
+ }
44
+
45
+ }
@@ -0,0 +1,25 @@
1
+ package com.reactnative.bottomsheet;
2
+
3
+ import androidx.annotation.NonNull;
4
+
5
+ import java.util.Collections;
6
+ import java.util.List;
7
+
8
+ import com.facebook.react.ReactPackage;
9
+ import com.facebook.react.bridge.NativeModule;
10
+ import com.facebook.react.bridge.ReactApplicationContext;
11
+ import com.facebook.react.uimanager.ViewManager;
12
+
13
+ public class BottomSheetPackage implements ReactPackage {
14
+ @NonNull
15
+ @Override
16
+ public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
17
+ return Collections.emptyList();
18
+ }
19
+
20
+ @NonNull
21
+ @Override
22
+ public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
23
+ return Collections.singletonList(new BottomSheetManager());
24
+ }
25
+ }
@@ -0,0 +1,5 @@
1
+ package com.reactnative.bottomsheet;
2
+
3
+ public enum BottomSheetState {
4
+ COLLAPSED, EXPANDED, HIDDEN, DRAGGING, SETTLING
5
+ }