@lodev09/react-native-true-sheet 3.0.0-beta.7 → 3.0.0-beta.8
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/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +51 -49
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +10 -18
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +76 -20
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetHeaderView.kt +38 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetHeaderViewManager.kt +21 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +1 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +81 -147
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +303 -409
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +2 -4
- package/android/src/main/java/com/lodev09/truesheet/core/RNScreensFragmentObserver.kt +116 -0
- package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +33 -5
- package/ios/TrueSheetContainerView.h +20 -2
- package/ios/TrueSheetContainerView.mm +61 -14
- package/ios/TrueSheetContentView.h +4 -2
- package/ios/TrueSheetContentView.mm +29 -72
- package/ios/TrueSheetFooterView.mm +2 -2
- package/ios/TrueSheetHeaderView.h +29 -0
- package/ios/TrueSheetHeaderView.mm +60 -0
- package/ios/TrueSheetView.mm +178 -232
- package/ios/TrueSheetViewController.h +1 -2
- package/ios/TrueSheetViewController.mm +126 -236
- package/ios/utils/LayoutUtil.h +2 -1
- package/ios/utils/LayoutUtil.mm +14 -1
- package/lib/module/TrueSheet.js +10 -2
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/fabric/TrueSheetHeaderViewNativeComponent.ts +8 -0
- package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.types.d.ts +9 -9
- package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
- package/lib/typescript/src/fabric/TrueSheetHeaderViewNativeComponent.d.ts +6 -0
- package/lib/typescript/src/fabric/TrueSheetHeaderViewNativeComponent.d.ts.map +1 -0
- package/package.json +4 -1
- package/src/TrueSheet.tsx +10 -0
- package/src/TrueSheet.types.ts +10 -11
- package/src/fabric/TrueSheetHeaderViewNativeComponent.ts +8 -0
|
@@ -25,8 +25,8 @@ import com.lodev09.truesheet.events.WillDismissEvent
|
|
|
25
25
|
import com.lodev09.truesheet.events.WillPresentEvent
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
* Main TrueSheet host view.
|
|
29
|
-
*
|
|
28
|
+
* Main TrueSheet host view that manages the sheet dialog and dispatches events to JavaScript.
|
|
29
|
+
* This view is hidden (GONE) and delegates all rendering to TrueSheetViewController in a dialog window.
|
|
30
30
|
*/
|
|
31
31
|
@SuppressLint("ViewConstructor")
|
|
32
32
|
class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
@@ -35,14 +35,8 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
35
35
|
TrueSheetViewControllerDelegate,
|
|
36
36
|
TrueSheetContainerViewDelegate {
|
|
37
37
|
|
|
38
|
-
/**
|
|
39
|
-
* The TrueSheetViewController instance that acts as both root view and controller
|
|
40
|
-
*/
|
|
41
38
|
private val viewController: TrueSheetViewController = TrueSheetViewController(reactContext)
|
|
42
39
|
|
|
43
|
-
/**
|
|
44
|
-
* Gets the container view (first child of view controller)
|
|
45
|
-
*/
|
|
46
40
|
private val containerView: TrueSheetContainerView?
|
|
47
41
|
get() = viewController.getChildAt(0) as? TrueSheetContainerView
|
|
48
42
|
|
|
@@ -52,24 +46,27 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
52
46
|
var initialDetentAnimated: Boolean = true
|
|
53
47
|
|
|
54
48
|
var stateWrapper: StateWrapper? = null
|
|
49
|
+
set(value) {
|
|
50
|
+
// Immediately update state with screen width during first state update
|
|
51
|
+
// This ensures we have initial width for content layout before presenting
|
|
52
|
+
if (field == null && value != null) {
|
|
53
|
+
updateState(viewController.screenWidth, 0)
|
|
54
|
+
}
|
|
55
|
+
field = value
|
|
56
|
+
}
|
|
55
57
|
|
|
56
58
|
// Track last dimensions to avoid unnecessary state updates
|
|
57
59
|
private var lastContainerWidth: Int = 0
|
|
58
60
|
private var lastContainerHeight: Int = 0
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
*/
|
|
63
|
-
private var hasHandledInitialPresentation = false
|
|
62
|
+
// Flag to prevent multiple pending sheet updates
|
|
63
|
+
private var isSheetUpdatePending: Boolean = false
|
|
64
64
|
|
|
65
65
|
init {
|
|
66
66
|
reactContext.addLifecycleEventListener(this)
|
|
67
|
-
|
|
68
|
-
// Set delegates
|
|
69
67
|
viewController.delegate = this
|
|
70
68
|
|
|
71
|
-
// Hide the host view
|
|
72
|
-
// The actual content is shown in a dialog window
|
|
69
|
+
// Hide the host view - actual content is rendered in the dialog window
|
|
73
70
|
visibility = GONE
|
|
74
71
|
}
|
|
75
72
|
|
|
@@ -89,98 +86,66 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
89
86
|
|
|
90
87
|
override fun setId(id: Int) {
|
|
91
88
|
super.setId(id)
|
|
92
|
-
|
|
93
89
|
viewController.id = id
|
|
94
90
|
TrueSheetModule.registerView(this, id)
|
|
95
91
|
}
|
|
96
92
|
|
|
97
93
|
override fun onDetachedFromWindow() {
|
|
98
94
|
super.onDetachedFromWindow()
|
|
99
|
-
onDropInstance()
|
|
100
95
|
|
|
101
|
-
|
|
96
|
+
// Don't unregister if we have active modals - we need the view for recovery
|
|
97
|
+
if (viewController.hasActiveModals()) {
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
onDropInstance()
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* if the changed properties can be applied directly to the Dialog or require the recreation of a
|
|
108
|
-
* new Dialog.
|
|
105
|
+
* Called by the manager after all properties are set.
|
|
106
|
+
* Reconfigures the sheet if it's currently presented.
|
|
109
107
|
*/
|
|
110
|
-
fun
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
// Create dialog if not created yet
|
|
116
|
-
if (!viewController.isPresented) {
|
|
117
|
-
viewController.createDialog()
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
post {
|
|
121
|
-
present(initialDetentIndex, initialDetentAnimated) { }
|
|
122
|
-
}
|
|
123
|
-
} else if (viewController.isPresented) {
|
|
124
|
-
viewController.setupSheetDetents()
|
|
108
|
+
fun finalizeUpdates() {
|
|
109
|
+
if (viewController.isPresented) {
|
|
110
|
+
viewController.setupBackground()
|
|
111
|
+
viewController.setupGrabber()
|
|
112
|
+
updateSheetIfNeeded()
|
|
125
113
|
viewController.setStateForDetentIndex(viewController.currentDetentIndex)
|
|
126
|
-
viewController.positionFooter()
|
|
127
114
|
}
|
|
128
115
|
}
|
|
129
116
|
|
|
130
117
|
// ==================== View Management ====================
|
|
131
118
|
|
|
132
119
|
override fun addView(child: View?, index: Int) {
|
|
133
|
-
// Add the child to our ViewController
|
|
134
|
-
// This is the TrueSheetContainerView
|
|
135
120
|
viewController.addView(child, index)
|
|
136
121
|
|
|
137
|
-
// Create dialog and dispatch mount event when TrueSheetContainerView is added
|
|
138
122
|
if (child is TrueSheetContainerView) {
|
|
139
|
-
// Set up container delegate to listen for content size changes
|
|
140
123
|
child.delegate = this
|
|
124
|
+
viewController.createDialog()
|
|
141
125
|
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
viewController.contentHeight = contentHeight
|
|
126
|
+
// Present at initial detent after layout pass when content height is available
|
|
127
|
+
if (initialDetentIndex >= 0) {
|
|
128
|
+
post { present(initialDetentIndex, initialDetentAnimated) { } }
|
|
146
129
|
}
|
|
147
130
|
|
|
148
|
-
// Create the dialog now that the container is mounted
|
|
149
|
-
viewController.createDialog()
|
|
150
|
-
|
|
151
131
|
val surfaceId = UIManagerHelper.getSurfaceId(this)
|
|
152
|
-
eventDispatcher?.dispatchEvent(
|
|
153
|
-
MountEvent(surfaceId, id)
|
|
154
|
-
)
|
|
132
|
+
eventDispatcher?.dispatchEvent(MountEvent(surfaceId, id))
|
|
155
133
|
}
|
|
156
134
|
}
|
|
157
135
|
|
|
158
136
|
override fun getChildCount(): Int = viewController.childCount
|
|
159
137
|
override fun getChildAt(index: Int): View? = viewController.getChildAt(index)
|
|
160
138
|
|
|
161
|
-
override fun removeView(child: View?) {
|
|
162
|
-
if (child != null) {
|
|
163
|
-
// Clean up container delegate
|
|
164
|
-
if (child is TrueSheetContainerView) {
|
|
165
|
-
child.delegate = null
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
viewController.removeView(child)
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
139
|
override fun removeViewAt(index: Int) {
|
|
173
140
|
val child = getChildAt(index)
|
|
141
|
+
if (child is TrueSheetContainerView) {
|
|
142
|
+
child.delegate = null
|
|
143
|
+
}
|
|
174
144
|
viewController.removeView(child)
|
|
175
145
|
}
|
|
176
146
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
// Those will be handled by the mHostView which lives in the dialog
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Explicitly override this to prevent accessibility events being passed down to children
|
|
183
|
-
// Those will be handled by the mHostView which lives in the dialog
|
|
147
|
+
// Accessibility events are handled by the dialog's host view
|
|
148
|
+
override fun addChildrenForAccessibility(outChildren: ArrayList<View>) {}
|
|
184
149
|
override fun dispatchPopulateAccessibilityEvent(event: AccessibilityEvent): Boolean = false
|
|
185
150
|
|
|
186
151
|
fun onDropInstance() {
|
|
@@ -194,7 +159,7 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
194
159
|
}
|
|
195
160
|
|
|
196
161
|
override fun onHostResume() {
|
|
197
|
-
|
|
162
|
+
finalizeUpdates()
|
|
198
163
|
}
|
|
199
164
|
|
|
200
165
|
override fun onHostPause() {
|
|
@@ -208,71 +173,55 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
208
173
|
|
|
209
174
|
override fun viewControllerWillPresent(index: Int, position: Float) {
|
|
210
175
|
val surfaceId = UIManagerHelper.getSurfaceId(this)
|
|
211
|
-
eventDispatcher?.dispatchEvent(
|
|
212
|
-
WillPresentEvent(surfaceId, id, index, position)
|
|
213
|
-
)
|
|
176
|
+
eventDispatcher?.dispatchEvent(WillPresentEvent(surfaceId, id, index, position))
|
|
214
177
|
}
|
|
215
178
|
|
|
216
179
|
override fun viewControllerDidPresent(index: Int, position: Float) {
|
|
217
180
|
val surfaceId = UIManagerHelper.getSurfaceId(this)
|
|
218
|
-
eventDispatcher?.dispatchEvent(
|
|
219
|
-
DidPresentEvent(surfaceId, id, index, position)
|
|
220
|
-
)
|
|
181
|
+
eventDispatcher?.dispatchEvent(DidPresentEvent(surfaceId, id, index, position))
|
|
221
182
|
|
|
222
|
-
//
|
|
183
|
+
// Enable touch event dispatching to React Native
|
|
223
184
|
viewController.eventDispatcher = eventDispatcher
|
|
185
|
+
containerView?.footerView?.eventDispatcher = eventDispatcher
|
|
224
186
|
}
|
|
225
187
|
|
|
226
188
|
override fun viewControllerWillDismiss() {
|
|
227
189
|
val surfaceId = UIManagerHelper.getSurfaceId(this)
|
|
228
|
-
eventDispatcher?.dispatchEvent(
|
|
229
|
-
WillDismissEvent(surfaceId, id)
|
|
230
|
-
)
|
|
190
|
+
eventDispatcher?.dispatchEvent(WillDismissEvent(surfaceId, id))
|
|
231
191
|
|
|
232
|
-
//
|
|
192
|
+
// Disable touch event dispatching
|
|
233
193
|
viewController.eventDispatcher = null
|
|
194
|
+
containerView?.footerView?.eventDispatcher = null
|
|
234
195
|
}
|
|
235
196
|
|
|
236
197
|
override fun viewControllerDidDismiss() {
|
|
237
198
|
val surfaceId = UIManagerHelper.getSurfaceId(this)
|
|
238
|
-
eventDispatcher?.dispatchEvent(
|
|
239
|
-
DidDismissEvent(surfaceId, id)
|
|
240
|
-
)
|
|
199
|
+
eventDispatcher?.dispatchEvent(DidDismissEvent(surfaceId, id))
|
|
241
200
|
}
|
|
242
201
|
|
|
243
202
|
override fun viewControllerDidChangeDetent(index: Int, position: Float) {
|
|
244
203
|
val surfaceId = UIManagerHelper.getSurfaceId(this)
|
|
245
|
-
eventDispatcher?.dispatchEvent(
|
|
246
|
-
DetentChangeEvent(surfaceId, id, index, position)
|
|
247
|
-
)
|
|
204
|
+
eventDispatcher?.dispatchEvent(DetentChangeEvent(surfaceId, id, index, position))
|
|
248
205
|
}
|
|
249
206
|
|
|
250
207
|
override fun viewControllerDidDragBegin(index: Int, position: Float) {
|
|
251
208
|
val surfaceId = UIManagerHelper.getSurfaceId(this)
|
|
252
|
-
eventDispatcher?.dispatchEvent(
|
|
253
|
-
DragBeginEvent(surfaceId, id, index, position)
|
|
254
|
-
)
|
|
209
|
+
eventDispatcher?.dispatchEvent(DragBeginEvent(surfaceId, id, index, position))
|
|
255
210
|
}
|
|
256
211
|
|
|
257
212
|
override fun viewControllerDidDragChange(index: Int, position: Float) {
|
|
258
213
|
val surfaceId = UIManagerHelper.getSurfaceId(this)
|
|
259
|
-
eventDispatcher?.dispatchEvent(
|
|
260
|
-
DragChangeEvent(surfaceId, id, index, position)
|
|
261
|
-
)
|
|
214
|
+
eventDispatcher?.dispatchEvent(DragChangeEvent(surfaceId, id, index, position))
|
|
262
215
|
}
|
|
263
216
|
|
|
264
217
|
override fun viewControllerDidDragEnd(index: Int, position: Float) {
|
|
265
218
|
val surfaceId = UIManagerHelper.getSurfaceId(this)
|
|
266
|
-
eventDispatcher?.dispatchEvent(
|
|
267
|
-
DragEndEvent(surfaceId, id, index, position)
|
|
268
|
-
)
|
|
219
|
+
eventDispatcher?.dispatchEvent(DragEndEvent(surfaceId, id, index, position))
|
|
269
220
|
}
|
|
270
221
|
|
|
271
222
|
override fun viewControllerDidChangePosition(index: Int, position: Float, transitioning: Boolean) {
|
|
272
223
|
val surfaceId = UIManagerHelper.getSurfaceId(this)
|
|
273
|
-
eventDispatcher?.dispatchEvent(
|
|
274
|
-
PositionChangeEvent(surfaceId, id, index, position, transitioning)
|
|
275
|
-
)
|
|
224
|
+
eventDispatcher?.dispatchEvent(PositionChangeEvent(surfaceId, id, index, position, transitioning))
|
|
276
225
|
}
|
|
277
226
|
|
|
278
227
|
override fun viewControllerDidChangeSize(width: Int, height: Int) {
|
|
@@ -303,15 +252,13 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
303
252
|
}
|
|
304
253
|
|
|
305
254
|
fun setCornerRadius(radius: Float) {
|
|
306
|
-
if (viewController.
|
|
307
|
-
viewController.
|
|
308
|
-
viewController.setupBackground()
|
|
255
|
+
if (viewController.sheetCornerRadius == radius) return
|
|
256
|
+
viewController.sheetCornerRadius = radius
|
|
309
257
|
}
|
|
310
258
|
|
|
311
259
|
fun setSheetBackgroundColor(color: Int) {
|
|
312
260
|
if (viewController.sheetBackgroundColor == color) return
|
|
313
261
|
viewController.sheetBackgroundColor = color
|
|
314
|
-
viewController.setupBackground()
|
|
315
262
|
}
|
|
316
263
|
|
|
317
264
|
fun setSoftInputMode(mode: Int) {
|
|
@@ -322,7 +269,9 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
322
269
|
viewController.dismissible = dismissible
|
|
323
270
|
}
|
|
324
271
|
|
|
325
|
-
fun setGrabber(grabber: Boolean) {
|
|
272
|
+
fun setGrabber(grabber: Boolean) {
|
|
273
|
+
viewController.grabber = grabber
|
|
274
|
+
}
|
|
326
275
|
|
|
327
276
|
fun setDetents(newDetents: MutableList<Double>) {
|
|
328
277
|
viewController.detents = newDetents
|
|
@@ -337,77 +286,62 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
337
286
|
// ==================== State Management ====================
|
|
338
287
|
|
|
339
288
|
/**
|
|
340
|
-
*
|
|
341
|
-
* Called when the dialog size changes.
|
|
289
|
+
* Updates the Fabric state with container dimensions for Yoga layout.
|
|
342
290
|
*/
|
|
343
291
|
fun updateState(width: Int, height: Int) {
|
|
344
|
-
|
|
345
|
-
if (width == lastContainerWidth && height == lastContainerHeight) {
|
|
346
|
-
return
|
|
347
|
-
}
|
|
292
|
+
if (width == lastContainerWidth && height == lastContainerHeight) return
|
|
348
293
|
|
|
349
|
-
// Store new dimensions
|
|
350
294
|
lastContainerWidth = width
|
|
351
295
|
lastContainerHeight = height
|
|
352
296
|
|
|
353
297
|
val sw = stateWrapper ?: return
|
|
354
|
-
|
|
355
|
-
val realWidth = width.toFloat().pxToDp()
|
|
356
|
-
val realHeight = height.toFloat().pxToDp()
|
|
357
|
-
|
|
358
298
|
val newStateData = WritableNativeMap()
|
|
359
|
-
newStateData.putDouble("containerWidth",
|
|
360
|
-
newStateData.putDouble("containerHeight",
|
|
299
|
+
newStateData.putDouble("containerWidth", width.toFloat().pxToDp().toDouble())
|
|
300
|
+
newStateData.putDouble("containerHeight", height.toFloat().pxToDp().toDouble())
|
|
361
301
|
sw.updateState(newStateData)
|
|
362
302
|
}
|
|
363
303
|
|
|
364
|
-
/**
|
|
365
|
-
* Presents the sheet at the given detent index.
|
|
366
|
-
*
|
|
367
|
-
* @param detentIndex The detent index to present at
|
|
368
|
-
* @param animated Whether to animate the presentation
|
|
369
|
-
* @param promiseCallback Callback invoked when presentation completes
|
|
370
|
-
*/
|
|
371
304
|
@UiThread
|
|
372
305
|
fun present(detentIndex: Int, animated: Boolean = true, promiseCallback: () -> Unit) {
|
|
373
306
|
viewController.presentPromise = promiseCallback
|
|
374
307
|
viewController.present(detentIndex, animated)
|
|
375
308
|
}
|
|
376
309
|
|
|
377
|
-
/**
|
|
378
|
-
* Dismisses the sheet.
|
|
379
|
-
*
|
|
380
|
-
* @param promiseCallback Callback invoked when dismissal completes
|
|
381
|
-
*/
|
|
382
310
|
@UiThread
|
|
383
311
|
fun dismiss(promiseCallback: () -> Unit) {
|
|
384
312
|
viewController.dismissPromise = promiseCallback
|
|
385
313
|
viewController.dismiss()
|
|
386
314
|
}
|
|
387
315
|
|
|
388
|
-
|
|
316
|
+
/**
|
|
317
|
+
* Debounced sheet update to handle rapid content/header size changes.
|
|
318
|
+
* Uses post to ensure all layout passes complete before reconfiguring.
|
|
319
|
+
*/
|
|
320
|
+
fun updateSheetIfNeeded() {
|
|
321
|
+
if (!viewController.isPresented || isSheetUpdatePending) return
|
|
389
322
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
323
|
+
isSheetUpdatePending = true
|
|
324
|
+
viewController.post {
|
|
325
|
+
isSheetUpdatePending = false
|
|
326
|
+
viewController.setupSheetDetents()
|
|
327
|
+
viewController.positionFooter()
|
|
328
|
+
}
|
|
329
|
+
}
|
|
394
330
|
|
|
395
|
-
|
|
331
|
+
// ==================== TrueSheetContainerViewDelegate Implementation ====================
|
|
396
332
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
viewController.setupSheetDetents()
|
|
333
|
+
override fun containerViewContentDidChangeSize(width: Int, height: Int) {
|
|
334
|
+
updateSheetIfNeeded()
|
|
335
|
+
}
|
|
401
336
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
337
|
+
override fun containerViewHeaderDidChangeSize(width: Int, height: Int) {
|
|
338
|
+
updateSheetIfNeeded()
|
|
339
|
+
// Update state for scroll view behavior to work correctly with header
|
|
340
|
+
val sheetHeight = TrueSheetViewController.getEffectiveSheetHeight(viewController.height, height)
|
|
341
|
+
updateState(viewController.width, sheetHeight)
|
|
407
342
|
}
|
|
408
343
|
|
|
409
344
|
override fun containerViewFooterDidChangeSize(width: Int, height: Int) {
|
|
410
|
-
// Reposition footer when its size changes
|
|
411
345
|
if (viewController.isPresented) {
|
|
412
346
|
viewController.positionFooter()
|
|
413
347
|
}
|