@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.
Files changed (34) hide show
  1. package/README.md +9 -224
  2. package/android/src/main/java/com/lodev09/truesheet/TrueSheetDialog.kt +25 -23
  3. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +25 -20
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +10 -0
  5. package/ios/TrueSheetView.swift +37 -45
  6. package/ios/TrueSheetViewController.swift +6 -2
  7. package/ios/TrueSheetViewManager.m +4 -0
  8. package/lib/commonjs/TrueSheet.js +79 -26
  9. package/lib/commonjs/TrueSheet.js.map +1 -1
  10. package/lib/commonjs/TrueSheetFooter.js +20 -0
  11. package/lib/commonjs/TrueSheetFooter.js.map +1 -0
  12. package/lib/commonjs/__mocks__/index.js +3 -0
  13. package/lib/commonjs/__mocks__/index.js.map +1 -1
  14. package/lib/module/TrueSheet.js +78 -25
  15. package/lib/module/TrueSheet.js.map +1 -1
  16. package/lib/module/TrueSheetFooter.js +12 -0
  17. package/lib/module/TrueSheetFooter.js.map +1 -0
  18. package/lib/module/__mocks__/index.js +3 -0
  19. package/lib/module/__mocks__/index.js.map +1 -1
  20. package/lib/typescript/docs/docusaurus.config.d.ts +4 -0
  21. package/lib/typescript/docs/docusaurus.config.d.ts.map +1 -0
  22. package/lib/typescript/docs/sidebars.d.ts +4 -0
  23. package/lib/typescript/docs/sidebars.d.ts.map +1 -0
  24. package/lib/typescript/src/TrueSheet.d.ts +27 -3
  25. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  26. package/lib/typescript/src/TrueSheetFooter.d.ts +8 -0
  27. package/lib/typescript/src/TrueSheetFooter.d.ts.map +1 -0
  28. package/lib/typescript/src/types.d.ts +15 -0
  29. package/lib/typescript/src/types.d.ts.map +1 -1
  30. package/package.json +8 -8
  31. package/src/TrueSheet.tsx +102 -37
  32. package/src/TrueSheetFooter.tsx +18 -0
  33. package/src/__mocks__/index.js +4 -0
  34. package/src/types.ts +15 -0
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  ![GitHub Release](https://img.shields.io/github/v/release/lodev09/react-native-true-sheet)
5
5
  ![NPM Downloads](https://img.shields.io/npm/dw/%40lodev09%2Freact-native-true-sheet)
6
6
 
7
- The true native bottom sheet 💩
7
+ The true native bottom sheet experience for your React Native Apps. 💩
8
8
 
9
9
  ![Preview](preview.gif)
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
- * ✅ Works with Expo by default.
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, contentHeight: Int): Int {
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 minOf(height, maxSheetHeight ?: maxScreenHeight)
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
- footerView?.apply {
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], contentHeight)
184
- peekHeight = maxHeight
186
+ maxHeight = getSizeHeight(sizes[0])
185
187
  skipCollapsed = true
186
188
  }
187
189
 
188
190
  2 -> {
189
- peekHeight = getSizeHeight(sizes[0], contentHeight)
190
- maxHeight = getSizeHeight(sizes[1], contentHeight)
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
- peekHeight = getSizeHeight(sizes[0], contentHeight)
198
- halfExpandedRatio = getSizeHeight(sizes[1], contentHeight).toFloat() / maxScreenHeight.toFloat()
199
- maxHeight = getSizeHeight(sizes[2], contentHeight)
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
- footerView?.apply {
83
- UiThreadUtil.runOnUiThread {
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
- // Configure sheet behavior events
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 - it.height).toFloat()
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 - it.height * slideOffset
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
- sheetDialog.configure()
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
- sheetDialog.configure()
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>()
@@ -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
- setupContent()
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
- setupContent()
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
- configureSheetIfPresented { sheet in
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
- configureSheetIfPresented { sheet in
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 configureSheetIfPresented(completion: (UISheetPresentationController) -> Void) {
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, with: contentHeight, nil)
277
+ viewController.configureSheet(at: activeIndex ?? 0, nil)
273
278
  }
274
279
  }
275
280
 
276
- func setupContent() {
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, with: contentHeight) { sizeInfo in
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)