@lodev09/react-native-true-sheet 0.6.0 → 0.8.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 (37) hide show
  1. package/README.md +37 -20
  2. package/android/src/main/java/com/lodev09/truesheet/{core/TrueSheetBehavior.kt → TrueSheetBehavior.kt} +69 -40
  3. package/android/src/main/java/com/lodev09/truesheet/TrueSheetDialog.kt +84 -0
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +87 -61
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +5 -0
  6. package/android/src/main/java/com/lodev09/truesheet/core/Events.kt +1 -0
  7. package/android/src/main/java/com/lodev09/truesheet/core/KeyboardManager.kt +53 -0
  8. package/android/src/main/java/com/lodev09/truesheet/core/RootViewGroup.kt +1 -1
  9. package/android/src/main/java/com/lodev09/truesheet/core/Utils.kt +5 -37
  10. package/ios/Extensions/UIView+pinTo.swift +54 -10
  11. package/ios/TrueSheetView.swift +106 -56
  12. package/ios/TrueSheetViewController.swift +46 -36
  13. package/ios/TrueSheetViewManager.m +1 -0
  14. package/lib/commonjs/TrueSheet.js +11 -7
  15. package/lib/commonjs/TrueSheet.js.map +1 -1
  16. package/lib/commonjs/TrueSheetGrabber.js +56 -0
  17. package/lib/commonjs/TrueSheetGrabber.js.map +1 -0
  18. package/lib/commonjs/index.js +11 -0
  19. package/lib/commonjs/index.js.map +1 -1
  20. package/lib/module/TrueSheet.js +11 -7
  21. package/lib/module/TrueSheet.js.map +1 -1
  22. package/lib/module/TrueSheetGrabber.js +48 -0
  23. package/lib/module/TrueSheetGrabber.js.map +1 -0
  24. package/lib/module/index.js +1 -0
  25. package/lib/module/index.js.map +1 -1
  26. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  27. package/lib/typescript/src/TrueSheetGrabber.d.ts +31 -0
  28. package/lib/typescript/src/TrueSheetGrabber.d.ts.map +1 -0
  29. package/lib/typescript/src/index.d.ts +1 -0
  30. package/lib/typescript/src/index.d.ts.map +1 -1
  31. package/lib/typescript/src/types.d.ts +8 -0
  32. package/lib/typescript/src/types.d.ts.map +1 -1
  33. package/package.json +1 -1
  34. package/src/TrueSheet.tsx +9 -7
  35. package/src/TrueSheetGrabber.tsx +74 -0
  36. package/src/index.ts +1 -0
  37. package/src/types.ts +9 -0
package/README.md CHANGED
@@ -9,10 +9,10 @@ The true native bottom sheet 💩
9
9
  ![Preview](preview.gif)
10
10
 
11
11
  ## Features
12
- * ✅ Implemented on the native realm.
13
- * ✅ **_NOT_** your pure JS, (re)animated View. But might integrate in the future 👀
14
- * ✅ Clean, fast and lightweight.
15
- * ✅ Handles your Sscrolling needs, easy.
12
+ * ✅ Implemented in the native realm.
13
+ * ✅ **_NOT_** your pure JS, (re)animated view (might integrate in the future 👀)
14
+ * ✅ Clean, fast, and lightweight.
15
+ * ✅ Handles your scrolling needs, easy.
16
16
  * ✅ Asynchronus `ref` methods.
17
17
 
18
18
  ## Installation
@@ -21,6 +21,10 @@ The true native bottom sheet 💩
21
21
  yarn add @lodev09/react-native-true-sheet
22
22
  ```
23
23
 
24
+ ```sh
25
+ npm i @lodev09/react-native-true-sheet
26
+ ```
27
+
24
28
  ## Usage
25
29
 
26
30
  ```ts
@@ -54,15 +58,16 @@ Extended from `ViewProps`
54
58
 
55
59
  | Prop | Type | Default | Description | 🍎 | 🤖 |
56
60
  | - | - | - | - | - | - |
