@lodev09/react-native-true-sheet 0.9.8 → 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 +9 -224
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetDialog.kt +25 -23
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +25 -20
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +10 -0
- package/ios/TrueSheetView.swift +37 -45
- package/ios/TrueSheetViewController.swift +6 -2
- package/ios/TrueSheetViewManager.m +4 -0
- package/lib/commonjs/TrueSheet.js +79 -26
- package/lib/commonjs/TrueSheet.js.map +1 -1
- package/lib/commonjs/TrueSheetFooter.js +20 -0
- package/lib/commonjs/TrueSheetFooter.js.map +1 -0
- package/lib/commonjs/__mocks__/index.js +3 -0
- package/lib/commonjs/__mocks__/index.js.map +1 -1
- package/lib/module/TrueSheet.js +78 -25
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/TrueSheetFooter.js +12 -0
- package/lib/module/TrueSheetFooter.js.map +1 -0
- package/lib/module/__mocks__/index.js +3 -0
- package/lib/module/__mocks__/index.js.map +1 -1
- package/lib/typescript/docs/docusaurus.config.d.ts +4 -0
- package/lib/typescript/docs/docusaurus.config.d.ts.map +1 -0
- package/lib/typescript/docs/sidebars.d.ts +4 -0
- package/lib/typescript/docs/sidebars.d.ts.map +1 -0
- package/lib/typescript/src/TrueSheet.d.ts +27 -3
- package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheetFooter.d.ts +8 -0
- package/lib/typescript/src/TrueSheetFooter.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +15 -0
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/TrueSheet.tsx +102 -37
- package/src/TrueSheetFooter.tsx +18 -0
- package/src/__mocks__/index.js +4 -0
- package/src/types.ts +15 -0
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|

|
|
5
5
|

|
|
6
6
|
|
|
7
|
-
The true native bottom sheet 💩
|
|
7
|
+
The true native bottom sheet experience for your React Native Apps. 💩
|
|
8
8
|
|
|
9
9
|

