@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.
- package/README.md +37 -20
- package/android/src/main/java/com/lodev09/truesheet/{core/TrueSheetBehavior.kt → TrueSheetBehavior.kt} +69 -40
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetDialog.kt +84 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +87 -61
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +5 -0
- package/android/src/main/java/com/lodev09/truesheet/core/Events.kt +1 -0
- package/android/src/main/java/com/lodev09/truesheet/core/KeyboardManager.kt +53 -0
- package/android/src/main/java/com/lodev09/truesheet/core/RootViewGroup.kt +1 -1
- package/android/src/main/java/com/lodev09/truesheet/core/Utils.kt +5 -37
- package/ios/Extensions/UIView+pinTo.swift +54 -10
- package/ios/TrueSheetView.swift +106 -56
- package/ios/TrueSheetViewController.swift +46 -36
- package/ios/TrueSheetViewManager.m +1 -0
- package/lib/commonjs/TrueSheet.js +11 -7
- package/lib/commonjs/TrueSheet.js.map +1 -1
- package/lib/commonjs/TrueSheetGrabber.js +56 -0
- package/lib/commonjs/TrueSheetGrabber.js.map +1 -0
- package/lib/commonjs/index.js +11 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/TrueSheet.js +11 -7
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/TrueSheetGrabber.js +48 -0
- package/lib/module/TrueSheetGrabber.js.map +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheetGrabber.d.ts +31 -0
- package/lib/typescript/src/TrueSheetGrabber.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +8 -0
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/TrueSheet.tsx +9 -7
- package/src/TrueSheetGrabber.tsx +74 -0
- package/src/index.ts +1 -0
- package/src/types.ts +9 -0
package/README.md
CHANGED
|
@@ -9,10 +9,10 @@ The true native bottom sheet 💩
|
|
|
9
9
|

|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
|
-
* ✅ Implemented
|
|
13
|
-
* ✅ **_NOT_** your pure JS, (re)animated
|
|
14
|
-
* ✅ Clean, fast and lightweight.
|
|
15
|
-
* ✅ Handles your
|
|
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
|
|
58
|
-
| backgroundColor | `ColorValue` |
|
|
59
|
-
| cornerRadius | `number` | - |
|
|
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
|
|
63
|
-
|
|
|
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
|
|
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
|
|
95
|
-
| dismiss | - | Dismisses the
|
|
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
|
|
114
|
-
| onDismiss | - | Called when the
|
|
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
|
|
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
|
|
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
|
|
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" ->
|
|
74
|
+
"large" -> maxScreenHeight
|
|
71
75
|
|
|
72
|
-
"medium" -> (
|
|
76
|
+
"medium" -> (maxScreenHeight * 0.50).toInt()
|
|
73
77
|
|
|
74
|
-
"small" -> (
|
|
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) *
|
|
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 -> (
|
|
100
|
+
else -> (maxScreenHeight * 0.5).toInt()
|
|
97
101
|
}
|
|
98
102
|
|
|
99
|
-
return minOf(height, maxSheetHeight ?:
|
|
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() /
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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 *
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
55
|
+
/**
|
|
56
|
+
* React root view placeholder.
|
|
57
|
+
*/
|
|
46
58
|
private val sheetRootView: RootViewGroup
|
|
47
59
|
|
|
48
|
-
|
|
60
|
+
/**
|
|
61
|
+
* 1st child of the container view.
|
|
62
|
+
*/
|
|
49
63
|
private var contentView: ViewGroup? = null
|
|
50
64
|
|
|
51
|
-
|
|
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
|
-
|
|
62
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
87
|
+
footerView?.apply {
|
|
88
|
+
UiThreadUtil.runOnUiThread {
|
|
89
|
+
y = (sheetBehavior.maxScreenHeight - sheetView.top - height).toFloat()
|
|
84
90
|
}
|
|
85
91
|
}
|
|
86
92
|
|
|
87
|
-
presentPromise?.
|
|
88
|
-
|
|
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
|
-
|
|
96
|
-
dismissPromise
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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) {
|