diff --git a/android/src/main/java/com/lodev09/truesheet/TrueSheetDialog.kt b/android/src/main/java/com/lodev09/truesheet/TrueSheetDialog.kt index f4b38fc..d92c354 100644 --- a/android/src/main/java/com/lodev09/truesheet/TrueSheetDialog.kt +++ b/android/src/main/java/com/lodev09/truesheet/TrueSheetDialog.kt @@ -2,11 +2,13 @@ package com.lodev09.truesheet import android.annotation.SuppressLint import android.graphics.Color +import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ShapeDrawable import android.graphics.drawable.shapes.RoundRectShape import android.view.View import android.view.ViewGroup import android.view.WindowManager +import android.widget.FrameLayout import com.facebook.react.uimanager.ThemedReactContext import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog @@ -23,6 +25,9 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val private var keyboardManager = KeyboardManager(reactContext) private var windowAnimation: Int = 0 + // Custom dimming overlay view + private var dimmingView: View? = null + // First child of the rootSheetView private val containerView: ViewGroup? get() = if (rootSheetView.childCount > 0) { @@ -39,6 +44,10 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val * Set to `false` to allow interaction with the background components. */ var dimmed = true + set(value) { + field = value + updateDimmingView() + } /** * The size index that the sheet should start to dim the background. @@ -46,6 +55,17 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val */ var dimmedIndex = 0 + /** + * The alpha value of the dimmed background. + * + * @default 0.75f + */ + var dimmedAlpha = 0.75f + set(value) { + field = value + updateDimmingView() + } + /** * The maximum window height */ @@ -93,12 +113,58 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val window?.apply { // Store current windowAnimation value to toggle later windowAnimation = attributes.windowAnimations + + // Disable default dimming + clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) } + // Create and add our custom dimming view + createDimmingView() + // Update the usable sheet height maxScreenHeight = Utils.screenHeight(reactContext, edgeToEdge) } + /** + * Create and add a custom dimming view to the dialog + */ + private fun createDimmingView() { + // Get the content view of the dialog + val decorView = window?.decorView as? ViewGroup + val contentView = decorView?.findViewById(android.R.id.content) + + // Create a new view for dimming + dimmingView = View(context).apply { + layoutParams = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT + ) + background = ColorDrawable(Color.BLACK) + alpha = if (dimmed) dimmedAlpha else 0f + visibility = if (dimmed) View.VISIBLE else View.GONE + elevation = -1f // Make sure it's below other content + + // Add the view at index 0 so it's behind everything else + contentView?.addView(this, 0) + + // Make sure it's behind the sheet + bringToFront() + } + + // Make sure the sheet is on top of the dimming view + rootSheetView.bringToFront() + } + + /** + * Update the dimming view based on current settings + */ + private fun updateDimmingView() { + dimmingView?.apply { + alpha = if (dimmed) dimmedAlpha else 0f + visibility = if (dimmed) View.VISIBLE else View.GONE + } + } + override fun getEdgeToEdgeEnabled(): Boolean = edgeToEdge || super.getEdgeToEdgeEnabled() override fun onStart() { @@ -114,6 +180,9 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION } } + + // Make sure dimming is applied + updateDimmingView() } /** @@ -150,11 +219,9 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val // Remove touch listener view.setOnTouchListener(null) - // Add the dimmed background - setFlags( - WindowManager.LayoutParams.FLAG_DIM_BEHIND, - WindowManager.LayoutParams.FLAG_DIM_BEHIND - ) + // Show dimming view with the specified alpha + dimmingView?.visibility = View.VISIBLE + dimmingView?.alpha = dimmedAlpha setCanceledOnTouchOutside(dismissible) } else { @@ -165,8 +232,8 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val false } - // Remove the dimmed background - clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) + // Hide our custom dimming view + dimmingView?.visibility = View.GONE setCanceledOnTouchOutside(false) } @@ -183,11 +250,12 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val * Present the sheet. */ fun present(sizeIndex: Int, animated: Boolean = true) { - setupDimmedBackground(sizeIndex) if (isShowing) { + setupDimmedBackground(sizeIndex) setStateForSizeIndex(sizeIndex) } else { configure() + setupDimmedBackground(sizeIndex) setStateForSizeIndex(sizeIndex) if (!animated) { @@ -196,6 +264,14 @@ class TrueSheetDialog(private val reactContext: ThemedReactContext, private val } show() + + // Ensure dimming is applied after showing + if (dimmed && sizeIndex >= dimmedIndex) { + dimmingView?.visibility = View.VISIBLE + dimmingView?.alpha = dimmedAlpha + } else { + dimmingView?.visibility = View.GONE + } } } diff --git a/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt b/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt index e946d96..c233c35 100644 --- a/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +++ b/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt @@ -307,7 +307,6 @@ class TrueSheetView(context: Context) : } currentSizeIndex = it.index - sheetDialog.setupDimmedBackground(it.index) // Dispatch onSizeChange event dispatchEvent(TrueSheetEvent.SIZE_CHANGE, sizeInfoData(it)) @@ -357,9 +356,12 @@ class TrueSheetView(context: Context) : if (sheetDialog.dimmed == dimmed) return sheetDialog.dimmed = dimmed - if (sheetDialog.isShowing) { - sheetDialog.setupDimmedBackground(currentSizeIndex) - } + } + + fun setDimmedAlpha(alpha: Float) { + if (sheetDialog.dimmedAlpha == alpha) return + + sheetDialog.dimmedAlpha = alpha } fun setDimmedIndex(index: Int) { diff --git a/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt b/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt index 0ed9b8a..edecc95 100644 --- a/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +++ b/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt @@ -67,6 +67,11 @@ class TrueSheetViewManager : ViewGroupManager() { view.setDimmed(dimmed) } + @ReactProp(name = "dimmedAlpha") + fun setDimmedAlpha(view: TrueSheetView, alpha: Float) { + view.setDimmedAlpha(alpha) + } + @ReactProp(name = "initialIndex") fun setInitialIndex(view: TrueSheetView, index: Int) { view.initialIndex = index diff --git a/ios/TrueSheetView.swift b/ios/TrueSheetView.swift index 45a39eb..38c42ae 100644 --- a/ios/TrueSheetView.swift +++ b/ios/TrueSheetView.swift @@ -8,6 +8,11 @@ @objc(TrueSheetView) class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate { + // MARK: - Static properties + + // Keep track of active sheet opacity values (stack) + private static var sheetOpacityStack: [CGFloat] = [] + // MARK: - React properties // MARK: - Events @@ -25,6 +30,7 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate { @objc var initialIndex: NSNumber = -1 @objc var initialIndexAnimated = true + @objc var dimmedAlpha: CGFloat = 0.75 // MARK: - Private properties @@ -195,6 +201,18 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate { } func viewControllerWillAppear() { + // Only apply dimming if the sheet has dimmed property set to true + if viewController.dimmed { + let opacity = 1 - dimmedAlpha + // Add this sheet's opacity to the stack and dim root view controller + TrueSheetView.sheetOpacityStack.append(opacity) + UIView.animate(withDuration: 0.3) { + if let rootViewController = UIApplication.shared.windows.first?.rootViewController { + rootViewController.view.alpha = opacity + } + } + } + guard let contentView, let scrollView, let containerView else { return } @@ -204,6 +222,26 @@ class TrueSheetView: UIView, RCTInvalidating, TrueSheetViewControllerDelegate { scrollView.pinTo(view: contentView, constraints: nil) } + func viewControllerWillDisappear() { + if viewController.isBeingDismissed && viewController.dimmed { + if !TrueSheetView.sheetOpacityStack.isEmpty { + TrueSheetView.sheetOpacityStack.removeLast() + } + + UIView.animate(withDuration: 0.3) { + if let rootViewController = UIApplication.shared.windows.first?.rootViewController { + // If there are still active sheets, apply the alpha of the topmost one + if let topmostOpacity = TrueSheetView.sheetOpacityStack.last { + rootViewController.view.alpha = topmostOpacity + } else { + // If no sheets are left, restore alpha to 1 + rootViewController.view.alpha = 1 + } + } + } + } + } + func viewControllerDidDismiss() { isPresented = false activeIndex = nil diff --git a/ios/TrueSheetViewController.swift b/ios/TrueSheetViewController.swift index a116617..aea33e6 100644 --- a/ios/TrueSheetViewController.swift +++ b/ios/TrueSheetViewController.swift @@ -20,6 +20,7 @@ protocol TrueSheetViewControllerDelegate: AnyObject { func viewControllerDidDismiss() func viewControllerDidChangeSize(_ sizeInfo: SizeInfo?) func viewControllerWillAppear() + func viewControllerWillDisappear() func viewControllerKeyboardWillShow(_ keyboardHeight: CGFloat) func viewControllerKeyboardWillHide() func viewControllerDidDrag(_ state: UIPanGestureRecognizer.State, _ height: CGFloat) @@ -144,6 +145,11 @@ class TrueSheetViewController: UIViewController, UISheetPresentationControllerDe delegate?.viewControllerWillAppear() } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + delegate?.viewControllerWillDisappear() + } + override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) delegate?.viewControllerDidDismiss() diff --git a/ios/TrueSheetViewManager.m b/ios/TrueSheetViewManager.m index d849bb5..086573e 100644 --- a/ios/TrueSheetViewManager.m +++ b/ios/TrueSheetViewManager.m @@ -45,6 +45,7 @@ @interface RCT_EXTERN_REMAP_MODULE (TrueSheetView, TrueSheetViewManager, RCTView RCT_EXPORT_VIEW_PROPERTY(dimmedIndex, NSNumber) RCT_EXPORT_VIEW_PROPERTY(initialIndex, NSNumber) RCT_EXPORT_VIEW_PROPERTY(initialIndexAnimated, BOOL) +RCT_EXPORT_VIEW_PROPERTY(dimmedAlpha, CGFloat) // Internal properties RCT_EXPORT_VIEW_PROPERTY(contentHeight, NSNumber) diff --git a/src/TrueSheet.tsx b/src/TrueSheet.tsx index a9c0eea..bde6c36 100644 --- a/src/TrueSheet.tsx +++ b/src/TrueSheet.tsx @@ -251,6 +251,7 @@ export class TrueSheet extends PureComponent { keyboardMode = 'resize', initialIndex, dimmedIndex, + dimmedAlpha = 0.75, grabberProps, blurTint, cornerRadius, @@ -276,6 +277,7 @@ export class TrueSheet extends PureComponent { grabber={grabber} dimmed={dimmed} dimmedIndex={dimmedIndex} + dimmedAlpha={dimmedAlpha} edgeToEdge={edgeToEdge} initialIndex={initialIndex} initialIndexAnimated={initialIndexAnimated} diff --git a/src/TrueSheet.types.ts b/src/TrueSheet.types.ts index bef5541..9083ea0 100644 --- a/src/TrueSheet.types.ts +++ b/src/TrueSheet.types.ts @@ -166,6 +166,13 @@ export interface TrueSheetProps extends ViewProps { */ dimmedIndex?: number + /** + * The alpha value of the dimmed background. + * + * @default 0.75 + */ + dimmedAlpha?: number + /** * Prevents interactive dismissal of the Sheet. *