Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

authored by

Samuel Newman and committed by
GitHub
5d30111b 798bf478

+116 -58
+28 -11
modules/bottom-sheet/android/src/main/java/expo/modules/bottomsheet/BottomSheetView.kt
··· 31 31 private lateinit var dialogRootViewGroup: DialogRootViewGroup 32 32 private var eventDispatcher: EventDispatcher? = null 33 33 34 - private val screenHeight = 35 - context.resources.displayMetrics.heightPixels 36 - .toFloat() 34 + private val rawScreenHeight = context.resources.displayMetrics.heightPixels.toFloat() 35 + private val safeScreenHeight = (rawScreenHeight - getNavigationBarHeight()).toFloat() 36 + 37 + private fun getNavigationBarHeight(): Int { 38 + val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android") 39 + return if (resourceId > 0) resources.getDimensionPixelSize(resourceId) else 0 40 + } 37 41 38 42 private val onAttemptDismiss by EventDispatcher() 39 43 private val onSnapPointChange by EventDispatcher() ··· 63 67 } 64 68 } 65 69 66 - var maxHeight = this.screenHeight 70 + var maxHeight = this.safeScreenHeight 67 71 set(value) { 68 72 val px = dpToPx(value) 69 73 field = 70 - if (px > this.screenHeight) { 71 - this.screenHeight 74 + if (px > this.safeScreenHeight) { 75 + this.safeScreenHeight 72 76 } else { 73 77 px 74 78 } ··· 153 157 154 158 // Presentation 155 159 160 + private fun getHalfExpandedRatio(contentHeight: Float): Float { 161 + return when { 162 + // Full height sheets 163 + contentHeight >= safeScreenHeight -> 0.99f 164 + // Medium height sheets (>50% but <100%) 165 + contentHeight >= safeScreenHeight / 2 -> 166 + this.clampRatio(this.getTargetHeight() / safeScreenHeight) 167 + // Small height sheets (<50%) 168 + else -> 169 + this.clampRatio(this.getTargetHeight() / rawScreenHeight) 170 + } 171 + } 172 + 156 173 private fun present() { 157 174 if (this.isOpen || this.isOpening || this.isClosing) return 158 175 ··· 172 189 val behavior = BottomSheetBehavior.from(it) 173 190 behavior.state = BottomSheetBehavior.STATE_HIDDEN 174 191 behavior.isFitToContents = true 175 - behavior.halfExpandedRatio = this.clampRatio(this.getTargetHeight() / this.screenHeight) 192 + behavior.halfExpandedRatio = getHalfExpandedRatio(contentHeight) 176 193 behavior.skipCollapsed = true 177 194 behavior.isDraggable = true 178 195 behavior.isHideable = true 179 196 180 - if (contentHeight >= this.screenHeight || this.minHeight >= this.screenHeight) { 197 + if (contentHeight >= this.safeScreenHeight || this.minHeight >= this.safeScreenHeight) { 181 198 behavior.state = BottomSheetBehavior.STATE_EXPANDED 182 199 this.selectedSnapPoint = 2 183 200 } else { ··· 227 244 bottomSheet?.let { 228 245 val behavior = BottomSheetBehavior.from(it) 229 246 230 - behavior.halfExpandedRatio = this.clampRatio(this.getTargetHeight() / this.screenHeight) 247 + behavior.halfExpandedRatio = getHalfExpandedRatio(contentHeight) 231 248 232 - if (contentHeight > this.screenHeight && behavior.state != BottomSheetBehavior.STATE_EXPANDED) { 249 + if (contentHeight > this.safeScreenHeight && behavior.state != BottomSheetBehavior.STATE_EXPANDED) { 233 250 behavior.state = BottomSheetBehavior.STATE_EXPANDED 234 - } else if (contentHeight < this.screenHeight && behavior.state != BottomSheetBehavior.STATE_HALF_EXPANDED) { 251 + } else if (contentHeight < this.safeScreenHeight && behavior.state != BottomSheetBehavior.STATE_HALF_EXPANDED) { 235 252 behavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED 236 253 } 237 254 }
+65 -33
modules/bottom-sheet/src/BottomSheetNativeComponent.tsx
··· 1 1 import * as React from 'react' 2 2 import { 3 3 Dimensions, 4 + LayoutChangeEvent, 4 5 NativeSyntheticEvent, 5 6 Platform, 6 7 StyleProp, 7 8 View, 8 9 ViewStyle, 9 10 } from 'react-native' 11 + import {useSafeAreaInsets} from 'react-native-safe-area-context' 10 12 import {requireNativeModule, requireNativeViewManager} from 'expo-modules-core' 11 13 14 + import {isIOS} from '#/platform/detection' 12 15 import {BottomSheetState, BottomSheetViewProps} from './BottomSheet.types' 13 16 import {BottomSheetPortalProvider} from './BottomSheetPortal' 14 17 import {Context as PortalContext} from './BottomSheetPortal' ··· 81 84 return null 82 85 } 83 86 84 - const {children, backgroundColor, ...rest} = this.props 85 - const cornerRadius = rest.cornerRadius ?? 0 86 - 87 87 let extraStyles 88 88 if (isIOS15 && this.state.viewHeight) { 89 89 const {viewHeight} = this.state 90 + const cornerRadius = this.props.cornerRadius ?? 0 90 91 if (viewHeight < screenHeight / 2) { 91 92 extraStyles = { 92 93 height: viewHeight, ··· 99 100 100 101 return ( 101 102 <Portal> 102 - <NativeView 103 - {...rest} 103 + <BottomSheetNativeComponentInner 104 + {...this.props} 105 + nativeViewRef={this.ref} 104 106 onStateChange={this.onStateChange} 105 - ref={this.ref} 106 - style={{ 107 - position: 'absolute', 108 - height: screenHeight, 109 - width: '100%', 107 + extraStyles={extraStyles} 108 + onLayout={e => { 109 + const {height} = e.nativeEvent.layout 110 + this.setState({viewHeight: height}) 111 + this.updateLayout() 110 112 }} 111 - containerBackgroundColor={backgroundColor}> 112 - <View 113 - style={[ 114 - { 115 - flex: 1, 116 - backgroundColor, 117 - }, 118 - Platform.OS === 'android' && { 119 - borderTopLeftRadius: cornerRadius, 120 - borderTopRightRadius: cornerRadius, 121 - }, 122 - extraStyles, 123 - ]}> 124 - <View 125 - onLayout={e => { 126 - const {height} = e.nativeEvent.layout 127 - this.setState({viewHeight: height}) 128 - this.updateLayout() 129 - }}> 130 - <BottomSheetPortalProvider>{children}</BottomSheetPortalProvider> 131 - </View> 132 - </View> 133 - </NativeView> 113 + /> 134 114 </Portal> 135 115 ) 136 116 } 137 117 } 118 + 119 + function BottomSheetNativeComponentInner({ 120 + children, 121 + backgroundColor, 122 + onLayout, 123 + onStateChange, 124 + nativeViewRef, 125 + extraStyles, 126 + ...rest 127 + }: BottomSheetViewProps & { 128 + extraStyles?: StyleProp<ViewStyle> 129 + onStateChange: ( 130 + event: NativeSyntheticEvent<{state: BottomSheetState}>, 131 + ) => void 132 + nativeViewRef: React.RefObject<View> 133 + onLayout: (event: LayoutChangeEvent) => void 134 + }) { 135 + const insets = useSafeAreaInsets() 136 + const cornerRadius = rest.cornerRadius ?? 0 137 + 138 + const sheetHeight = isIOS ? screenHeight - insets.top : screenHeight 139 + 140 + return ( 141 + <NativeView 142 + {...rest} 143 + onStateChange={onStateChange} 144 + ref={nativeViewRef} 145 + style={{ 146 + position: 'absolute', 147 + height: sheetHeight, 148 + width: '100%', 149 + }} 150 + containerBackgroundColor={backgroundColor}> 151 + <View 152 + style={[ 153 + { 154 + flex: 1, 155 + backgroundColor, 156 + }, 157 + Platform.OS === 'android' && { 158 + borderTopLeftRadius: cornerRadius, 159 + borderTopRightRadius: cornerRadius, 160 + }, 161 + extraStyles, 162 + ]}> 163 + <View onLayout={onLayout}> 164 + <BottomSheetPortalProvider>{children}</BottomSheetPortalProvider> 165 + </View> 166 + </View> 167 + </NativeView> 168 + ) 169 + }
+22 -13
src/components/Dialog/index.tsx
··· 26 26 import {useA11y} from '#/state/a11y' 27 27 import {useDialogStateControlContext} from '#/state/dialogs' 28 28 import {List, ListMethods, ListProps} from '#/view/com/util/List' 29 - import {atoms as a, useTheme} from '#/alf' 29 + import {atoms as a, tokens, useTheme} from '#/alf' 30 30 import {useThemeName} from '#/alf/util/useColorModeTheme' 31 31 import {Context, useDialogContext} from '#/components/Dialog/context' 32 32 import { ··· 46 46 export * from '#/components/Dialog/shared' 47 47 export * from '#/components/Dialog/types' 48 48 export * from '#/components/Dialog/utils' 49 - // @ts-ignore 49 + 50 50 export const Input = createInput(TextInput) 51 51 52 52 export function Outer({ ··· 168 168 onStateChange={onStateChange} 169 169 disableDrag={disableDrag}> 170 170 <Context.Provider value={context}> 171 - <View testID={testID}>{children}</View> 171 + <View testID={testID} style={[a.relative]}> 172 + {children} 173 + </View> 172 174 </Context.Provider> 173 175 </BottomSheet> 174 176 ) ··· 196 198 197 199 export const ScrollableInner = React.forwardRef<ScrollView, DialogInnerProps>( 198 200 function ScrollableInner( 199 - {children, style, contentContainerStyle, header, ...props}, 201 + {children, contentContainerStyle, header, ...props}, 200 202 ref, 201 203 ) { 202 204 const {nativeSnapPoint, disableDrag, setDisableDrag} = useDialogContext() ··· 216 218 [], 217 219 ) 218 220 219 - const basePading = 220 - (isIOS ? 30 : 50) + (isIOS ? keyboardHeight / 4 : keyboardHeight) 221 - const fullPaddingBase = insets.bottom + insets.top + basePading 222 - const fullPadding = isIOS ? fullPaddingBase : fullPaddingBase + 50 223 - 224 - const paddingBottom = 225 - nativeSnapPoint === BottomSheetSnapPoint.Full ? fullPadding : basePading 221 + let paddingBottom = 0 222 + if (isIOS) { 223 + paddingBottom += keyboardHeight / 4 224 + if (nativeSnapPoint === BottomSheetSnapPoint.Full) { 225 + paddingBottom += insets.bottom + tokens.space.md 226 + } 227 + paddingBottom = Math.max(paddingBottom, tokens.space._2xl) 228 + } else { 229 + paddingBottom += keyboardHeight 230 + if (nativeSnapPoint === BottomSheetSnapPoint.Full) { 231 + paddingBottom += insets.top 232 + } 233 + paddingBottom += 234 + Math.max(insets.bottom, tokens.space._5xl) + tokens.space._2xl 235 + } 226 236 227 237 const onScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => { 228 238 if (!isAndroid) { ··· 238 248 239 249 return ( 240 250 <KeyboardAwareScrollView 241 - style={[style]} 242 251 contentContainerStyle={[ 243 252 a.pt_2xl, 244 253 a.px_xl, ··· 316 325 style={[ 317 326 a.rounded_sm, 318 327 { 319 - top: 10, 328 + top: tokens.space._2xl / 2 - 2.5, 320 329 width: 35, 321 330 height: 5, 322 331 alignSelf: 'center',
+1 -1
src/components/Menu/index.tsx
··· 100 100 <Dialog.Handle /> 101 101 {/* Re-wrap with context since Dialogs are portal-ed to root */} 102 102 <Context.Provider value={context}> 103 - <Dialog.ScrollableInner label={_(msg`Menu`)} style={[a.py_sm]}> 103 + <Dialog.ScrollableInner label={_(msg`Menu`)}> 104 104 <View style={[a.gap_lg]}> 105 105 {children} 106 106 {isNative && showCancel && <Cancel />}