|
|
10
10
|
|
|
@@ -15,9 +15,8 @@ The true native bottom sheet 💩
|
|
|
15
15
|
* ✅ Handles your _Scrolling_ needs, natively.
|
|
16
16
|
* ✅ Handles your _Footer_ needs, natively.
|
|
17
17
|
* ✅ Handles your _Keyboard_ needs, natively.
|
|
18
|
-
* ✅ Asynchronus `ref` [methods](#methods).
|
|
19
|
-
* ✅
|
|
20
|
-
* ✅ Bonus! [Blur](#blurtint) support on iOS 😎
|
|
18
|
+
* ✅ Asynchronus `ref` [methods](https://sheet.lodev09.com/reference/methods#ref-methods).
|
|
19
|
+
* ✅ Bonus! [Blur](https://sheet.lodev09.com/reference/types#blurtint) support on IOS 😎
|
|
21
20
|
|
|
22
21
|
## Installation
|
|
23
22
|
|
|
@@ -29,6 +28,12 @@ yarn add @lodev09/react-native-true-sheet
|
|
|
29
28
|
npm i @lodev09/react-native-true-sheet
|
|
30
29
|
```
|
|
31
30
|
|
|
31
|
+
## Documentation
|
|
32
|
+
|
|
33
|
+
- [Guides](https://sheet.lodev09.com/category/guides)
|
|
34
|
+
- [Reference](https://sheet.lodev09.com/category/reference)
|
|
35
|
+
- [Example](example)
|
|
36
|
+
|
|
32
37
|
## Usage
|
|
33
38
|
|
|
34
39
|
```tsx
|
|
@@ -57,226 +62,6 @@ return (
|
|
|
57
62
|
)
|
|
58
63
|
```
|
|
59
64
|
|
|
60
|
-
## Options
|
|
61
|
-
|
|
62
|
-
Props available for `TrueSheet`.
|
|
63
|
-
Extends `ViewProps`
|
|
64
|
-
|
|
65
|
-
| Prop | Type | Default | Description | 🍎 | 🤖 |
|
|
66
|
-
| - | - | - | - | - | - |
|
|
67
|
-
| sizes | [`SheetSize[]`](#sheetsize) | `["medium", "large"]` | Array of sizes you want the sheet to support. Maximum of _**3 sizes**_ only! **_collapsed_**, **_half-expanded_**, and **_expanded_**. Example: `size={["auto", "60%", "large"]}`| ✅ | ✅ |
|
|
68
|
-
| backgroundColor | `ColorValue` | `"white"` | The sheet's background color. | ✅ | ✅ |
|
|
69
|
-
| cornerRadius | `number` | - | the sheet corner radius. | ✅ | ✅ |
|
|
70
|
-
| maxHeight | `number` | - | Overrides `"large"` or `"100%"` height. | ✅ | ✅ |
|
|
71
|
-
| contentContainerStyle | `StyleProp<ViewStyle>` | - | Optional content container styles. | ✅ | ✅ |
|
|
72
|
-
| FooterComponent | `ComponentType<...> \| ReactElement` | - | A component that floats at the bottom of the sheet. Accepts a functional `Component` or `ReactElement`. | ✅ | ✅ |
|
|
73
|
-
| dismissible | `boolean` | `true` | If set to `false`, the sheet will prevent interactive dismissal via dragging or clicking outside of it. | ✅ | ✅ |
|
|
74
|
-
| grabber | `boolean` | `true` | Shows a grabber (or handle). Native on IOS and styled `View` on Android. | ✅ | ✅ |
|
|
75
|
-
| grabberProps | [`TrueSheetGrabberProps`](#truesheetgrabberprops) | - | Overrides the grabber props for android. | | ✅ |
|
|
76
|
-
| blurTint | [`BlurTint`](#blurtint) | - | The blur effect style on iOS. Overrides `backgroundColor` if set. Example: `"light"`, `"dark"`, etc. | ✅ | |
|
|
77
|
-
| scrollRef | `RefObject<...>` | - | The main scrollable ref that the sheet should handle on iOS. | ✅ | |
|
|
78
|
-
|
|
79
|
-
## Methods
|
|
80
|
-
|
|
81
|
-
```tsx
|
|
82
|
-
const sheet = useRef<TrueSheet>(null)
|
|
83
|
-
|
|
84
|
-
const resize = () => {
|
|
85
|
-
sheet.current?.resize(1)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const dismiss = () => {
|
|
89
|
-
sheet.current?.dismiss()
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return (
|
|
93
|
-
<View>
|
|
94
|
-
<Button onPress={resize} title="Resize to 80%" />
|
|
95
|
-
<Button onPress={dismiss} title="Dimiss" />
|
|
96
|
-
<TrueSheet sizes={['auto', '80%']} ref={sheet}>
|
|
97
|
-
// ...
|
|
98
|
-
</TrueSheet>
|
|
99
|
-
</View>
|
|
100
|
-
)
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
| Name | Parameters | Description |
|
|
104
|
-
| - | - | - |
|
|
105
|
-
| present | `index: number = 0` | Present the modal sheet. Optionally accepts a size `index`. See `sizes` prop. |
|
|
106
|
-
| resize | `index: number` | Resizes the sheet programmatically by `index`. This is an alias of the `present(index)` method. |
|
|
107
|
-
| dismiss | - | Dismisses the sheet. |
|
|
108
|
-
|
|
109
|
-
## Events
|
|
110
|
-
|
|
111
|
-
```tsx
|
|
112
|
-
const handleSizeChange = (info: SizeInfo) => {
|
|
113
|
-
console.log(info)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return (
|
|
117
|
-
<TrueSheet onSizeChange={handleSizeChange} sizes={['auto', '80%']} ref={sheet}>
|
|
118
|
-
// ...
|
|
119
|
-
</TrueSheet>
|
|
120
|
-
)
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
| Name | Parameters | Description |
|
|
124
|
-
| - | - | - |
|
|
125
|
-
| onPresent | [`SizeInfo`](#sizeinfo) | Called when the sheet has been presented. Comes with the size index and value. |
|
|
126
|
-
| onDismiss | - | Called when the sheet has been dismissed. |
|
|
127
|
-
| 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. |
|
|
128
|
-
|
|
129
|
-
## Types
|
|
130
|
-
|
|
131
|
-
### `SheetSize`
|
|
132
|
-
|
|
133
|
-
```tsx
|
|
134
|
-
<TrueSheet sizes={['auto', '80%', 'large']}>
|
|
135
|
-
// ...
|
|
136
|
-
</TrueSheet>
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
| Value | Description | 🍎 | 🤖 |
|
|
140
|
-
| - | - | - | - |
|
|
141
|
-
| `"auto"` | Auto resize based on content height. | **_16+_** | ✅ |
|
|
142
|
-
| `"small"` | Translates to 25% | **_16+_** | ✅ |
|
|
143
|
-
| `"medium"` | Translates to 50% | **_15+_** | ✅ |
|
|
144
|
-
| `"large"` | Translates to 100% | ✅ | ✅ |
|
|
145
|
-
| `number` | Fixed height | **_16+_** | ✅ |
|
|
146
|
-
| `${number}%` | Fixed height in % | **_16+_** | ✅ |
|
|
147
|
-
|
|
148
|
-
> [!NOTE]
|
|
149
|
-
> `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.
|
|
150
|
-
>
|
|
151
|
-
> Alternatively, you can programmatically call [`resize`](#methods) to adjust the sheet size on-the-fly.
|
|
152
|
-
|
|
153
|
-
### `TrueSheetGrabberProps`
|
|
154
|
-
|
|
155
|
-
Grabber props to be used for android grabber or handle.
|
|
156
|
-
|
|
157
|
-
| Prop | Type | Default | Description |
|
|
158
|
-
| - | - | - | - |
|
|
159
|
-
| visible | `boolean` | `true` | Is grabber visible. |
|
|
160
|
-
| color | `ColorValue` | `"rgba(73,69,79,0.4)"` | Grabber color according to M3 specs. |
|
|
161
|
-
| height | `number` | `4` | Grabber height according to M3 specs. |
|
|
162
|
-
| width | `number` | `32` | Grabber width according to M3 specs. |
|
|
163
|
-
| topOffset | `number` | `0` | Grabber top position offset. |
|
|
164
|
-
|
|
165
|
-
### `BlurTint`
|
|
166
|
-
|
|
167
|
-
Blur tint that is mapped into native values in iOS.
|
|
168
|
-
|
|
169
|
-
```tsx
|
|
170
|
-
<TrueSheet blurTint="dark">
|
|
171
|
-
// ...
|
|
172
|
-
</TrueSheet>
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
| Value |
|
|
176
|
-
| - |
|
|
177
|
-
| `"light"` |
|
|
178
|
-
| `"dark"` |
|
|
179
|
-
| `"default"` |
|
|
180
|
-
| `"extraLight"` |
|
|
181
|
-
| `"regular"` |
|
|
182
|
-
| `"prominent"` |
|
|
183
|
-
| `"systemUltraThinMaterial"` |
|
|
184
|
-
| `"systemThinMaterial"` |
|
|
185
|
-
| `"systemMaterial"` |
|
|
186
|
-
| `"systemThickMaterial"` |
|
|
187
|
-
| `"systemChromeMaterial"` |
|
|
188
|
-
| `"systemUltraThinMaterialLight"` |
|
|
189
|
-
| `"systemThinMaterialLight"` |
|
|
190
|
-
| `"systemMaterialLight"` |
|
|
191
|
-
| `"systemThickMaterialLight"` |
|
|
192
|
-
| `"systemChromeMaterialLight"` |
|
|
193
|
-
| `"systemUltraThinMaterialDark"` |
|
|
194
|
-
| `"systemThinMaterialDark"` |
|
|
195
|
-
| `"systemMaterialDark"` |
|
|
196
|
-
| `"systemThickMaterialDark"` |
|
|
197
|
-
| `"systemChromeMaterialDark"` |
|
|
198
|
-
|
|
199
|
-
### `SizeInfo`
|
|
200
|
-
|
|
201
|
-
`Object` that comes with some events.
|
|
202
|
-
|
|
203
|
-
```tsx
|
|
204
|
-
{
|
|
205
|
-
index: 1,
|
|
206
|
-
value: 69
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
| Property | Type | Description |
|
|
211
|
-
| - | - | - |
|
|
212
|
-
| index | `number` | The size index from the provided sizes. See `sizes` prop. |
|
|
213
|
-
| value | `number` | The actual height value of the size. |
|
|
214
|
-
|
|
215
|
-
## Testing
|
|
216
|
-
|
|
217
|
-
When using `jest`, simply mock the entire package.
|
|
218
|
-
|
|
219
|
-
```tsx
|
|
220
|
-
jest.mock('@lodev09/react-native-true-sheet')
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
## Troubleshooting
|
|
224
|
-
|
|
225
|
-
### Handling `ScrollView` on **Android**
|
|
226
|
-
|
|
227
|
-
On Android, `nestedScrollEnabled` needs to be enabled so that scrolling works when the sheet is expanded to its `maxHeight`.
|
|
228
|
-
|
|
229
|
-
```tsx
|
|
230
|
-
<TrueSheet ref={sheet}>
|
|
231
|
-
<ScrollView nestedScrollEnabled>
|
|
232
|
-
// ...
|
|
233
|
-
</ScrollView>
|
|
234
|
-
</TrueSheet>
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### Using `react-native-gesture-handler` on **Android**
|
|
238
|
-
|
|
239
|
-
On Android, RNGH does not work by default because modals are not located under React Native Root view in native hierarchy. To fix that, components need to be wrapped with `GestureHandlerRootView`.
|
|
240
|
-
|
|
241
|
-
Example:
|
|
242
|
-
```tsx
|
|
243
|
-
import { GestureHandlerRootView } from 'react-native-gesture-handler'
|
|
244
|
-
```
|
|
245
|
-
```tsx
|
|
246
|
-
return (
|
|
247
|
-
<TrueSheet ref={sheet}>
|
|
248
|
-
<GestureHandlerRootView>
|
|
249
|
-
// ...
|
|
250
|
-
</GestureHandlerRootView>
|
|
251
|
-
</TrueSheet>
|
|
252
|
-
)
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
### Integrating `@react-navigation/native` on iOS
|
|
256
|
-
|
|
257
|
-
On iOS, navigating to a [React Navigation](https://reactnavigation.org) screen from within the Sheet can cause issues. To resolve this, dismiss the sheet before navigating!
|
|
258
|
-
|
|
259
|
-
Example:
|
|
260
|
-
```tsx
|
|
261
|
-
const sheet = useRef<TrueSheet>(null)
|
|
262
|
-
|
|
263
|
-
const navigate = async () => {
|
|
264
|
-
await sheet.current?.dismiss() // wait for the sheet to dismiss ✅
|
|
265
|
-
navigation.navigate('SomeScreen') // navigate to the screen 🎉
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
return (
|
|
269
|
-
<TrueSheet ref={sheet}>
|
|
270
|
-
<Button onPress={navigate} title="Navigate to SomeScreen" />
|
|
271
|
-
// ...
|
|
272
|
-
</TrueSheet>
|
|
273
|
-
)
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
### Weird layout render
|
|
277
|
-
|
|
278
|
-
The sheet does not have control over how React Native renders components and may lead to rendering issues. To resolve this, try to minimize the use of `flex=1` in your content styles. Instead, use fixed `height` or employ `flexGrow`, `flexBasis`, etc., to manage your layout requirements.
|
|
279
|
-
|
|
280
65
|
## v1 Roadmap
|
|
281
66
|
|
|
282
67
|
- [ ] Inline sheet
|
|
@@ -18,14 +18,15 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val
|
|
|
18
18
|
private var keyboardManager = KeyboardManager(reactContext)
|
|
19
19
|
|
|
20
20
|
var maxScreenHeight: Int = 0
|
|
21
|
+
var contentHeight: Int = 0
|
|
22
|
+
var footerHeight: Int = 0
|
|
21
23
|
var maxSheetHeight: Int? = null
|
|
22
24
|
|
|
23
|
-
var contentView: ViewGroup? = null
|
|
24
25
|
var footerView: ViewGroup? = null
|
|
25
26
|
|
|
26
27
|
var sizes: Array<Any> = arrayOf("medium", "large")
|
|
27
28
|
|
|
28
|
-
var sheetView: ViewGroup
|
|
29
|
+
private var sheetView: ViewGroup
|
|
29
30
|
|
|
30
31
|
init {
|
|
31
32
|
setContentView(rootSheetView)
|
|
@@ -39,6 +40,9 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val
|
|
|
39
40
|
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
|
|
40
41
|
)
|
|
41
42
|
}
|
|
43
|
+
|
|
44
|
+
// Update the usable sheet height
|
|
45
|
+
maxScreenHeight = Utils.screenHeight(reactContext)
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
fun show(sizeIndex: Int) {
|
|
@@ -52,6 +56,12 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val
|
|
|
52
56
|
}
|
|
53
57
|
}
|
|
54
58
|
|
|
59
|
+
fun positionFooter() {
|
|
60
|
+
footerView?.apply {
|
|
61
|
+
y = (maxScreenHeight - sheetView.top - footerHeight).toFloat()
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
55
65
|
/**
|
|
56
66
|
* Set the state based on the given size index.
|
|
57
67
|
*/
|
|
@@ -62,7 +72,7 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val
|
|
|
62
72
|
/**
|
|
63
73
|
* Get the height value based on the size config value.
|
|
64
74
|
*/
|
|
65
|
-
private fun getSizeHeight(size: Any
|
|
75
|
+
private fun getSizeHeight(size: Any): Int {
|
|
66
76
|
val height =
|
|
67
77
|
when (size) {
|
|
68
78
|
is Double -> Utils.toPixel(size)
|
|
@@ -71,7 +81,7 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val
|
|
|
71
81
|
|
|
72
82
|
is String -> {
|
|
73
83
|
when (size) {
|
|
74
|
-
"auto" -> contentHeight
|
|
84
|
+
"auto" -> contentHeight + footerHeight
|
|
75
85
|
|
|
76
86
|
"large" -> maxScreenHeight
|
|
77
87
|
|
|
@@ -102,7 +112,10 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val
|
|
|
102
112
|
else -> (maxScreenHeight * 0.5).toInt()
|
|
103
113
|
}
|
|
104
114
|
|
|
105
|
-
return
|
|
115
|
+
return when (maxSheetHeight) {
|
|
116
|
+
null -> height
|
|
117
|
+
else -> minOf(height, maxSheetHeight ?: maxScreenHeight)
|
|
118
|
+
}
|
|
106
119
|
}
|
|
107
120
|
|
|
108
121
|
/**
|
|
@@ -144,9 +157,7 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val
|
|
|
144
157
|
else -> Utils.screenHeight(reactContext)
|
|
145
158
|
}
|
|
146
159
|
|
|
147
|
-
|
|
148
|
-
y = (maxScreenHeight - (sheetView.top ?: 0) - height).toFloat()
|
|
149
|
-
}
|
|
160
|
+
positionFooter()
|
|
150
161
|
}
|
|
151
162
|
})
|
|
152
163
|
}
|
|
@@ -162,14 +173,6 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val
|
|
|
162
173
|
* Configure the sheet based on size preferences.
|
|
163
174
|
*/
|
|
164
175
|
fun configure() {
|
|
165
|
-
// Update the usable sheet height
|
|
166
|
-
maxScreenHeight = Utils.screenHeight(reactContext)
|
|
167
|
-
|
|
168
|
-
var contentHeight = 0
|
|
169
|
-
|
|
170
|
-
contentView?.let { contentHeight = it.height }
|
|
171
|
-
footerView?.let { contentHeight += it.height }
|
|
172
|
-
|
|
173
176
|
// Configure sheet sizes
|
|
174
177
|
behavior.apply {
|
|
175
178
|
skipCollapsed = false
|
|
@@ -180,23 +183,22 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val
|
|
|
180
183
|
|
|
181
184
|
when (sizes.size) {
|
|
182
185
|
1 -> {
|
|
183
|
-
maxHeight = getSizeHeight(sizes[0]
|
|
184
|
-
peekHeight = maxHeight
|
|
186
|
+
maxHeight = getSizeHeight(sizes[0])
|
|
185
187
|
skipCollapsed = true
|
|
186
188
|
}
|
|
187
189
|
|
|
188
190
|
2 -> {
|
|
189
|
-
|
|
190
|
-
maxHeight = getSizeHeight(sizes[1]
|
|
191
|
+
setPeekHeight(getSizeHeight(sizes[0]), isShowing)
|
|
192
|
+
maxHeight = getSizeHeight(sizes[1])
|
|
191
193
|
}
|
|
192
194
|
|
|
193
195
|
3 -> {
|
|
194
196
|
// Enables half expanded
|
|
195
197
|
isFitToContents = false
|
|
196
198
|
|
|
197
|
-
|
|
198
|
-
halfExpandedRatio = getSizeHeight(sizes[1]
|
|
199
|
-
maxHeight = getSizeHeight(sizes[2]
|
|
199
|
+
setPeekHeight(getSizeHeight(sizes[0]), isShowing)
|
|
200
|
+
halfExpandedRatio = getSizeHeight(sizes[1]).toFloat() / maxScreenHeight.toFloat()
|
|
201
|
+
maxHeight = getSizeHeight(sizes[2])
|
|
200
202
|
}
|
|
201
203
|
}
|
|
202
204
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
package com.lodev09.truesheet
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
-
import android.util.Log
|
|
5
4
|
import android.view.View
|
|
6
5
|
import android.view.ViewGroup
|
|
7
6
|
import android.view.ViewStructure
|
|
@@ -53,11 +52,6 @@ class TrueSheetView(context: Context) :
|
|
|
53
52
|
*/
|
|
54
53
|
private val rootSheetView: RootSheetView
|
|
55
54
|
|
|
56
|
-
/**
|
|
57
|
-
* 1st child of the container view.
|
|
58
|
-
*/
|
|
59
|
-
private var contentView: ViewGroup? = null
|
|
60
|
-
|
|
61
55
|
/**
|
|
62
56
|
* 2nd child of the container view.
|
|
63
57
|
*/
|
|
@@ -79,10 +73,8 @@ class TrueSheetView(context: Context) :
|
|
|
79
73
|
registerKeyboardManager()
|
|
80
74
|
|
|
81
75
|
// Initialize footer y
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
y = (sheetDialog.maxScreenHeight - sheetView.top - height).toFloat()
|
|
85
|
-
}
|
|
76
|
+
UiThreadUtil.runOnUiThread {
|
|
77
|
+
positionFooter()
|
|
86
78
|
}
|
|
87
79
|
|
|
88
80
|
presentPromise?.let { promise ->
|
|
@@ -96,8 +88,8 @@ class TrueSheetView(context: Context) :
|
|
|
96
88
|
|
|
97
89
|
// Setup listener when the dialog has been dismissed.
|
|
98
90
|
setOnDismissListener {
|
|
99
|
-
Log.d(TAG, "dismissed")
|
|
100
91
|
unregisterKeyboardManager()
|
|
92
|
+
|
|
101
93
|
dismissPromise?.let { promise ->
|
|
102
94
|
promise()
|
|
103
95
|
dismissPromise = null
|
|
@@ -106,19 +98,17 @@ class TrueSheetView(context: Context) :
|
|
|
106
98
|
// dispatch onDismiss event
|
|
107
99
|
eventDispatcher?.dispatchEvent(DismissEvent(surfaceId, id))
|
|
108
100
|
}
|
|
109
|
-
}
|
|
110
101
|
|
|
111
|
-
|
|
112
|
-
sheetDialog.apply {
|
|
102
|
+
// Configure sheet behavior events
|
|
113
103
|
behavior.addBottomSheetCallback(
|
|
114
104
|
object : BottomSheetBehavior.BottomSheetCallback() {
|
|
115
105
|
override fun onSlide(sheetView: View, slideOffset: Float) {
|
|
116
106
|
footerView?.let {
|
|
117
|
-
val y = (maxScreenHeight - sheetView.top -
|
|
107
|
+
val y = (maxScreenHeight - sheetView.top - footerHeight).toFloat()
|
|
118
108
|
if (slideOffset >= 0) {
|
|
119
109
|
it.y = y
|
|
120
110
|
} else {
|
|
121
|
-
it.y = y -
|
|
111
|
+
it.y = y - footerHeight * slideOffset
|
|
122
112
|
}
|
|
123
113
|
}
|
|
124
114
|
}
|
|
@@ -168,10 +158,8 @@ class TrueSheetView(context: Context) :
|
|
|
168
158
|
|
|
169
159
|
(child as ViewGroup).let {
|
|
170
160
|
// Container View's first child is the Content View
|
|
171
|
-
contentView = it.getChildAt(0) as ViewGroup
|
|
172
161
|
footerView = it.getChildAt(1) as ViewGroup
|
|
173
162
|
|
|
174
|
-
sheetDialog.contentView = contentView
|
|
175
163
|
sheetDialog.footerView = footerView
|
|
176
164
|
|
|
177
165
|
// rootView's first child is the Container View
|
|
@@ -221,9 +209,26 @@ class TrueSheetView(context: Context) :
|
|
|
221
209
|
sheetDialog.dismiss()
|
|
222
210
|
}
|
|
223
211
|
|
|
212
|
+
private fun configureIfShowing() {
|
|
213
|
+
if (sheetDialog.isShowing) {
|
|
214
|
+
sheetDialog.configure()
|
|
215
|
+
sheetDialog.positionFooter()
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
224
219
|
fun setMaxHeight(height: Int) {
|
|
225
220
|
sheetDialog.maxSheetHeight = height
|
|
226
|
-
|
|
221
|
+
configureIfShowing()
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
fun setContentHeight(height: Int) {
|
|
225
|
+
sheetDialog.contentHeight = height
|
|
226
|
+
configureIfShowing()
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
fun setFooterHeight(height: Int) {
|
|
230
|
+
sheetDialog.footerHeight = height
|
|
231
|
+
configureIfShowing()
|
|
227
232
|
}
|
|
228
233
|
|
|
229
234
|
fun setDismissible(dismissible: Boolean) {
|
|
@@ -233,7 +238,7 @@ class TrueSheetView(context: Context) :
|
|
|
233
238
|
|
|
234
239
|
fun setSizes(newSizes: Array<Any>) {
|
|
235
240
|
sheetDialog.sizes = newSizes
|
|
236
|
-
|
|
241
|
+
configureIfShowing()
|
|
237
242
|
}
|
|
238
243
|
|
|
239
244
|
/**
|
|
@@ -39,6 +39,16 @@ class TrueSheetViewManager : ViewGroupManager<TrueSheetView>() {
|
|
|
39
39
|
view.setDismissible(dismissible)
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
@ReactProp(name = "contentHeight")
|
|
43
|
+
fun setContentHeight(view: TrueSheetView, height: Double) {
|
|
44
|
+
view.setContentHeight(Utils.toPixel(height))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@ReactProp(name = "footerHeight")
|
|
48
|
+
fun setFooterHeight(view: TrueSheetView, height: Double) {
|
|
49
|
+
view.setFooterHeight(Utils.toPixel(height))
|
|
50
|
+
}
|
|
51
|
+
|
|
42
52
|
@ReactProp(name = "sizes")
|
|
43
53
|
fun setSizes(view: TrueSheetView, sizes: ReadableArray) {
|
|
44
54
|
val result = ArrayList<Any>()
|
package/ios/TrueSheetView.swift
CHANGED
|
@@ -10,8 +10,6 @@
|
|
|
10
10
|
class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
|
|
11
11
|
// MARK: - React properties
|
|
12
12
|
|
|
13
|
-
var maxHeight: CGFloat?
|
|
14
|
-
|
|
15
13
|
// Events
|
|
16
14
|
@objc var onDismiss: RCTDirectEventBlock?
|
|
17
15
|
@objc var onPresent: RCTDirectEventBlock?
|
|
@@ -40,24 +38,6 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
|
|
|
40
38
|
|
|
41
39
|
private var rctScrollView: RCTScrollView?
|
|
42
40
|
|
|
43
|
-
// Content height minus the footer height for `auto` layout
|
|
44
|
-
private var contentHeight: CGFloat {
|
|
45
|
-
guard let contentView else { return 0 }
|
|
46
|
-
|
|
47
|
-
var height = contentView.frame.height
|
|
48
|
-
|
|
49
|
-
// Add footer view's height
|
|
50
|
-
if let footerContent = footerView?.subviews.first {
|
|
51
|
-
height += footerContent.bounds.height
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Exclude bottom safe area for consistency with a Scrollable content
|
|
55
|
-
let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow })
|
|
56
|
-
let bottomInset = window?.safeAreaInsets.bottom ?? 0
|
|
57
|
-
|
|
58
|
-
return height - bottomInset
|
|
59
|
-
}
|
|
60
|
-
|
|
61
41
|
// MARK: - Setup
|
|
62
42
|
|
|
63
43
|
init(with bridge: RCTBridge) {
|
|
@@ -127,7 +107,7 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
|
|
|
127
107
|
}
|
|
128
108
|
|
|
129
109
|
// Update content containers
|
|
130
|
-
|
|
110
|
+
setupScrollable()
|
|
131
111
|
}
|
|
132
112
|
}
|
|
133
113
|
|
|
@@ -158,14 +138,10 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
|
|
|
158
138
|
|
|
159
139
|
let size = CGSize(width: width, height: containerView.bounds.height)
|
|
160
140
|
bridge.uiManager.setSize(size, for: containerView)
|
|
161
|
-
|
|
162
|
-
if let footerView {
|
|
163
|
-
bridge.uiManager.setSize(size, for: footerView)
|
|
164
|
-
}
|
|
165
141
|
}
|
|
166
142
|
|
|
167
143
|
func viewControllerWillAppear() {
|
|
168
|
-
|
|
144
|
+
setupScrollable()
|
|
169
145
|
}
|
|
170
146
|
|
|
171
147
|
func viewControllerDidDismiss() {
|
|
@@ -199,6 +175,35 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
|
|
|
199
175
|
configureSheetIfPresented()
|
|
200
176
|
}
|
|
201
177
|
|
|
178
|
+
@objc
|
|
179
|
+
func setContentHeight(_ height: NSNumber) {
|
|
180
|
+
// Exclude bottom safe area for consistency with a Scrollable content
|
|
181
|
+
let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow })
|
|
182
|
+
let bottomInset = window?.safeAreaInsets.bottom ?? 0
|
|
183
|
+
|
|
184
|
+
viewController.contentHeight = CGFloat(height.floatValue) - bottomInset
|
|
185
|
+
configureSheetIfPresented()
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@objc
|
|
189
|
+
func setFooterHeight(_ height: NSNumber) {
|
|
190
|
+
guard let footerView, let footerViewHeightConstraint else {
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
viewController.footerHeight = CGFloat(height.floatValue)
|
|
195
|
+
|
|
196
|
+
if footerView.subviews.first != nil {
|
|
197
|
+
containerView?.bringSubviewToFront(footerView)
|
|
198
|
+
footerViewHeightConstraint.constant = viewController.footerHeight
|
|
199
|
+
} else {
|
|
200
|
+
containerView?.sendSubviewToBack(footerView)
|
|
201
|
+
footerViewHeightConstraint.constant = 0
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
configureSheetIfPresented()
|
|
205
|
+
}
|
|
206
|
+
|
|
202
207
|
@objc
|
|
203
208
|
func setSizes(_ sizes: [Any]) {
|
|
204
209
|
viewController.sizes = Array(sizes.prefix(3))
|
|
@@ -224,7 +229,7 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
|
|
|
224
229
|
|
|
225
230
|
viewController.cornerRadius = cornerRadius
|
|
226
231
|
if #available(iOS 15.0, *) {
|
|
227
|
-
|
|
232
|
+
configureIfPresented { sheet in
|
|
228
233
|
sheet.preferredCornerRadius = viewController.cornerRadius
|
|
229
234
|
}
|
|
230
235
|
}
|
|
@@ -234,7 +239,7 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
|
|
|
234
239
|
func setGrabber(_ visible: Bool) {
|
|
235
240
|
viewController.grabber = visible
|
|
236
241
|
if #available(iOS 15.0, *) {
|
|
237
|
-
|
|
242
|
+
configureIfPresented { sheet in
|
|
238
243
|
sheet.prefersGrabberVisible = visible
|
|
239
244
|
}
|
|
240
245
|
}
|
|
@@ -258,7 +263,7 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
|
|
|
258
263
|
|
|
259
264
|
/// Use to customize some properties of the Sheet
|
|
260
265
|
@available(iOS 15.0, *)
|
|
261
|
-
func
|
|
266
|
+
func configureIfPresented(completion: (UISheetPresentationController) -> Void) {
|
|
262
267
|
guard isPresented, let sheet = viewController.sheetPresentationController else {
|
|
263
268
|
return
|
|
264
269
|
}
|
|
@@ -269,11 +274,11 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
|
|
|
269
274
|
/// Full reconfiguration of the Sheet
|
|
270
275
|
func configureSheetIfPresented() {
|
|
271
276
|
if isPresented {
|
|
272
|
-
viewController.configureSheet(at: activeIndex ?? 0,
|
|
277
|
+
viewController.configureSheet(at: activeIndex ?? 0, nil)
|
|
273
278
|
}
|
|
274
279
|
}
|
|
275
280
|
|
|
276
|
-
func
|
|
281
|
+
func setupScrollable() {
|
|
277
282
|
guard let contentView, let containerView else { return }
|
|
278
283
|
|
|
279
284
|
// Add constraints to fix weirdness and support ScrollView
|
|
@@ -281,16 +286,6 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
|
|
|
281
286
|
contentView.pinTo(view: containerView, constraints: nil)
|
|
282
287
|
rctScrollView.pinTo(view: contentView, constraints: nil)
|
|
283
288
|
}
|
|
284
|
-
|
|
285
|
-
if let footerView, let footerViewHeightConstraint {
|
|
286
|
-
if let footerContent = footerView.subviews.first {
|
|
287
|
-
containerView.bringSubviewToFront(footerView)
|
|
288
|
-
footerViewHeightConstraint.constant = footerContent.bounds.height
|
|
289
|
-
} else {
|
|
290
|
-
containerView.sendSubviewToBack(footerView)
|
|
291
|
-
footerViewHeightConstraint.constant = 0
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
289
|
}
|
|
295
290
|
|
|
296
291
|
func dismiss(promise: Promise) {
|
|
@@ -299,9 +294,6 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
|
|
|
299
294
|
return
|
|
300
295
|
}
|
|
301
296
|
|
|
302
|
-
// Dismiss the keyboard
|
|
303
|
-
contentView?.endEditing(true)
|
|
304
|
-
|
|
305
297
|
viewController.dismiss(animated: true) {
|
|
306
298
|
promise.resolve(nil)
|
|
307
299
|
}
|
|
@@ -320,7 +312,7 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate {
|
|
|
320
312
|
return
|
|
321
313
|
}
|
|
322
314
|
|
|
323
|
-
viewController.configureSheet(at: index
|
|
315
|
+
viewController.configureSheet(at: index) { sizeInfo in
|
|
324
316
|
// Trigger onSizeChange event when size is changed while presenting
|
|
325
317
|
if self.isPresented {
|
|
326
318
|
self.viewControllerSheetDidChangeSize(sizeInfo)
|