@sdcx/bottom-sheet 0.9.0 → 0.10.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.
package/README.md CHANGED
@@ -43,7 +43,12 @@ const App = () => {
43
43
  }
44
44
  ```
45
45
 
46
- > :white_check_mark: 当和可滚动视图一起使用时,需要设置 `nestedScrollEnabled` 属性。
46
+ > :exclamation: :exclamation: :exclamation:
47
+ > Android 是基于 [NestedScrolling API](https://developer.android.com/reference/androidx/core/view/NestedScrollingChild) 实现的。
48
+ >
49
+ > <h3>请记得为你的列表开启 `nestedScrollEnabled` 属性。</h3>
50
+ >
51
+ > :exclamation: :exclamation: :exclamation:
47
52
 
48
53
  ## 基本概念和 API
49
54
 
@@ -67,6 +72,8 @@ const App = () => {
67
72
 
68
73
  - `fitToContents`,是指 BottomSheet 在展开时,是否适应内容的高度,默认是 `false`。当和可滚动列表,譬如 ScrollView 一起使用时,请保持默认值。
69
74
 
75
+ - `contentContainerStyle`,用来设置内层视图的样式。
76
+
70
77
  ### 回调
71
78
 
72
79
  - `onStateChanged`, 是指 BottomSheet 状态变化时的回调,它和 `state` 属性是一对,用来实现受控模式。
@@ -1,14 +1,19 @@
1
1
  #import <UIKit/UIKit.h>
2
- #import <React/RCTComponent.h>
2
+ #import <React/RCTView.h>
3
+ #import <React/RCTEventDispatcher.h>
4
+
5
+ #import "RNBottomSheetState.h"
3
6
 
4
7
  NS_ASSUME_NONNULL_BEGIN
5
8
 
6
- @interface RNBottomSheet : UIView
9
+ @interface RNBottomSheet : RCTView
7
10
 
8
11
  @property(nonatomic, copy) RCTDirectEventBlock onSlide;
9
12
  @property(nonatomic, copy) RCTDirectEventBlock onStateChanged;
10
13
  @property(nonatomic, assign) CGFloat peekHeight;
11
- @property(nonatomic, copy) NSString *state;
14
+ @property(nonatomic, assign) RNBottomSheetState state;
15
+
16
+ - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher;
12
17
 
13
18
  @end
14
19
 
@@ -1,4 +1,6 @@
1
1
  #import "RNBottomSheet.h"
2
+ #import "RNBottomSheetStateChangedEvent.h"
3
+ #import "RNBottomSheetOffsetChangedEvent.h"
2
4
 
3
5
  #import <React/UIView+React.h>
4
6
  #import <React/RCTRootContentView.h>
@@ -7,6 +9,8 @@
7
9
 
8
10
  @interface RNBottomSheet () <UIGestureRecognizerDelegate>
9
11
 
12
+ @property(nonatomic, strong) UIView *contentView;
13
+
10
14
  @property(nonatomic, strong) UIScrollView *target;
11
15
  @property(nonatomic, strong) UIPanGestureRecognizer *panGestureRecognizer;
12
16
 
@@ -14,26 +18,41 @@
14
18
  @property(nonatomic, assign) CGFloat maxY;
15
19
 
16
20
  @property(nonatomic, assign) BOOL nextReturn;
17
- @property(nonatomic, assign) CGFloat lastDragDistance;
18
21
 
19
22
  @property(nonatomic, strong) CADisplayLink *displayLink;
23
+ @property(nonatomic, strong) RCTEventDispatcher *eventDispatcher;
20
24
 
21
25
  @end
22
26
 
23
27
  @implementation RNBottomSheet {
24
- __weak RCTRootContentView *_cachedRootView;
28
+ __weak RCTRootContentView *_rootView;
25
29
  }
26
30
 
27
- - (instancetype)init {
31
+ - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher {
28
32
  if (self = [super init]) {
29
33
  _panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
30
34
  _panGestureRecognizer.delegate = self;
31
- _state = @"collapsed";
32
- [self addGestureRecognizer:_panGestureRecognizer];
35
+ _state = RNBottomSheetStateCollapsed;
36
+ _eventDispatcher = eventDispatcher;
33
37
  }
34
38
  return self;
35
39
  }
36
40
 
41
+ - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex {
42
+ [super insertReactSubview:subview atIndex:atIndex];
43
+ if (atIndex == 0) {
44
+ self.contentView = subview;
45
+ [subview addGestureRecognizer:_panGestureRecognizer];
46
+ }
47
+ }
48
+
49
+ - (void)removeReactSubview:(UIView *)subview {
50
+ [super removeReactSubview:subview];
51
+ if (self.contentView == subview) {
52
+ [subview removeGestureRecognizer:_panGestureRecognizer];
53
+ }
54
+ }
55
+
37
56
  - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
38
57
  if (gestureRecognizer == self.panGestureRecognizer && otherGestureRecognizer == self.target.panGestureRecognizer) {
39
58
  return YES;
@@ -100,20 +119,20 @@
100
119
  - (void)didMoveToWindow {
101
120
  [super didMoveToWindow];
102
121
  if (self.window) {
103
- [self _cacheRootView];
122
+ [self cacheRootView];
104
123
  }
105
124
  }
106
125
 
107
- - (void)_cacheRootView {
126
+ - (void)cacheRootView {
108
127
  UIView *rootView = self;
109
128
  while (rootView.superview && ![rootView isReactRootView]) {
110
129
  rootView = rootView.superview;
111
130
  }
112
- _cachedRootView = rootView;
131
+ _rootView = rootView;
113
132
  }
114
133
 
115
134
  - (void)cancelRootViewTouches {
116
- RCTRootContentView *rootView = (RCTRootContentView *)_cachedRootView;
135
+ RCTRootContentView *rootView = (RCTRootContentView *)_rootView;
117
136
  [rootView.touchHandler cancel];
118
137
  }
119
138
 
@@ -124,39 +143,42 @@
124
143
 
125
144
  - (void)reactSetFrame:(CGRect)frame {
126
145
  [super reactSetFrame:frame];
127
- if (!CGRectEqualToRect(self.frame, CGRectZero)) {
128
- [self calculateOffset];
129
- if ([self.state isEqualToString:@"collapsed"]) {
130
- self.frame = CGRectOffset(self.frame, 0, self.frame.size.height - self.peekHeight);
131
- [self dispatchOnSlide:self.frame.origin.y];
132
- } else if ([self.state isEqualToString:@"hidden"]) {
133
- self.frame = CGRectOffset(self.frame, 0, self.frame.size.height);
134
- [self dispatchOnSlide:self.frame.origin.y];
135
- }
136
- }
146
+
137
147
  }
138
148
 
139
149
  - (void)layoutSubviews {
140
150
  [super layoutSubviews];
141
- [self calculateOffset];
142
- [self dispatchOnSlide:self.frame.origin.y];
151
+ if (!self.contentView) {
152
+ return;
153
+ }
154
+
155
+ if (!CGRectEqualToRect(self.contentView.frame, CGRectZero)) {
156
+ [self calculateOffset];
157
+ if (self.state == RNBottomSheetStateCollapsed) {
158
+ self.contentView.frame = CGRectOffset(self.contentView.frame, 0, self.maxY - self.contentView.frame.origin.y);
159
+ } else if (self.state == RNBottomSheetStateExpanded) {
160
+ self.contentView.frame = CGRectOffset(self.contentView.frame, 0, self.minY - self.contentView.frame.origin.y);
161
+ } else if (self.state == RNBottomSheetStateHidden) {
162
+ self.contentView.frame = CGRectOffset(self.contentView.frame, 0, self.frame.size.height - self.contentView.frame.origin.y);
163
+ }
164
+ [self dispatchOnSlide:self.contentView.frame.origin.y];
165
+ }
143
166
  }
144
167
 
145
168
  - (void)calculateOffset {
146
- CGFloat parentHeight = self.superview.frame.size.height;
147
- self.minY = fmax(0, parentHeight - self.frame.size.height);
169
+ CGFloat parentHeight = self.frame.size.height;
170
+ self.minY = fmax(0, parentHeight - self.contentView.frame.size.height);
148
171
  self.maxY = fmax(self.minY, parentHeight - self.peekHeight);
149
172
  }
150
173
 
151
174
  - (void)handlePan:(UIPanGestureRecognizer *)pan {
175
+ CGFloat translationY = [pan translationInView:self.contentView].y;
176
+ [pan setTranslation:CGPointZero inView:self.contentView];
152
177
 
153
- CGFloat translationY = [pan translationInView:self].y;
154
- [pan setTranslation:CGPointZero inView:self];
155
-
156
- CGFloat top = self.frame.origin.y;
178
+ CGFloat top = self.contentView.frame.origin.y;
157
179
 
158
180
  if (pan.state == UIGestureRecognizerStateChanged) {
159
- [self setStateInternal:@"dragging"];
181
+ [self setStateInternal:RNBottomSheetStateDragging];
160
182
  }
161
183
 
162
184
  // 如果有嵌套滚动
@@ -164,15 +186,15 @@
164
186
  if(translationY > 0 && top < self.maxY && self.target.contentOffset.y <= 0) {
165
187
  //向下拖
166
188
  CGFloat y = fmin(top + translationY, self.maxY);
167
- self.frame = CGRectOffset(self.frame, 0, y - top);
168
- [self dispatchOnSlide:self.frame.origin.y];
189
+ self.contentView.frame = CGRectOffset(self.contentView.frame, 0, y - top);
190
+ [self dispatchOnSlide:self.contentView.frame.origin.y];
169
191
  }
170
192
 
171
193
  if (translationY < 0 && top > self.minY) {
172
194
  //向上拖
173
195
  CGFloat y = fmax(top + translationY, self.minY);
174
- self.frame = CGRectOffset(self.frame, 0, y - top);
175
- [self dispatchOnSlide:self.frame.origin.y];
196
+ self.contentView.frame = CGRectOffset(self.contentView.frame, 0, y - top);
197
+ [self dispatchOnSlide:self.contentView.frame.origin.y];
176
198
  }
177
199
  }
178
200
 
@@ -181,43 +203,43 @@
181
203
  if(translationY > 0 && top < self.maxY) {
182
204
  //向下拖
183
205
  CGFloat y = fmin(top + translationY, self.maxY);
184
- self.frame = CGRectOffset(self.frame, 0, y - top);
185
- [self dispatchOnSlide:self.frame.origin.y];
206
+ self.contentView.frame = CGRectOffset(self.contentView.frame, 0, y - top);
207
+ [self dispatchOnSlide:self.contentView.frame.origin.y];
186
208
  }
187
209
 
188
210
  if (translationY < 0 && top > self.minY) {
189
211
  //向上拖
190
212
  CGFloat y = fmax(top + translationY, self.minY);
191
- self.frame = CGRectOffset(self.frame, 0, y - top);
192
- [self dispatchOnSlide:self.frame.origin.y];
213
+ self.contentView.frame = CGRectOffset(self.contentView.frame, 0, y - top);
214
+ [self dispatchOnSlide:self.contentView.frame.origin.y];
193
215
  }
194
216
  }
195
217
 
196
218
  if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled) {
197
- if (self.lastDragDistance > 0) {
219
+ // RCTLogInfo(@"velocity:%f", [pan velocityInView:self.contentView].y);
220
+ CGFloat velocity = [pan velocityInView:self.contentView].y;
221
+ if (velocity > 400) {
198
222
  if (self.target && self.target.contentOffset.y <= 0) {
199
223
  //如果是类似轻扫的那种
200
- [self settleToState:@"collapsed"];
224
+ [self settleToState:RNBottomSheetStateCollapsed];
201
225
  }
202
226
 
203
227
  if (!self.target) {
204
228
  //如果是类似轻扫的那种
205
- [self settleToState:@"collapsed"];
229
+ [self settleToState:RNBottomSheetStateCollapsed];
206
230
  }
207
- } else if (self.lastDragDistance < 0) {
231
+ } else if (velocity < -400) {
208
232
  //如果是类似轻扫的那种
209
- [self settleToState:@"expanded"];
233
+ [self settleToState:RNBottomSheetStateExpanded];
210
234
  } else {
211
235
  //如果是普通拖拽
212
- if(fabs(self.frame.origin.y - self.minY) > fabs(self.frame.origin.y - self.maxY)) {
213
- [self settleToState:@"collapsed"];
236
+ if(fabs(self.contentView.frame.origin.y - self.minY) > fabs(self.contentView.frame.origin.y - self.maxY)) {
237
+ [self settleToState:RNBottomSheetStateCollapsed];
214
238
  } else {
215
- [self settleToState:@"expanded"];
239
+ [self settleToState:RNBottomSheetStateExpanded];
216
240
  }
217
241
  }
218
242
  }
219
-
220
- self.lastDragDistance = translationY;
221
243
  }
222
244
 
223
245
  - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(UIScrollView *)target change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
@@ -249,7 +271,7 @@
249
271
 
250
272
  if (dy < 0) {
251
273
  //向上
252
- if (self.frame.origin.y > self.minY) {
274
+ if (self.contentView.frame.origin.y > self.minY) {
253
275
  _nextReturn = true;
254
276
  target.contentOffset = CGPointMake(0, old);
255
277
  }
@@ -258,20 +280,20 @@
258
280
 
259
281
  - (void)setPeekHeight:(CGFloat)peekHeight {
260
282
  _peekHeight = peekHeight;
261
- if (!CGRectEqualToRect(self.frame, CGRectZero)) {
283
+ if (!CGRectEqualToRect(self.contentView.frame, CGRectZero)) {
262
284
  [self calculateOffset];
263
- if ([self.state isEqualToString:@"collapsed"]) {
264
- [self settleToState:@"collapsed"];
285
+ if (self.state == RNBottomSheetStateCollapsed) {
286
+ [self settleToState:RNBottomSheetStateCollapsed];
265
287
  }
266
288
  }
267
289
  }
268
290
 
269
- - (void)setState:(NSString *)state {
270
- if ([_state isEqualToString:state]) {
291
+ - (void)setState:(RNBottomSheetState)state {
292
+ if (_state == state) {
271
293
  return;
272
294
  }
273
295
 
274
- if (CGRectEqualToRect(self.frame, CGRectZero)) {
296
+ if (CGRectEqualToRect(self.contentView.frame, CGRectZero)) {
275
297
  [self setStateInternal:state];
276
298
  return;
277
299
  }
@@ -279,24 +301,24 @@
279
301
  [self settleToState:state];
280
302
  }
281
303
 
282
- - (void)settleToState:(NSString *)state {
283
- if ([state isEqualToString:@"collapsed"]) {
304
+ - (void)settleToState:(RNBottomSheetState)state {
305
+ if (state == RNBottomSheetStateCollapsed) {
284
306
  [self startSettlingToState:state top:self.maxY];
285
- } else if ([state isEqualToString:@"expanded"]) {
307
+ } else if (state == RNBottomSheetStateExpanded) {
286
308
  [self startSettlingToState:state top:self.minY];
287
- } else if ([state isEqualToString:@"hidden"]) {
288
- [self startSettlingToState:@"hidden" top:self.superview.frame.size.height];
309
+ } else if (state == RNBottomSheetStateHidden) {
310
+ [self startSettlingToState:state top:self.frame.size.height];
289
311
  }
290
312
  }
291
313
 
292
- - (void)startSettlingToState:(NSString *)state top:(CGFloat)top {
314
+ - (void)startSettlingToState:(RNBottomSheetState)state top:(CGFloat)top {
293
315
  self.target.pagingEnabled = YES;
294
- [self setStateInternal:@"settling"];
316
+ [self setStateInternal:RNBottomSheetStateSettling];
295
317
  [self startWatchBottomSheetTransition];
296
318
  [self.layer removeAllAnimations];
297
- CGFloat duration = fmin(fabs(self.frame.origin.y - top) / (self.maxY - self.minY) * 0.3, 0.3);
298
- [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveEaseOut|UIViewAnimationOptionBeginFromCurrentState animations:^{
299
- self.frame = CGRectOffset(self.frame, 0, top - self.frame.origin.y);
319
+ // CGFloat duration = fmin(fabs(self.contentView.frame.origin.y - top) / (self.maxY - self.minY) * 0.3, 0.3);
320
+ [UIView animateWithDuration:0.25 delay:0 usingSpringWithDamping:1 initialSpringVelocity:0.5 options:NULL animations:^{
321
+ self.contentView.frame = CGRectOffset(self.contentView.frame, 0, top - self.contentView.frame.origin.y);
300
322
  } completion:^(BOOL finished) {
301
323
  self.target.pagingEnabled = NO;
302
324
  [self stopWatchBottomSheetTransition];
@@ -304,18 +326,14 @@
304
326
  }];
305
327
  }
306
328
 
307
- - (void)setStateInternal:(NSString *)state {
308
- if ([_state isEqualToString:state]) {
329
+ - (void)setStateInternal:(RNBottomSheetState)state {
330
+ if (_state == state) {
309
331
  return;
310
332
  }
311
333
  _state = state;
312
334
 
313
- if (self.onStateChanged) {
314
- if ([state isEqualToString:@"collapsed"] || [state isEqualToString:@"expanded"] || [state isEqualToString:@"hidden"]) {
315
- self.onStateChanged(@{
316
- @"state": state,
317
- });
318
- }
335
+ if (state == RNBottomSheetStateCollapsed || state == RNBottomSheetStateExpanded || state == RNBottomSheetStateHidden) {
336
+ [self.eventDispatcher sendEvent:[[RNBottomSheetStateChangedEvent alloc] initWithViewTag:self.reactTag state:state]];
319
337
  }
320
338
  }
321
339
 
@@ -323,15 +341,8 @@
323
341
  if (top < 0 || self.maxY == 0) {
324
342
  return;
325
343
  }
326
- if (self.onSlide) {
327
- CGFloat progress = fmin((top - self.minY) * 1.0f / (self.maxY - self.minY), 1);
328
- self.onSlide(@{
329
- @"progress": @(progress),
330
- @"offset": @(top),
331
- @"collapsedOffset": @(self.maxY),
332
- @"expandedOffset": @(self.minY)
333
- });
334
- }
344
+ CGFloat progress = fmin((top - self.minY) * 1.0f / (self.maxY - self.minY), 1);
345
+ [self.eventDispatcher sendEvent:[[RNBottomSheetOffsetChangedEvent alloc] initWithViewTag:self.reactTag progress:progress offset:top minY:self.minY maxY:self.maxY]];
335
346
  }
336
347
 
337
348
  - (void)startWatchBottomSheetTransition {
@@ -342,9 +353,9 @@
342
353
  }
343
354
 
344
355
  - (void)stopWatchBottomSheetTransition {
345
- if ([self.state isEqualToString:@"collapsed"]) {
356
+ if (self.state == RNBottomSheetStateCollapsed) {
346
357
  [self dispatchOnSlide:self.maxY];
347
- } else if ([self.state isEqualToString:@"expanded"]) {
358
+ } else if (self.state == RNBottomSheetStateExpanded) {
348
359
  [self dispatchOnSlide:self.minY];
349
360
  }
350
361
  if(_displayLink){
@@ -354,7 +365,7 @@
354
365
  }
355
366
 
356
367
  - (void)watchBottomSheetTransition {
357
- CGFloat top = [self.layer presentationLayer].frame.origin.y;
368
+ CGFloat top = [self.contentView.layer presentationLayer].frame.origin.y;
358
369
  [self dispatchOnSlide:top];
359
370
  }
360
371
 
@@ -1,18 +1,23 @@
1
1
  #import "RNBottomSheetManager.h"
2
2
  #import "RNBottomSheet.h"
3
+ #import "RNBottomSheetState.h"
3
4
 
4
5
  @implementation RNBottomSheetManager
5
6
 
6
7
  RCT_EXPORT_MODULE(BottomSheet)
7
8
 
8
9
  - (UIView *)view {
9
- return [RNBottomSheet new];
10
+ RNBottomSheet *bottomSheet = [[RNBottomSheet alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
11
+ bottomSheet.pointerEvents = RCTPointerEventsBoxNone;
12
+ return bottomSheet;
10
13
  }
11
14
 
12
15
  RCT_EXPORT_VIEW_PROPERTY(onSlide, RCTDirectEventBlock)
13
16
  RCT_EXPORT_VIEW_PROPERTY(onStateChanged, RCTDirectEventBlock)
14
-
15
17
  RCT_EXPORT_VIEW_PROPERTY(peekHeight, CGFloat)
16
- RCT_EXPORT_VIEW_PROPERTY(state, NSString)
18
+
19
+ RCT_CUSTOM_VIEW_PROPERTY(state, NSString, RNBottomSheet) {
20
+ view.state = RNBottomSheetStateFromString([RCTConvert NSString:json]);
21
+ }
17
22
 
18
23
  @end
@@ -0,0 +1,11 @@
1
+ #import <React/RCTEventDispatcherProtocol.h>
2
+
3
+ NS_ASSUME_NONNULL_BEGIN
4
+
5
+ @interface RNBottomSheetOffsetChangedEvent : NSObject <RCTEvent>
6
+
7
+ - (instancetype)initWithViewTag:(NSNumber *)viewTag progress:(CGFloat)progress offset:(CGFloat)offset minY:(CGFloat)minY maxY:(CGFloat)maxY;
8
+
9
+ @end
10
+
11
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,58 @@
1
+ #import "RNBottomSheetOffsetChangedEvent.h"
2
+
3
+ @interface RNBottomSheetOffsetChangedEvent ()
4
+
5
+ @property (nonatomic, assign) CGFloat progress;
6
+ @property (nonatomic, assign) CGFloat offest;
7
+ @property (nonatomic, assign) CGFloat minY;
8
+ @property (nonatomic, assign) CGFloat maxY;
9
+
10
+ @end
11
+
12
+ @implementation RNBottomSheetOffsetChangedEvent
13
+
14
+ @synthesize viewTag = _viewTag;
15
+
16
+ + (NSString *)moduleDotMethod {
17
+ return @"RCTEventEmitter.receiveEvent";
18
+ }
19
+
20
+ RCT_NOT_IMPLEMENTED(- (instancetype)init)
21
+ - (instancetype)initWithViewTag:(NSNumber *)viewTag progress:(CGFloat)progress offset:(CGFloat)offset minY:(CGFloat)minY maxY:(CGFloat)maxY {
22
+ if (self = [super init]) {
23
+ _viewTag = viewTag;
24
+ _progress = progress;
25
+ _offest = offset;
26
+ _minY = minY;
27
+ _maxY = maxY;
28
+ }
29
+ return self;
30
+ }
31
+
32
+ - (NSString *)eventName {
33
+ return @"onSlide";
34
+ }
35
+
36
+ - (BOOL)canCoalesce {
37
+ return YES;
38
+ }
39
+
40
+ - (uint16_t)coalescingKey {
41
+ return 0;
42
+ }
43
+
44
+ - (NSArray *)arguments {
45
+ return @[self.viewTag, RCTNormalizeInputEventName(self.eventName), @{
46
+ @"progress": @(self.progress),
47
+ @"offset": @(self.offest),
48
+ @"expandedOffset": @(self.minY),
49
+ @"collapsedOffset": @(self.maxY)
50
+ }];
51
+ }
52
+
53
+ - (id<RCTEvent>)coalesceWithEvent:(id<RCTEvent>)newEvent {
54
+ return newEvent;
55
+ }
56
+
57
+
58
+ @end
@@ -0,0 +1,17 @@
1
+ #import <React/RCTUtils.h>
2
+
3
+ NS_ASSUME_NONNULL_BEGIN
4
+
5
+ typedef NS_ENUM(NSUInteger, RNBottomSheetState) {
6
+ RNBottomSheetStateCollapsed = 0,
7
+ RNBottomSheetStateExpanded,
8
+ RNBottomSheetStateHidden,
9
+ RNBottomSheetStateDragging,
10
+ RNBottomSheetStateSettling,
11
+ };
12
+
13
+ RCT_EXTERN RNBottomSheetState RNBottomSheetStateFromString(NSString *state);
14
+
15
+ RCT_EXTERN NSString* RNBottomSheetStateToString(RNBottomSheetState state);
16
+
17
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,38 @@
1
+ #import "RNBottomSheetState.h"
2
+
3
+ RNBottomSheetState RNBottomSheetStateFromString(NSString *state) {
4
+ if ([state isEqualToString:@"collapsed"]) {
5
+ return RNBottomSheetStateCollapsed;
6
+ }
7
+ if ([state isEqualToString:@"expanded"]) {
8
+ return RNBottomSheetStateExpanded;
9
+ }
10
+ if ([state isEqualToString:@"settling"]) {
11
+ return RNBottomSheetStateSettling;
12
+ }
13
+ if ([state isEqualToString:@"dragging"]) {
14
+ return RNBottomSheetStateDragging;
15
+ }
16
+ return RNBottomSheetStateHidden;
17
+ }
18
+
19
+
20
+ NSString* RNBottomSheetStateToString(RNBottomSheetState state) {
21
+ if (state == RNBottomSheetStateCollapsed) {
22
+ return @"collapsed";
23
+ }
24
+
25
+ if (state == RNBottomSheetStateExpanded) {
26
+ return @"expanded";
27
+ }
28
+
29
+ if (state == RNBottomSheetStateSettling) {
30
+ return @"settling";
31
+ }
32
+
33
+ if (state == RNBottomSheetStateDragging) {
34
+ return @"dragging";
35
+ }
36
+
37
+ return @"hidden";
38
+ }
@@ -0,0 +1,13 @@
1
+ #import <React/RCTEventDispatcherProtocol.h>
2
+
3
+ #import "RNBottomSheetState.h"
4
+
5
+ NS_ASSUME_NONNULL_BEGIN
6
+
7
+ @interface RNBottomSheetStateChangedEvent : NSObject <RCTEvent>
8
+
9
+ - (instancetype)initWithViewTag:(NSNumber *)viewTag state:(RNBottomSheetState)state;
10
+
11
+ @end
12
+
13
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,50 @@
1
+ #import "RNBottomSheetStateChangedEvent.h"
2
+
3
+ static uint16_t _coalescingKey = 0;
4
+
5
+ @interface RNBottomSheetStateChangedEvent ()
6
+
7
+ @property (nonatomic, assign) RNBottomSheetState state;
8
+
9
+ @end
10
+
11
+ @implementation RNBottomSheetStateChangedEvent
12
+
13
+ @synthesize viewTag = _viewTag;
14
+
15
+ + (NSString *)moduleDotMethod {
16
+ return @"RCTEventEmitter.receiveEvent";
17
+ }
18
+
19
+ RCT_NOT_IMPLEMENTED(- (instancetype)init)
20
+ - (instancetype)initWithViewTag:(NSNumber *)viewTag state:(RNBottomSheetState)state {
21
+ if (self = [super init]) {
22
+ _viewTag = viewTag;
23
+ _state = state;
24
+ }
25
+ return self;
26
+ }
27
+
28
+ - (NSString *)eventName {
29
+ return @"onStateChanged";
30
+ }
31
+
32
+ - (BOOL)canCoalesce {
33
+ return NO;
34
+ }
35
+
36
+ - (uint16_t)coalescingKey {
37
+ return _coalescingKey++;
38
+ }
39
+
40
+ - (NSArray *)arguments {
41
+ return @[self.viewTag, RCTNormalizeInputEventName(self.eventName), @{
42
+ @"state": RNBottomSheetStateToString(self.state)
43
+ }];
44
+ }
45
+
46
+ - (id<RCTEvent>)coalesceWithEvent:(id<RCTEvent>)newEvent {
47
+ return newEvent;
48
+ }
49
+
50
+ @end
package/lib/index.d.ts CHANGED
@@ -15,6 +15,7 @@ interface NativeBottomSheetProps extends ViewProps {
15
15
  onStateChanged?: (event: NativeSyntheticEvent<StateChangedEventData>) => void;
16
16
  peekHeight?: number;
17
17
  state?: BottomSheetState;
18
+ contentContainerStyle?: ViewProps['style'];
18
19
  }
19
20
  declare const BottomSheet: React.ForwardRefExoticComponent<NativeBottomSheetProps & {
20
21
  fitToContents?: boolean | undefined;
package/lib/index.js CHANGED
@@ -1,24 +1,15 @@
1
1
  import React from 'react';
2
- import { Platform, requireNativeComponent, StyleSheet, View } from 'react-native';
2
+ import { requireNativeComponent, StyleSheet, View } from 'react-native';
3
3
  import splitLayoutProps from './splitLayoutProps';
4
4
  const NativeBottomSheet = requireNativeComponent('BottomSheet');
5
5
  const BottomSheet = React.forwardRef((props, ref) => {
6
- const { style, children, peekHeight = 200, state = 'collapsed', fitToContents, ...rest } = props;
6
+ const { style, contentContainerStyle, children, peekHeight = 200, state = 'collapsed', fitToContents, ...rest } = props;
7
7
  const { outer, inner } = splitLayoutProps(StyleSheet.flatten(style));
8
- if (Platform.OS === 'android') {
9
- return (<NativeBottomSheet style={[StyleSheet.absoluteFill, outer]} peekHeight={peekHeight} state={state} {...rest} ref={ref}>
10
- <View style={[fitToContents ? styles.fitToContents : StyleSheet.absoluteFill, inner]} collapsable={false}>
11
- {children}
12
- </View>
13
- </NativeBottomSheet>);
14
- }
15
- else {
16
- return (<View style={[StyleSheet.absoluteFill, outer]} pointerEvents="box-none">
17
- <NativeBottomSheet style={[fitToContents ? styles.fitToContents : StyleSheet.absoluteFill, inner]} peekHeight={peekHeight} state={state} {...rest} ref={ref}>
18
- {children}
19
- </NativeBottomSheet>
20
- </View>);
21
- }
8
+ return (<NativeBottomSheet style={[StyleSheet.absoluteFill, outer]} peekHeight={peekHeight} state={state} {...rest} ref={ref}>
9
+ <View style={[fitToContents ? styles.fitToContents : StyleSheet.absoluteFill, inner, contentContainerStyle]} collapsable={false}>
10
+ {children}
11
+ </View>
12
+ </NativeBottomSheet>);
22
13
  });
23
14
  const styles = StyleSheet.create({
24
15
  fitToContents: {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sdcx/bottom-sheet",
3
3
  "description": "A react-native BottomSheet component.",
4
- "version": "0.9.0",
4
+ "version": "0.10.0",
5
5
  "main": "./lib/index.js",
6
6
  "typings": "./lib/index.d.ts",
7
7
  "react-native": "src/index",
package/src/index.tsx CHANGED
@@ -1,5 +1,5 @@
1
1
  import React from 'react'
2
- import { NativeSyntheticEvent, Platform, requireNativeComponent, StyleSheet, View, ViewProps } from 'react-native'
2
+ import { NativeSyntheticEvent, requireNativeComponent, StyleSheet, View, ViewProps } from 'react-native'
3
3
  import splitLayoutProps from './splitLayoutProps'
4
4
 
5
5
  export interface OffsetChangedEventData {
@@ -20,6 +20,7 @@ interface NativeBottomSheetProps extends ViewProps {
20
20
  onStateChanged?: (event: NativeSyntheticEvent<StateChangedEventData>) => void
21
21
  peekHeight?: number
22
22
  state?: BottomSheetState
23
+ contentContainerStyle?: ViewProps['style']
23
24
  }
24
25
 
25
26
  type BottomSheetProps = NativeBottomSheetProps & {
@@ -31,36 +32,30 @@ const NativeBottomSheet = requireNativeComponent<NativeBottomSheetProps>('Bottom
31
32
  type NativeBottomSheetInstance = InstanceType<typeof NativeBottomSheet>
32
33
 
33
34
  const BottomSheet = React.forwardRef<NativeBottomSheetInstance, BottomSheetProps>((props, ref) => {
34
- const { style, children, peekHeight = 200, state = 'collapsed', fitToContents, ...rest } = props
35
+ const {
36
+ style,
37
+ contentContainerStyle,
38
+ children,
39
+ peekHeight = 200,
40
+ state = 'collapsed',
41
+ fitToContents,
42
+ ...rest
43
+ } = props
35
44
  const { outer, inner } = splitLayoutProps(StyleSheet.flatten(style))
36
-
37
- if (Platform.OS === 'android') {
38
- return (
39
- <NativeBottomSheet
40
- style={[StyleSheet.absoluteFill, outer]}
41
- peekHeight={peekHeight}
42
- state={state}
43
- {...rest}
44
- ref={ref}>
45
- <View style={[fitToContents ? styles.fitToContents : StyleSheet.absoluteFill, inner]} collapsable={false}>
46
- {children}
47
- </View>
48
- </NativeBottomSheet>
49
- )
50
- } else {
51
- return (
52
- <View style={[StyleSheet.absoluteFill, outer]} pointerEvents="box-none">
53
- <NativeBottomSheet
54
- style={[fitToContents ? styles.fitToContents : StyleSheet.absoluteFill, inner]}
55
- peekHeight={peekHeight}
56
- state={state}
57
- {...rest}
58
- ref={ref}>
59
- {children}
60
- </NativeBottomSheet>
45
+ return (
46
+ <NativeBottomSheet
47
+ style={[StyleSheet.absoluteFill, outer]}
48
+ peekHeight={peekHeight}
49
+ state={state}
50
+ {...rest}
51
+ ref={ref}>
52
+ <View
53
+ style={[fitToContents ? styles.fitToContents : StyleSheet.absoluteFill, inner, contentContainerStyle]}
54
+ collapsable={false}>
55
+ {children}
61
56
  </View>
62
- )
63
- }
57
+ </NativeBottomSheet>
58
+ )
64
59
  })
65
60
 
66
61
  const styles = StyleSheet.create({