57
- | sizes | [`SheetSize`](#sheetsize) | `['medium', 'large']` | The sizes you want the Sheet to support. Maximum of _**3 sizes**_ only! **_collapsed_**, **_half-expanded_** and **_expanded_**. Example: `size={['auto', '60%', 'large']}`| ✅ | ✅ |
58
- | backgroundColor | `ColorValue` | - | Main sheet background color. | ✅ | ✅ |
59
- | cornerRadius | `number` | - | The sheet corner radius. | ✅ | ✅ |
61
+ | sizes | [`SheetSize`](#sheetsize) | `['medium', 'large']` | The sizes you want the sheet to support. Maximum of _**3 sizes**_ only! **_collapsed_**, **_half-expanded_**, and **_expanded_**. Example: `size={['auto', '60%', 'large']}`| ✅ | ✅ |
62
+ | backgroundColor | `ColorValue` | `white` | Main sheet background color. | ✅ | ✅ |
63
+ | cornerRadius | `number` | - | the sheet corner radius. | ✅ | ✅ |
60
64
  | maxHeight | `number` | - | Overrides `large` or `100%` height. | ✅ | ✅ |
61
65
  | contentContainerStyle | `StyleProp<ViewStyle>` | - | Optional content container styles. | ✅ | ✅ |
62
- | FooterComponent | `ReactNode` | - | A component that floats at the bottom of the Sheet. | ✅ | ✅ |
63
- | grabber | `boolean` | - | Shows native grabber (or handle) on IOS. | ✅ | |
66
+ | FooterComponent | `ReactNode` | - | A component that floats at the bottom of the sheet. | ✅ | ✅ |
67
+ | dismissible | `boolean` | `true` | If set to `false`, the sheet will prevent interactive dismissal via dragging or clicking outside of it. | ✅ | |
68
+ | grabber | `boolean` | `true` | Shows a grabber (or handle). Native on IOS and styled `View` on Android. | ✅ | ✅ |
64
69
  | blurTint | [`BlurTint`](#blurTint) | - | The blur effect style on iOS. Overrides `backgroundColor` if set. Example: `light`, `dark`, etc. | ✅ | |
65
- | scrollRef | `RefObject<...>` | - | The main scrollable ref that Sheet should handle on IOS. | ✅ | |
70
+ | scrollRef | `RefObject<...>` | - | The main scrollable ref that Sheet should handle on iOS. | ✅ | |
66
71
 
67
72
  ## Methods
68
73
 
@@ -91,8 +96,8 @@ return (
91
96
  | Name | Parameters | Description |
92
97
  | - | - | - |
93
98
  | present | `index: number = 0` | Present the modal sheet. Optionally accepts a size `index`. See `sizes` prop. |
94
- | resize | `index: number` | Resizes the Sheet programmatically by `index`. This is an alias of the `present(index)` method. |
95
- | dismiss | - | Dismisses the Sheet. |
99
+ | resize | `index: number` | Resizes the sheet programmatically by `index`. This is an alias of the `present(index)` method. |
100
+ | dismiss | - | Dismisses the sheet. |
96
101
 
97
102
  ## Events
98
103
 
@@ -110,8 +115,8 @@ return (
110
115
 
111
116
  | Name | Parameters | Description |
112
117
  | - | - | - |
113
- | onPresent | [`SizeInfo`](#sizeinfo) | Called when the Sheet has been presented. Comes with the size index and value. |
114
- | onDismiss | - | Called when the Sheet has been dismissed. |
118
+ | onPresent | [`SizeInfo`](#sizeinfo) | Called when the sheet has been presented. Comes with the size index and value. |
119
+ | onDismiss | - | Called when the sheet has been dismissed. |
115
120
  | onSizeChange | [`SizeInfo`](#sizeinfo) | Called when the size of the sheet has changed. Either by dragging or presenting programatically. Comes with the size index and value. |
116
121
 
117
122
  ## Types
@@ -126,16 +131,21 @@ return (
126
131
 
127
132
  | Value | Description | 🍎 | 🤖 |
128
133
  | - | - | - | - |
129
- | `large` | Translates to 100% | ✅ | ✅ |
130
- | `medium` | Translates to 50% | **_15+_** | ✅ |
131
- | `auto` | Auto resize based on content height. | **_16+_** | ✅ |
132
- | `number` | Fixed height | **_16+_** | ✅ |
134
+ | `"large"` | Translates to 100% | ✅ | ✅ |
135
+ | `"medium"` | Translates to 50% | **_15+_** | ✅ |
136
+ | `"auto"` | Auto resize based on content height. | **_16+_** | ✅ |
137
+ | `"number"` | Fixed height | **_16+_** | ✅ |
133
138
  | `${number}%` | Fixed height in % | **_16+_** | ✅ |
134
- | `small` | Translates to 25% | **_16+_** | ✅ |
139
+ | `"small"` | Translates to 25% | **_16+_** | ✅ |
140
+
141
+ > [!NOTE]
142
+ > `auto` is not guaranteed to be accurate if your content depends on various rendering logic. Experiment with it and try to keep your content size as fixed as possible.
143
+ >
144
+ > Alternatively, you can programmatically call [`resize`](#methods) to adjust the sheet size on-the-fly.
135
145
 
136
146
  ### `BlurTint`
137
147
 
138
- Blur style mapped to native values in IOS.
148
+ Blur tint that is mapped into native values in iOS.
139
149
 
140
150
  ```ts
141
151
  <TrueSheet blurTint="dark">
@@ -183,6 +193,13 @@ Blur style mapped to native values in IOS.
183
193
  | index | `number` | The size index from the provided sizes. See `sizes` prop. |
184
194
  | value | `number` | The actual height value of the size. |
185
195
 
196
+ ## v1 Roadmap
197
+
198
+ - [ ] Android: grabber
199
+ - [ ] Inline sheet
200
+ - [ ] Reanimated integration(?)
201
+ - [ ] Any ideas?
202
+
186
203
  ## Contributing
187
204
 
188
205
  See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
@@ -1,16 +1,17 @@
1
- package com.lodev09.truesheet.core
1
+ package com.lodev09.truesheet
2
2
 
3
- import android.graphics.Point
4
3
  import android.view.MotionEvent
5
4
  import android.view.ViewGroup
6
5
  import android.widget.ScrollView
7
6
  import androidx.coordinatorlayout.widget.CoordinatorLayout
7
+ import com.facebook.react.bridge.ReactContext
8
8
  import com.google.android.material.bottomsheet.BottomSheetBehavior
9
+ import com.lodev09.truesheet.core.Utils
9
10
 
10
11
  data class SizeInfo(val index: Int, val value: Float)
11
12
 
12
- class TrueSheetBehavior : BottomSheetBehavior<ViewGroup>() {
13
- var maxSheetSize: Point = Point()
13
+ class TrueSheetBehavior(private val reactContext: ReactContext) : BottomSheetBehavior<ViewGroup>() {
14
+ var maxScreenHeight: Int = 0
14
15
  var maxSheetHeight: Int? = null
15
16
 
16
17
  var contentView: ViewGroup? = null
@@ -56,6 +57,9 @@ class TrueSheetBehavior : BottomSheetBehavior<ViewGroup>() {
56
57
  event.action == MotionEvent.ACTION_CANCEL
57
58
  }
58
59
 
60
+ /**
61
+ * Get the height value based on the size config value.
62
+ */
59
63
  private fun getSizeHeight(size: Any, contentHeight: Int): Int {
60
64
  val height =
61
65
  when (size) {
@@ -67,11 +71,11 @@ class TrueSheetBehavior : BottomSheetBehavior<ViewGroup>() {
67
71
  when (size) {
68
72
  "auto" -> contentHeight
69
73
 
70
- "large" -> maxSheetSize.y
74
+ "large" -> maxScreenHeight
71
75
 
72
- "medium" -> (maxSheetSize.y * 0.50).toInt()
76
+ "medium" -> (maxScreenHeight * 0.50).toInt()
73
77
 
74
- "small" -> (maxSheetSize.y * 0.25).toInt()
78
+ "small" -> (maxScreenHeight * 0.25).toInt()
75
79
 
76
80
  else -> {
77
81
  if (size.endsWith('%')) {
@@ -79,7 +83,7 @@ class TrueSheetBehavior : BottomSheetBehavior<ViewGroup>() {
79
83
  if (percent == null) {
80
84
  0
81
85
  } else {
82
- ((percent / 100) * maxSheetSize.y).toInt()
86
+ ((percent / 100) * maxScreenHeight).toInt()
83
87
  }
84
88
  } else {
85
89
  val fixedHeight = size.toDoubleOrNull()
@@ -93,13 +97,46 @@ class TrueSheetBehavior : BottomSheetBehavior<ViewGroup>() {
93
97
  }
94
98
  }
95
99
 
96
- else -> (maxSheetSize.y * 0.5).toInt()
100
+ else -> (maxScreenHeight * 0.5).toInt()
97
101
  }
98
102
 
99
- return minOf(height, maxSheetHeight ?: maxSheetSize.y)
103
+ return minOf(height, maxSheetHeight ?: maxScreenHeight)
100
104
  }
101
105
 
106
+ /**
107
+ * Determines the state based on the given size index.
108
+ */
109
+ fun getStateForSizeIndex(index: Int) =
110
+ when (sizes.size) {
111
+ 1 -> STATE_EXPANDED
112
+
113
+ 2 -> {
114
+ when (index) {
115
+ 0 -> STATE_COLLAPSED
116
+ 1 -> STATE_EXPANDED
117
+ else -> STATE_HIDDEN
118
+ }
119
+ }
120
+
121
+ 3 -> {
122
+ when (index) {
123
+ 0 -> STATE_COLLAPSED
124
+ 1 -> STATE_HALF_EXPANDED
125
+ 2 -> STATE_EXPANDED
126
+ else -> STATE_HIDDEN
127
+ }
128
+ }
129
+
130
+ else -> STATE_HIDDEN
131
+ }
132
+
133
+ /**
134
+ * Configure the sheet based on size preferences.
135
+ */
102
136
  fun configure() {
137
+ // Update the usable sheet height
138
+ maxScreenHeight = Utils.activityView(reactContext)?.height ?: 0
139
+
103
140
  var contentHeight = 0
104
141
 
105
142
  contentView?.let { contentHeight = it.height }
@@ -107,13 +144,16 @@ class TrueSheetBehavior : BottomSheetBehavior<ViewGroup>() {
107
144
 
108
145
  // Configure sheet sizes
109
146
  apply {
110
- isFitToContents = true
111
- isHideable = true
112
147
  skipCollapsed = false
148
+ isFitToContents = true
149
+
150
+ // m3 max width 640dp
151
+ maxWidth = Utils.toPixel(640.0)
113
152
 
114
153
  when (sizes.size) {
115
154
  1 -> {
116
155
  maxHeight = getSizeHeight(sizes[0], contentHeight)
156
+ peekHeight = maxHeight
117
157
  skipCollapsed = true
118
158
  }
119
159
 
@@ -127,37 +167,16 @@ class TrueSheetBehavior : BottomSheetBehavior<ViewGroup>() {
127
167
  isFitToContents = false
128
168
 
129
169
  peekHeight = getSizeHeight(sizes[0], contentHeight)
130
- halfExpandedRatio = getSizeHeight(sizes[1], contentHeight).toFloat() / maxSheetSize.y.toFloat()
170
+ halfExpandedRatio = getSizeHeight(sizes[1], contentHeight).toFloat() / maxScreenHeight.toFloat()
131
171
  maxHeight = getSizeHeight(sizes[2], contentHeight)
132
172
  }
133
173
  }
134
174
  }
135
175
  }
136
176
 
137
- fun getStateForIndex(index: Int) =
138
- when (sizes.size) {
139
- 1 -> STATE_EXPANDED
140
-
141
- 2 -> {
142
- when (index) {
143
- 0 -> STATE_COLLAPSED
144
- 1 -> STATE_EXPANDED
145
- else -> STATE_HIDDEN
146
- }
147
- }
148
-
149
- 3 -> {
150
- when (index) {
151
- 0 -> STATE_COLLAPSED
152
- 1 -> STATE_HALF_EXPANDED
153
- 2 -> STATE_EXPANDED
154
- else -> STATE_HIDDEN
155
- }
156
- }
157
-
158
- else -> STATE_HIDDEN
159
- }
160
-
177
+ /**
178
+ * Get the SizeInfo data by state.
179
+ */
161
180
  fun getSizeInfoForState(state: Int): SizeInfo? =
162
181
  when (sizes.size) {
163
182
  1 -> {
@@ -180,7 +199,7 @@ class TrueSheetBehavior : BottomSheetBehavior<ViewGroup>() {
180
199
  STATE_COLLAPSED -> SizeInfo(0, Utils.toDIP(peekHeight))
181
200
 
182
201
  STATE_HALF_EXPANDED -> {
183
- val height = halfExpandedRatio * maxSheetSize.y
202
+ val height = halfExpandedRatio * maxScreenHeight
184
203
  SizeInfo(1, Utils.toDIP(height.toInt()))
185
204
  }
186
205
 
@@ -193,9 +212,19 @@ class TrueSheetBehavior : BottomSheetBehavior<ViewGroup>() {
193
212
  else -> null
194
213
  }
195
214
 
196
- fun getSizeInfoForIndex(index: Int) = getSizeInfoForState(getStateForIndex(index)) ?: SizeInfo(0, 0f)
215
+ /**
216
+ * Get SizeInfo data for given size index.
217
+ */
218
+ fun getSizeInfoForIndex(index: Int) = getSizeInfoForState(getStateForSizeIndex(index)) ?: SizeInfo(0, 0f)
197
219
 
220
+ /**
221
+ * Set the state based on the given size index.
222
+ */
198
223
  fun setStateForSizeIndex(index: Int) {
199
- state = getStateForIndex(index)
224
+ state = getStateForSizeIndex(index)
225
+ }
226
+
227
+ companion object {
228
+ const val TAG = "TrueSheetView"
200
229
  }
201
230
  }
@@ -0,0 +1,84 @@
1
+ package com.lodev09.truesheet
2
+
3
+ import android.graphics.Color
4
+ import android.view.ViewGroup
5
+ import android.view.WindowManager
6
+ import android.widget.LinearLayout
7
+ import androidx.coordinatorlayout.widget.CoordinatorLayout
8
+ import com.facebook.react.uimanager.ThemedReactContext
9
+ import com.google.android.material.bottomsheet.BottomSheetDialog
10
+ import com.lodev09.truesheet.core.KeyboardManager
11
+ import com.lodev09.truesheet.core.RootViewGroup
12
+ import com.lodev09.truesheet.core.Utils
13
+
14
+ class TrueSheetDialog(
15
+ private val reactContext: ThemedReactContext,
16
+ private val behavior: TrueSheetBehavior,
17
+ private val rootViewGroup: RootViewGroup
18
+ ) : BottomSheetDialog(reactContext) {
19
+
20
+ private var keyboardManager = KeyboardManager(reactContext)
21
+
22
+ var sheetView: ViewGroup
23
+
24
+ init {
25
+ LinearLayout(reactContext).apply {
26
+ addView(rootViewGroup)
27
+ setContentView(this)
28
+
29
+ sheetView = parent as ViewGroup
30
+
31
+ // Set to transparent background to support corner radius
32
+ sheetView.setBackgroundColor(Color.TRANSPARENT)
33
+
34
+ // Assign our main BottomSheetBehavior
35
+ val sheetViewParams = sheetView.layoutParams as CoordinatorLayout.LayoutParams
36
+ sheetViewParams.behavior = behavior
37
+ }
38
+
39
+ // Setup window params to adjust layout based on Keyboard state.
40
+ window?.apply {
41
+ // SOFT_INPUT_ADJUST_RESIZE to resize the sheet above the keyboard
42
+ setSoftInputMode(
43
+ WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
44
+ )
45
+ }
46
+ }
47
+
48
+ fun show(sizeIndex: Int) {
49
+ if (isShowing) {
50
+ behavior.setStateForSizeIndex(sizeIndex)
51
+ } else {
52
+ behavior.configure()
53
+ behavior.setStateForSizeIndex(sizeIndex)
54
+
55
+ this.show()
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Handle keyboard state changes and adjust maxScreenHeight (sheet max height) accordingly.
61
+ * Also update footer's Y position.
62
+ */
63
+ fun registerKeyboardManager() {
64
+ keyboardManager.registerKeyboardListener(object : KeyboardManager.OnKeyboardListener {
65
+ override fun onKeyboardStateChange(isVisible: Boolean) {
66
+ behavior.maxScreenHeight = Utils.activityView(reactContext)?.height ?: 0
67
+ behavior.footerView?.apply {
68
+ y = (behavior.maxScreenHeight - (sheetView.top ?: 0) - height).toFloat()
69
+ }
70
+ }
71
+ })
72
+ }
73
+
74
+ /**
75
+ * Remove keyboard listener.
76
+ */
77
+ fun unregisterKeyboardManager() {
78
+ keyboardManager.unregisterKeyboardListener()
79
+ }
80
+
81
+ companion object {
82
+ const val TAG = "TrueSheetView"
83
+ }
84
+ }
@@ -1,26 +1,20 @@
1
1
  package com.lodev09.truesheet
2
2
 
3
3
  import android.content.Context
4
- import android.graphics.Color
5
4
  import android.view.View
6
5
  import android.view.ViewGroup
7
6
  import android.view.ViewStructure
8
7
  import android.view.accessibility.AccessibilityEvent
9
- import android.widget.LinearLayout
10
- import androidx.coordinatorlayout.widget.CoordinatorLayout
11
8
  import com.facebook.react.bridge.LifecycleEventListener
12
9
  import com.facebook.react.bridge.UiThreadUtil
13
10
  import com.facebook.react.uimanager.ThemedReactContext
14
11
  import com.facebook.react.uimanager.UIManagerHelper
15
12
  import com.facebook.react.uimanager.events.EventDispatcher
16
13
  import com.google.android.material.bottomsheet.BottomSheetBehavior
17
- import com.google.android.material.bottomsheet.BottomSheetDialog
18
14
  import com.lodev09.truesheet.core.DismissEvent
19
15
  import com.lodev09.truesheet.core.PresentEvent
20
16
  import com.lodev09.truesheet.core.RootViewGroup
21
17
  import com.lodev09.truesheet.core.SizeChangeEvent
22
- import com.lodev09.truesheet.core.TrueSheetBehavior
23
- import com.lodev09.truesheet.core.Utils
24
18
 
25
19
  class TrueSheetView(context: Context) :
26
20
  ViewGroup(context),
@@ -33,22 +27,44 @@ class TrueSheetView(context: Context) :
33
27
  private val surfaceId: Int
34
28
  get() = UIManagerHelper.getSurfaceId(this)
35
29
 
30
+ /**
31
+ * Current activeIndex.
32
+ */
36
33
  private var activeIndex: Int = 0
37
34
 
35
+ /**
36
+ * Promise callback to be invoked after `present` is called.
37
+ */
38
38
  private var presentPromise: (() -> Unit)? = null
39
+
40
+ /**
41
+ * Promise callback to be invoked after `dismiss` is called.
42
+ */
39
43
  private var dismissPromise: (() -> Unit)? = null
40
44
 
41
- private val sheetDialog: BottomSheetDialog
45
+ /**
46
+ * The main BottomSheetDialog instance.
47
+ */
48
+ private val sheetDialog: TrueSheetDialog
49
+
50
+ /**
51
+ * The custom BottomSheetDialogBehavior instance.
52
+ */
42
53
  private val sheetBehavior: TrueSheetBehavior
43
- private val sheetView: ViewGroup
44
54
 
45
- // React root view placeholder
55
+ /**
56
+ * React root view placeholder.
57
+ */
46
58
  private val sheetRootView: RootViewGroup
47
59
 
48
- // 1st child of the container view
60
+ /**
61
+ * 1st child of the container view.
62
+ */
49
63
  private var contentView: ViewGroup? = null
50
64
 
51
- // 2nd child of the container view
65
+ /**
66
+ * 2nd child of the container view.
67
+ */
52
68
  private var footerView: ViewGroup? = null
53
69
 
54
70
  init {
@@ -58,73 +74,78 @@ class TrueSheetView(context: Context) :
58
74
  sheetRootView = RootViewGroup(context)
59
75
  sheetRootView.eventDispatcher = eventDispatcher
60
76
 
61
- sheetDialog = BottomSheetDialog(context)
62
- sheetBehavior = TrueSheetBehavior()
63
-
64
- // Configure the sheet layout view
65
- LinearLayout(context).apply {
66
- addView(sheetRootView)
67
- sheetDialog.setContentView(this)
68
-
69
- sheetView = parent as ViewGroup
70
- sheetView.setBackgroundColor(Color.TRANSPARENT)
71
-
72
- // Assign our main BottomSheetBehavior
73
- val sheetViewParams = sheetView.layoutParams as CoordinatorLayout.LayoutParams
74
- sheetViewParams.behavior = sheetBehavior
75
- }
77
+ sheetBehavior = TrueSheetBehavior(reactContext)
78
+ sheetDialog = TrueSheetDialog(reactContext, sheetBehavior, sheetRootView)
76
79
 
77
80
  // Configure Sheet Dialog
78
81
  sheetDialog.apply {
82
+ // Setup listener when the dialog has been presented.
79
83
  setOnShowListener {
84
+ registerKeyboardManager()
85
+
80
86
  // Initialize footer y
81
- UiThreadUtil.runOnUiThread {
82
- footerView?.let {
83
- it.y = (sheetView.height - sheetView.top - it.height).toFloat()
87
+ footerView?.apply {
88
+ UiThreadUtil.runOnUiThread {
89
+ y = (sheetBehavior.maxScreenHeight - sheetView.top - height).toFloat()
84
90
  }
85
91
  }
86
92
 
87
- presentPromise?.invoke()
88
- presentPromise = null
93
+ presentPromise?.let { promise ->
94
+ promise()
95
+ presentPromise = null
96
+ }
89
97
 
90
98
  // dispatch onPresent event
91
99
  eventDispatcher?.dispatchEvent(PresentEvent(surfaceId, id, sheetBehavior.getSizeInfoForIndex(activeIndex)))
92
100
  }
93
101
 
102
+ // Setup listener when the dialog has been dismissed.
94
103
  setOnDismissListener {
95
- dismissPromise?.invoke()
96
- dismissPromise = null
104
+ unregisterKeyboardManager()
105
+ dismissPromise?.let { promise ->
106
+ promise()
107
+ dismissPromise = null
108
+ }
97
109
 
98
110
  // dispatch onDismiss event
99
111
  eventDispatcher?.dispatchEvent(DismissEvent(surfaceId, id))
100
112
  }
101
113
  }
102
114
 
103
- // Configure Sheet events
115
+ // Configure sheet behavior events
104
116
  sheetBehavior.apply {
105
-
106
- // Set our default max height
107
- maxSheetSize = Utils.maxSize(context)
108
-
109
117
  addBottomSheetCallback(
110
118
  object : BottomSheetBehavior.BottomSheetCallback() {
111
119
  override fun onSlide(sheetView: View, slideOffset: Float) {
112
120
  footerView?.let {
113
- it.y = (sheetView.height - sheetView.top - it.height).toFloat()
121
+ val y = (maxScreenHeight - sheetView.top - it.height).toFloat()
122
+ if (slideOffset >= 0) {
123
+ it.y = y
124
+ } else {
125
+ it.y = y - it.height * slideOffset
126
+ }
114
127
  }
115
128
  }
116
129
 
117
130
  override fun onStateChanged(view: View, newState: Int) {
118
- val sizeInfo = getSizeInfoForState(newState)
119
- if (sizeInfo != null && sizeInfo.index != activeIndex) {
120
- activeIndex = sizeInfo.index
121
-
122
- // dispatch onSizeChange event
123
- eventDispatcher?.dispatchEvent(SizeChangeEvent(surfaceId, id, sizeInfo))
124
- }
125
-
126
- if (newState == BottomSheetBehavior.STATE_HIDDEN) {
127
- sheetDialog.dismiss()
131
+ when (newState) {
132
+ BottomSheetBehavior.STATE_HIDDEN -> sheetDialog.dismiss()
133
+
134
+ else -> {
135
+ val sizeInfo = getSizeInfoForState(newState)
136
+ if (sizeInfo != null && sizeInfo.index != activeIndex) {
137
+ // Invoke promise when sheet resized programmatically
138
+ presentPromise?.let { promise ->
139
+ promise()
140
+ presentPromise = null
141
+ }
142
+
143
+ activeIndex = sizeInfo.index
144
+
145
+ // dispatch onSizeChange event
146
+ eventDispatcher?.dispatchEvent(SizeChangeEvent(surfaceId, id, sizeInfo))
147
+ }
148
+ }
128
149
  }
129
150
  }
130
151
  }
@@ -215,26 +236,31 @@ class TrueSheetView(context: Context) :
215
236
  sheetBehavior.configure()
216
237
  }
217
238
 
239
+ fun setDismissible(dismissible: Boolean) {
240
+ sheetBehavior.isHideable = dismissible
241
+ sheetDialog.setCancelable(dismissible)
242
+ }
243
+
218
244
  fun setSizes(newSizes: Array<Any>) {
219
245
  sheetBehavior.sizes = newSizes
220
246
  sheetBehavior.configure()
221
247
  }
222
248
 
223
- fun present(index: Int, promiseCallback: () -> Unit) {
224
- if (sheetDialog.isShowing) {
225
- sheetBehavior.setStateForSizeIndex(index)
226
- promiseCallback()
227
- } else {
228
- sheetBehavior.configure()
229
-
230
- activeIndex = index
231
- sheetBehavior.setStateForSizeIndex(index)
232
-
233
- presentPromise = promiseCallback
234
- sheetDialog.show()
249
+ /**
250
+ * Present the sheet at given size index.
251
+ */
252
+ fun present(sizeIndex: Int, promiseCallback: () -> Unit) {
253
+ if (!sheetDialog.isShowing) {
254
+ activeIndex = sizeIndex
235
255
  }
256
+
257
+ presentPromise = promiseCallback
258
+ sheetDialog.show(sizeIndex)
236
259
  }
237
260
 
261
+ /**
262
+ * Dismisses the sheet.
263
+ */
238
264
  fun dismiss(promiseCallback: () -> Unit) {
239
265
  dismissPromise = promiseCallback
240
266
  sheetDialog.dismiss()
@@ -34,6 +34,11 @@ class TrueSheetViewManager : ViewGroupManager<TrueSheetView>() {
34
34
  view.setMaxHeight(Utils.toPixel(height))
35
35
  }
36
36
 
37
+ @ReactProp(name = "dismissible")
38
+ fun setDismissible(view: TrueSheetView, dismissible: Boolean) {
39
+ view.setDismissible(dismissible)
40
+ }
41
+
37
42
  @ReactProp(name = "sizes")
38
43
  fun setSizes(view: TrueSheetView, sizes: ReadableArray) {
39
44
  val result = ArrayList<Any>()
@@ -3,6 +3,7 @@ package com.lodev09.truesheet.core
3
3
  import com.facebook.react.bridge.Arguments
4
4
  import com.facebook.react.bridge.WritableMap
5
5
  import com.facebook.react.uimanager.events.Event
6
+ import com.lodev09.truesheet.SizeInfo
6
7
 
7
8
  // onPresent
8
9
  class PresentEvent(surfaceId: Int, viewId: Int, private val sizeInfo: SizeInfo) : Event<PresentEvent>(surfaceId, viewId) {