about summary refs log tree commit diff
path: root/modules
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2025-02-21 10:59:08 -0800
committerGitHub <noreply@github.com>2025-02-21 10:59:08 -0800
commit5d30111b7832377637d0f3ebb533610375e4edb9 (patch)
tree3c8682368576475fae3b8212fc8c8828bea2205e /modules
parent798bf4782730d2d900ee30630bf065e2551bf089 (diff)
downloadvoidsky-5d30111b7832377637d0f3ebb533610375e4edb9.tar.zst
Get sheet padding working consistently (#7798)
* tweak height/padding of iOS

* tweak android ratio calculation

* add a bit of extra padding to full height iOS to account for the bit below the safe area
Diffstat (limited to 'modules')
-rw-r--r--modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/BottomSheetView.kt39
-rw-r--r--modules/bottom-sheet/src/BottomSheetNativeComponent.tsx98
2 files changed, 93 insertions, 44 deletions
diff --git a/modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/BottomSheetView.kt b/modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/BottomSheetView.kt
index 56b5b3f05..5df163b64 100644
--- a/modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/BottomSheetView.kt
+++ b/modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/BottomSheetView.kt
@@ -31,9 +31,13 @@ class BottomSheetView(
   private lateinit var dialogRootViewGroup: DialogRootViewGroup
   private var eventDispatcher: EventDispatcher? = null
 
-  private val screenHeight =
-    context.resources.displayMetrics.heightPixels
-      .toFloat()
+  private val rawScreenHeight = context.resources.displayMetrics.heightPixels.toFloat()
+  private val safeScreenHeight = (rawScreenHeight - getNavigationBarHeight()).toFloat()
+
+  private fun getNavigationBarHeight(): Int {
+      val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
+      return if (resourceId > 0) resources.getDimensionPixelSize(resourceId) else 0
+  }
 
   private val onAttemptDismiss by EventDispatcher()
   private val onSnapPointChange by EventDispatcher()
@@ -63,12 +67,12 @@ class BottomSheetView(
         }
     }
 
-  var maxHeight = this.screenHeight
+  var maxHeight = this.safeScreenHeight
     set(value) {
       val px = dpToPx(value)
       field =
-        if (px > this.screenHeight) {
-          this.screenHeight
+        if (px > this.safeScreenHeight) {
+          this.safeScreenHeight
         } else {
           px
         }
@@ -153,6 +157,19 @@ class BottomSheetView(
 
   // Presentation
 
+  private fun getHalfExpandedRatio(contentHeight: Float): Float {
+    return when {
+      // Full height sheets
+      contentHeight >= safeScreenHeight -> 0.99f
+      // Medium height sheets (>50% but <100%)
+      contentHeight >= safeScreenHeight / 2 ->
+        this.clampRatio(this.getTargetHeight() / safeScreenHeight)
+      // Small height sheets (<50%)
+      else ->
+        this.clampRatio(this.getTargetHeight() / rawScreenHeight)
+    }
+  }
+
   private fun present() {
     if (this.isOpen || this.isOpening || this.isClosing) return
 
@@ -172,12 +189,12 @@ class BottomSheetView(
       val behavior = BottomSheetBehavior.from(it)
       behavior.state = BottomSheetBehavior.STATE_HIDDEN
       behavior.isFitToContents = true
-      behavior.halfExpandedRatio = this.clampRatio(this.getTargetHeight() / this.screenHeight)
+      behavior.halfExpandedRatio = getHalfExpandedRatio(contentHeight)
       behavior.skipCollapsed = true
       behavior.isDraggable = true
       behavior.isHideable = true
 
-      if (contentHeight >= this.screenHeight || this.minHeight >= this.screenHeight) {
+      if (contentHeight >= this.safeScreenHeight || this.minHeight >= this.safeScreenHeight) {
         behavior.state = BottomSheetBehavior.STATE_EXPANDED
         this.selectedSnapPoint = 2
       } else {
@@ -227,11 +244,11 @@ class BottomSheetView(
     bottomSheet?.let {
       val behavior = BottomSheetBehavior.from(it)
 
-      behavior.halfExpandedRatio = this.clampRatio(this.getTargetHeight() / this.screenHeight)
+      behavior.halfExpandedRatio = getHalfExpandedRatio(contentHeight)
 
-      if (contentHeight > this.screenHeight && behavior.state != BottomSheetBehavior.STATE_EXPANDED) {
+      if (contentHeight > this.safeScreenHeight && behavior.state != BottomSheetBehavior.STATE_EXPANDED) {
         behavior.state = BottomSheetBehavior.STATE_EXPANDED
-      } else if (contentHeight < this.screenHeight && behavior.state != BottomSheetBehavior.STATE_HALF_EXPANDED) {
+      } else if (contentHeight < this.safeScreenHeight && behavior.state != BottomSheetBehavior.STATE_HALF_EXPANDED) {
         behavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED
       }
     }
diff --git a/modules/bottom-sheet/src/BottomSheetNativeComponent.tsx b/modules/bottom-sheet/src/BottomSheetNativeComponent.tsx
index acd46ce01..1fe592aa2 100644
--- a/modules/bottom-sheet/src/BottomSheetNativeComponent.tsx
+++ b/modules/bottom-sheet/src/BottomSheetNativeComponent.tsx
@@ -1,14 +1,17 @@
 import * as React from 'react'
 import {
   Dimensions,
+  LayoutChangeEvent,
   NativeSyntheticEvent,
   Platform,
   StyleProp,
   View,
   ViewStyle,
 } from 'react-native'
+import {useSafeAreaInsets} from 'react-native-safe-area-context'
 import {requireNativeModule, requireNativeViewManager} from 'expo-modules-core'
 
+import {isIOS} from '#/platform/detection'
 import {BottomSheetState, BottomSheetViewProps} from './BottomSheet.types'
 import {BottomSheetPortalProvider} from './BottomSheetPortal'
 import {Context as PortalContext} from './BottomSheetPortal'
@@ -81,12 +84,10 @@ export class BottomSheetNativeComponent extends React.Component<
       return null
     }
 
-    const {children, backgroundColor, ...rest} = this.props
-    const cornerRadius = rest.cornerRadius ?? 0
-
     let extraStyles
     if (isIOS15 && this.state.viewHeight) {
       const {viewHeight} = this.state
+      const cornerRadius = this.props.cornerRadius ?? 0
       if (viewHeight < screenHeight / 2) {
         extraStyles = {
           height: viewHeight,
@@ -99,39 +100,70 @@ export class BottomSheetNativeComponent extends React.Component<
 
     return (
       <Portal>
-        <NativeView
-          {...rest}
+        <BottomSheetNativeComponentInner
+          {...this.props}
+          nativeViewRef={this.ref}
           onStateChange={this.onStateChange}
-          ref={this.ref}
-          style={{
-            position: 'absolute',
-            height: screenHeight,
-            width: '100%',
+          extraStyles={extraStyles}
+          onLayout={e => {
+            const {height} = e.nativeEvent.layout
+            this.setState({viewHeight: height})
+            this.updateLayout()
           }}
-          containerBackgroundColor={backgroundColor}>
-          <View
-            style={[
-              {
-                flex: 1,
-                backgroundColor,
-              },
-              Platform.OS === 'android' && {
-                borderTopLeftRadius: cornerRadius,
-                borderTopRightRadius: cornerRadius,
-              },
-              extraStyles,
-            ]}>
-            <View
-              onLayout={e => {
-                const {height} = e.nativeEvent.layout
-                this.setState({viewHeight: height})
-                this.updateLayout()
-              }}>
-              <BottomSheetPortalProvider>{children}</BottomSheetPortalProvider>
-            </View>
-          </View>
-        </NativeView>
+        />
       </Portal>
     )
   }
 }
+
+function BottomSheetNativeComponentInner({
+  children,
+  backgroundColor,
+  onLayout,
+  onStateChange,
+  nativeViewRef,
+  extraStyles,
+  ...rest
+}: BottomSheetViewProps & {
+  extraStyles?: StyleProp<ViewStyle>
+  onStateChange: (
+    event: NativeSyntheticEvent<{state: BottomSheetState}>,
+  ) => void
+  nativeViewRef: React.RefObject<View>
+  onLayout: (event: LayoutChangeEvent) => void
+}) {
+  const insets = useSafeAreaInsets()
+  const cornerRadius = rest.cornerRadius ?? 0
+
+  const sheetHeight = isIOS ? screenHeight - insets.top : screenHeight
+
+  return (
+    <NativeView
+      {...rest}
+      onStateChange={onStateChange}
+      ref={nativeViewRef}
+      style={{
+        position: 'absolute',
+        height: sheetHeight,
+        width: '100%',
+      }}
+      containerBackgroundColor={backgroundColor}>
+      <View
+        style={[
+          {
+            flex: 1,
+            backgroundColor,
+          },
+          Platform.OS === 'android' && {
+            borderTopLeftRadius: cornerRadius,
+            borderTopRightRadius: cornerRadius,
+          },
+          extraStyles,
+        ]}>
+        <View onLayout={onLayout}>
+          <BottomSheetPortalProvider>{children}</BottomSheetPortalProvider>
+        </View>
+      </View>
+    </NativeView>
+  )
+}