@onekeyfe/react-native-native-sheet 3.0.125

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 OneKey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,22 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "NativeSheet"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => '16.0' }
14
+ s.source = { :git => "https://github.com/OneKeyHQ/app-modules.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = [
17
+ "ios/**/*.{swift}",
18
+ "ios/**/*.{m,mm}",
19
+ ]
20
+ s.swift_version = '5.0'
21
+ install_modules_dependencies(s)
22
+ end
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # @onekeyfe/react-native-native-sheet
2
+
3
+ A native iOS and Android bottom-sheet host for arbitrary React Native content.
4
+ The platform owns presentation, stacking, gestures, and transitions; React
5
+ Native owns the sheet body. No action-row or icon schema is required.
6
+
7
+ ## Behavior
8
+
9
+ - iOS uses `UISheetPresentationController` with a fixed custom detent.
10
+ - Android uses `BottomSheetDialog` and `BottomSheetBehavior`.
11
+ - Multiple sheets form a native presentation stack. Closing a lower sheet also
12
+ closes its descendants in top-to-bottom order.
13
+ - `height` is captured when a sheet opens. Async child updates reflow inside the
14
+ existing surface and do not resize the outer sheet.
15
+ - `NativeSheetSecurityProvider` force-dismisses descendant sheets without an
16
+ animation while the app lock is active, so native windows never cover the
17
+ security surface.
18
+
19
+ ## Usage
20
+
21
+ ```tsx
22
+ import {
23
+ NativeSheet,
24
+ NativeSheetSecurityProvider,
25
+ } from '@onekeyfe/react-native-native-sheet';
26
+
27
+ function AppRoot({ isLocked, children }) {
28
+ return (
29
+ <NativeSheetSecurityProvider blocked={isLocked}>
30
+ {children}
31
+ </NativeSheetSecurityProvider>
32
+ );
33
+ }
34
+
35
+ function RenameSheet({ open, onClose }) {
36
+ return (
37
+ <NativeSheet
38
+ open={open}
39
+ height={360}
40
+ backgroundColor="#fff"
41
+ onDismiss={onClose}
42
+ >
43
+ {/* Any React Native subtree: form fields, lists, QR codes, WebViews, etc. */}
44
+ <RenameForm />
45
+ </NativeSheet>
46
+ );
47
+ }
48
+ ```
49
+
50
+ The caller owns `open` and must set it to `false` from `onDismiss`. Put scrolling
51
+ or asynchronously loaded content inside a fixed-height layout so it can update
52
+ without changing the native detent.
53
+
54
+ ## License
55
+
56
+ MIT
@@ -0,0 +1,82 @@
1
+ buildscript {
2
+ ext.getExtOrDefault = {name ->
3
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['NativeSheet_' + name]
4
+ }
5
+
6
+ repositories {
7
+ google()
8
+ mavenCentral()
9
+ }
10
+
11
+ dependencies {
12
+ classpath "com.android.tools.build:gradle:8.7.2"
13
+ // noinspection DifferentKotlinGradleVersion
14
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
15
+ }
16
+ }
17
+
18
+ apply plugin: "com.android.library"
19
+ apply plugin: "kotlin-android"
20
+ apply plugin: "com.facebook.react"
21
+
22
+ def getExtOrIntegerDefault(name) {
23
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["NativeSheet_" + name]).toInteger()
24
+ }
25
+
26
+ android {
27
+ namespace "com.onekey.nativesheet"
28
+
29
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
30
+
31
+ defaultConfig {
32
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
33
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
34
+ }
35
+
36
+ buildFeatures {
37
+ buildConfig true
38
+ }
39
+
40
+ buildTypes {
41
+ release {
42
+ minifyEnabled false
43
+ }
44
+ }
45
+
46
+ lintOptions {
47
+ disable "GradleCompatible"
48
+ }
49
+
50
+ compileOptions {
51
+ sourceCompatibility JavaVersion.VERSION_1_8
52
+ targetCompatibility JavaVersion.VERSION_1_8
53
+ }
54
+
55
+ sourceSets {
56
+ main {
57
+ java.srcDirs += [
58
+ "generated/java",
59
+ "generated/jni"
60
+ ]
61
+ }
62
+ }
63
+ }
64
+
65
+ repositories {
66
+ mavenCentral()
67
+ google()
68
+ }
69
+
70
+ def kotlin_version = getExtOrDefault("kotlinVersion")
71
+
72
+ dependencies {
73
+ implementation "com.facebook.react:react-android"
74
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
75
+ implementation "com.google.android.material:material:1.14.0-alpha05"
76
+ }
77
+
78
+ react {
79
+ jsRootDir = file("../src/")
80
+ libraryName = "RNCNativeSheet"
81
+ codegenJavaPackageName = "com.onekey.nativesheet"
82
+ }
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,37 @@
1
+ package com.onekey.nativesheet
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.WritableMap
5
+ import com.facebook.react.uimanager.events.Event
6
+
7
+ internal class NativeSheetDismissEvent(
8
+ surfaceId: Int,
9
+ viewTag: Int,
10
+ private val reason: String,
11
+ ) : Event<NativeSheetDismissEvent>(surfaceId, viewTag) {
12
+ override fun getEventName(): String = EVENT_NAME
13
+
14
+ override fun getEventData(): WritableMap = Arguments.createMap().apply {
15
+ putString("reason", reason)
16
+ }
17
+
18
+ companion object {
19
+ const val EVENT_NAME = "topDismiss"
20
+ }
21
+ }
22
+
23
+ internal class NativeSheetPresentedEvent(
24
+ surfaceId: Int,
25
+ viewTag: Int,
26
+ private val height: Double,
27
+ ) : Event<NativeSheetPresentedEvent>(surfaceId, viewTag) {
28
+ override fun getEventName(): String = EVENT_NAME
29
+
30
+ override fun getEventData(): WritableMap = Arguments.createMap().apply {
31
+ putDouble("height", height)
32
+ }
33
+
34
+ companion object {
35
+ const val EVENT_NAME = "topPresented"
36
+ }
37
+ }
@@ -0,0 +1,16 @@
1
+ package com.onekey.nativesheet
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+ class NativeSheetPackage : ReactPackage {
9
+ override fun createViewManagers(
10
+ reactContext: ReactApplicationContext,
11
+ ): List<ViewManager<*, *>> = listOf(NativeSheetViewManager(reactContext))
12
+
13
+ override fun createNativeModules(
14
+ reactContext: ReactApplicationContext,
15
+ ): List<NativeModule> = emptyList()
16
+ }
@@ -0,0 +1,354 @@
1
+ package com.onekey.nativesheet
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.graphics.Color
5
+ import android.graphics.drawable.GradientDrawable
6
+ import android.view.Gravity
7
+ import android.view.KeyEvent
8
+ import android.view.MotionEvent
9
+ import android.view.View
10
+ import android.view.ViewGroup
11
+ import android.widget.FrameLayout
12
+ import com.facebook.react.common.annotations.UnstableReactNativeAPI
13
+ import com.facebook.react.config.ReactFeatureFlags
14
+ import com.facebook.react.uimanager.JSPointerDispatcher
15
+ import com.facebook.react.uimanager.JSTouchDispatcher
16
+ import com.facebook.react.uimanager.RootView
17
+ import com.facebook.react.uimanager.ThemedReactContext
18
+ import com.facebook.react.uimanager.events.EventDispatcher
19
+ import com.google.android.material.bottomsheet.BottomSheetBehavior
20
+ import com.google.android.material.bottomsheet.BottomSheetDialog
21
+ import java.lang.ref.WeakReference
22
+ import kotlin.math.roundToInt
23
+
24
+ internal object NativeSheetStack {
25
+ private val active = mutableListOf<WeakReference<NativeSheetView>>()
26
+
27
+ private fun compact() {
28
+ active.removeAll { it.get() == null }
29
+ }
30
+
31
+ fun present(view: NativeSheetView) {
32
+ compact()
33
+ if (active.any { it.get() === view }) return
34
+ view.showDialogInternal()
35
+ if (view.hasDialog()) active.add(WeakReference(view))
36
+ }
37
+
38
+ fun dismiss(view: NativeSheetView, reason: String, animated: Boolean) {
39
+ compact()
40
+ val index = active.indexOfFirst { it.get() === view }
41
+ if (index < 0) {
42
+ view.dismissDialogInternal(reason, animated)
43
+ return
44
+ }
45
+
46
+ val targets = active.subList(index, active.size).mapNotNull { it.get() }.asReversed()
47
+ active.subList(index, active.size).clear()
48
+ targets.forEach { target ->
49
+ val targetReason = if (target === view) reason else if (reason == "security") reason else "system"
50
+ target.dismissDialogInternal(targetReason, animated)
51
+ }
52
+ }
53
+
54
+ fun didDismiss(view: NativeSheetView) {
55
+ active.removeAll { it.get() == null || it.get() === view }
56
+ }
57
+ }
58
+
59
+ /**
60
+ * React children mounted in a Dialog are outside their original ReactRootView.
61
+ * This root mirrors React Native Modal's touch dispatch boundary so Pressability,
62
+ * pointer events, and native gesture cancellation keep working after reparenting.
63
+ */
64
+ private class NativeSheetDialogRootView(
65
+ private val reactContext: ThemedReactContext,
66
+ ) : FrameLayout(reactContext), RootView {
67
+ var eventDispatcher: EventDispatcher? = null
68
+
69
+ private val touchDispatcher = JSTouchDispatcher(this)
70
+ private val pointerDispatcher = if (ReactFeatureFlags.dispatchPointerEvents) {
71
+ JSPointerDispatcher(this)
72
+ } else {
73
+ null
74
+ }
75
+
76
+ override fun handleException(t: Throwable) {
77
+ reactContext.reactApplicationContext.handleException(RuntimeException(t))
78
+ }
79
+
80
+ override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
81
+ eventDispatcher?.let { dispatcher ->
82
+ touchDispatcher.handleTouchEvent(event, dispatcher, reactContext)
83
+ pointerDispatcher?.handleMotionEvent(event, dispatcher, true)
84
+ }
85
+ return super.onInterceptTouchEvent(event)
86
+ }
87
+
88
+ @SuppressLint("ClickableViewAccessibility")
89
+ override fun onTouchEvent(event: MotionEvent): Boolean {
90
+ eventDispatcher?.let { dispatcher ->
91
+ touchDispatcher.handleTouchEvent(event, dispatcher, reactContext)
92
+ pointerDispatcher?.handleMotionEvent(event, dispatcher, false)
93
+ }
94
+ super.onTouchEvent(event)
95
+ return true
96
+ }
97
+
98
+ override fun onInterceptHoverEvent(event: MotionEvent): Boolean {
99
+ eventDispatcher?.let { pointerDispatcher?.handleMotionEvent(event, it, true) }
100
+ return super.onInterceptHoverEvent(event)
101
+ }
102
+
103
+ override fun onHoverEvent(event: MotionEvent): Boolean {
104
+ eventDispatcher?.let { pointerDispatcher?.handleMotionEvent(event, it, false) }
105
+ return super.onHoverEvent(event)
106
+ }
107
+
108
+ @OptIn(UnstableReactNativeAPI::class)
109
+ override fun onChildStartedNativeGesture(childView: View?, ev: MotionEvent) {
110
+ eventDispatcher?.let { dispatcher ->
111
+ touchDispatcher.onChildStartedNativeGesture(ev, dispatcher, reactContext)
112
+ pointerDispatcher?.onChildStartedNativeGesture(childView, ev, dispatcher)
113
+ }
114
+ }
115
+
116
+ override fun onChildEndedNativeGesture(childView: View, ev: MotionEvent) {
117
+ eventDispatcher?.let { touchDispatcher.onChildEndedNativeGesture(ev, it) }
118
+ pointerDispatcher?.onChildEndedNativeGesture()
119
+ }
120
+
121
+ override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) = Unit
122
+ }
123
+
124
+ class NativeSheetView(
125
+ private val reactContext: ThemedReactContext,
126
+ ) : FrameLayout(reactContext) {
127
+ var open: Boolean = false
128
+ var sheetHeight: Double = 0.0
129
+ var securityBlocked: Boolean = false
130
+ var dismissOnPanDown: Boolean = true
131
+ var dismissOnBackdropPress: Boolean = false
132
+ var dismissOnBackPress: Boolean = true
133
+ var showHandle: Boolean = true
134
+ var cornerRadius: Double = 32.0
135
+ var dimAmount: Double = 0.4
136
+ var sheetBackgroundColor: Int? = null
137
+ var onDismiss: ((String) -> Unit)? = null
138
+ var onPresented: ((Double) -> Unit)? = null
139
+ var eventDispatcher: EventDispatcher? = null
140
+ set(value) {
141
+ field = value
142
+ dialogContent?.eventDispatcher = value
143
+ }
144
+
145
+ private var contentChild: View? = null
146
+ private var dialog: BottomSheetDialog? = null
147
+ private var dialogContent: NativeSheetDialogRootView? = null
148
+ private var committedOpen = false
149
+ private var dismissedForCurrentOpen = false
150
+ private var pendingDismissReason = "system"
151
+ private var dismissNotified = false
152
+
153
+ init {
154
+ visibility = INVISIBLE
155
+ importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
156
+ }
157
+
158
+ fun addReactChild(child: View, index: Int) {
159
+ contentChild?.let { existing ->
160
+ if (existing !== child) removeReactChild(existing)
161
+ }
162
+ contentChild = child
163
+ attachChild(dialogContent ?: this, child, index)
164
+ }
165
+
166
+ fun getReactChildCount(): Int = if (contentChild == null) 0 else 1
167
+
168
+ fun getReactChildAt(index: Int): View? = if (index == 0) contentChild else null
169
+
170
+ fun removeReactChild(child: View) {
171
+ if (contentChild !== child) return
172
+ (child.parent as? ViewGroup)?.removeView(child)
173
+ contentChild = null
174
+ }
175
+
176
+ fun removeAllReactChildren() {
177
+ contentChild?.let(::removeReactChild)
178
+ }
179
+
180
+ fun commitConfiguration() {
181
+ val openChanged = committedOpen != open
182
+ committedOpen = open
183
+ if (!open) dismissedForCurrentOpen = false
184
+
185
+ if (securityBlocked) {
186
+ dismissedForCurrentOpen = open
187
+ NativeSheetStack.dismiss(this, "security", false)
188
+ return
189
+ }
190
+
191
+ if (!open) {
192
+ NativeSheetStack.dismiss(this, "programmatic", true)
193
+ } else if ((openChanged || dialog == null) && !dismissedForCurrentOpen) {
194
+ NativeSheetStack.present(this)
195
+ }
196
+ }
197
+
198
+ fun hasDialog(): Boolean = dialog != null
199
+
200
+ internal fun showDialogInternal() {
201
+ if (dialog != null || securityBlocked || !open) return
202
+ val activity = reactContext.currentActivity ?: return
203
+ if (activity.isFinishing || activity.isDestroyed) return
204
+
205
+ val heightPx = dpToPx(sheetHeight.coerceAtLeast(1.0))
206
+ val content = NativeSheetDialogRootView(reactContext).apply {
207
+ eventDispatcher = this@NativeSheetView.eventDispatcher
208
+ layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, heightPx)
209
+ background = GradientDrawable().apply {
210
+ shape = GradientDrawable.RECTANGLE
211
+ setColor(sheetBackgroundColor ?: Color.WHITE)
212
+ cornerRadii = floatArrayOf(
213
+ dpToPx(this@NativeSheetView.cornerRadius).toFloat(),
214
+ dpToPx(this@NativeSheetView.cornerRadius).toFloat(),
215
+ dpToPx(this@NativeSheetView.cornerRadius).toFloat(),
216
+ dpToPx(this@NativeSheetView.cornerRadius).toFloat(),
217
+ 0f,
218
+ 0f,
219
+ 0f,
220
+ 0f,
221
+ )
222
+ }
223
+ clipToOutline = true
224
+ }
225
+ contentChild?.let { attachChild(content, it, 0) }
226
+ if (showHandle) content.addView(createHandle(), createHandleLayoutParams())
227
+
228
+ pendingDismissReason = "system"
229
+ dismissNotified = false
230
+ val nextDialog = BottomSheetDialog(activity).apply {
231
+ setContentView(content)
232
+ setCancelable(dismissOnPanDown || dismissOnBackPress || dismissOnBackdropPress)
233
+ setCanceledOnTouchOutside(dismissOnBackdropPress)
234
+ dismissWithAnimation = true
235
+ setOnKeyListener { _, keyCode, event ->
236
+ if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
237
+ if (!dismissOnBackPress) return@setOnKeyListener true
238
+ pendingDismissReason = "back"
239
+ }
240
+ false
241
+ }
242
+ setOnCancelListener {
243
+ if (pendingDismissReason == "system") pendingDismissReason = "backdrop"
244
+ }
245
+ setOnDismissListener {
246
+ handleDialogDismissed()
247
+ }
248
+ setOnShowListener {
249
+ configureShownDialog(this, heightPx)
250
+ }
251
+ }
252
+
253
+ dialogContent = content
254
+ dialog = nextDialog
255
+ nextDialog.show()
256
+ }
257
+
258
+ private fun configureShownDialog(sheetDialog: BottomSheetDialog, heightPx: Int) {
259
+ sheetDialog.window?.apply {
260
+ setDimAmount(dimAmount.coerceIn(0.0, 1.0).toFloat())
261
+ setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
262
+ }
263
+ val bottomSheet = sheetDialog.findViewById<FrameLayout>(
264
+ com.google.android.material.R.id.design_bottom_sheet,
265
+ ) ?: return
266
+ bottomSheet.layoutParams = bottomSheet.layoutParams.apply { height = heightPx }
267
+ bottomSheet.setBackgroundColor(Color.TRANSPARENT)
268
+ sheetDialog.behavior.apply {
269
+ peekHeight = heightPx
270
+ isFitToContents = true
271
+ isHideable = dismissOnPanDown
272
+ isDraggable = dismissOnPanDown
273
+ skipCollapsed = true
274
+ state = BottomSheetBehavior.STATE_EXPANDED
275
+ addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
276
+ override fun onStateChanged(bottomSheet: View, newState: Int) {
277
+ if (newState == BottomSheetBehavior.STATE_HIDDEN) {
278
+ pendingDismissReason = "pan"
279
+ }
280
+ }
281
+
282
+ override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit
283
+ })
284
+ }
285
+ onPresented?.invoke(pxToDp(heightPx))
286
+ }
287
+
288
+ internal fun dismissDialogInternal(reason: String, animated: Boolean) {
289
+ val currentDialog = dialog ?: return
290
+ pendingDismissReason = reason
291
+ if (animated) {
292
+ currentDialog.dismissWithAnimation = true
293
+ } else {
294
+ currentDialog.dismissWithAnimation = false
295
+ }
296
+ currentDialog.dismiss()
297
+ }
298
+
299
+ private fun handleDialogDismissed() {
300
+ val child = contentChild
301
+ dialogContent = null
302
+ dialog = null
303
+ child?.let { attachChild(this, it, 0) }
304
+ NativeSheetStack.didDismiss(this)
305
+ if (!dismissNotified) {
306
+ dismissNotified = true
307
+ dismissedForCurrentOpen = committedOpen
308
+ onDismiss?.invoke(pendingDismissReason)
309
+ }
310
+ }
311
+
312
+ fun onDropViewInstance() {
313
+ NativeSheetStack.dismiss(this, "system", false)
314
+ onDismiss = null
315
+ onPresented = null
316
+ eventDispatcher = null
317
+ }
318
+
319
+ private fun createHandle(): View = View(reactContext).apply {
320
+ background = GradientDrawable().apply {
321
+ shape = GradientDrawable.RECTANGLE
322
+ cornerRadius = dpToPx(2.5).toFloat()
323
+ setColor(0x33000000)
324
+ }
325
+ importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_NO
326
+ }
327
+
328
+ private fun createHandleLayoutParams() = LayoutParams(dpToPx(36.0), dpToPx(5.0)).apply {
329
+ gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
330
+ topMargin = dpToPx(8.0)
331
+ }
332
+
333
+ private fun attachChild(parent: ViewGroup, child: View, index: Int) {
334
+ if (child.parent === parent) return
335
+ (child.parent as? ViewGroup)?.removeView(child)
336
+ val params = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
337
+ parent.addView(child, index.coerceIn(0, parent.childCount), params)
338
+ }
339
+
340
+ private fun dpToPx(value: Double): Int =
341
+ (value * resources.displayMetrics.density).roundToInt()
342
+
343
+ private fun pxToDp(value: Int): Double = value / resources.displayMetrics.density.toDouble()
344
+
345
+ override fun onDetachedFromWindow() {
346
+ super.onDetachedFromWindow()
347
+ if (!isAttachedToWindow) NativeSheetStack.dismiss(this, "system", false)
348
+ }
349
+
350
+ override fun onAttachedToWindow() {
351
+ super.onAttachedToWindow()
352
+ commitConfiguration()
353
+ }
354
+ }