-
-
Notifications
You must be signed in to change notification settings - Fork 514
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(iOS): add support for medium detent (#1649)
## Description Add support for [`sheetPresentationController`](https://developer.apple.com/documentation/uikit/uiviewcontroller/3850571-sheetpresentationcontroller?language=objc) by exposing new props. These changes work on both architectures (Fabric & Paper). Known issues: * Transition animations between detent sizes are not working on Fabric. Nevertheless after the review process I'm gonna merge this PR as it is getting to big and address these issues in separate PRs. ## Changes * [x] Added `sheetAllowedDetens` prop * Describes heights where a sheet can rest. * Works only when `stackPresentation` is set to `formSheet`. * Defaults to `large`. * [x] Added `sheetCornerRadius` prop * The corner radius that the sheet will try to render with. * Works only when `stackPresentation` is set to `formSheet`. * If left unset system default is used. * [x] Added `sheetLargestUndimmedDetent` prop * The largest sheet detent for which a view underneath won't be dimmed. * Works only when `stackPresentation` is set to `formSheet`. * If this prop is set to: * - `large` - the view underneath won't be dimmed at any detent level * - `medium` - the view underneath will be dimmed only when detent level is `large` * - `all` - the view underneath will be dimmed for any detent level * Defaults to `all`. * [x] Added `sheetGrabberVisible` prop * Boolean indicating whether the sheet shows a grabber at the top. * Works only when `stackPresentation` is set to `formSheet`. * Defaults to `false`. * [x] Added `sheetExpandsWhenScrolledToEdge` prop * Whether the sheet should expand to larger detent when scrolling. * Works only when `stackPresentation` is set to `formSheet`. * Defaults to `true`. * [x] Added noop implementations for Android * [x] Updated view manager interfaces for Paper ## Screenshots / GIFs ## Test code and steps to reproduce `Test1649` in `TestsExample` & `FabricTestExample` ## Checklist - [x] Included code example that can be used to test this change - [x] Updated TS types - [x] Updated documentation: <!-- For adding new props to native-stack --> - [x] https://github.com/software-mansion/react-native-screens/blob/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md - [x] https://github.com/software-mansion/react-native-screens/blob/main/native-stack/README.md - [x] https://github.com/software-mansion/react-native-screens/blob/main/src/types.tsx - [x] https://github.com/software-mansion/react-native-screens/blob/main/src/native-stack/types.tsx - [x] Ensured that CI passes
- Loading branch information
Showing
20 changed files
with
797 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
/* eslint-disable react/display-name */ | ||
import * as React from 'react'; | ||
import {Button, StyleSheet, View, Text, ScrollView} from 'react-native'; | ||
import {NavigationContainer, ParamListBase} from '@react-navigation/native'; | ||
import { | ||
createNativeStackNavigator, | ||
NativeStackNavigationProp, | ||
NativeStackNavigationOptions | ||
} from 'react-native-screens/native-stack'; | ||
import { SheetDetentTypes } from 'react-native-screens'; | ||
|
||
const Stack = createNativeStackNavigator(); | ||
|
||
export default function App(): JSX.Element { | ||
const initialScreenOptions: NativeStackNavigationOptions = { | ||
stackPresentation: 'formSheet', | ||
sheetAllowedDetents: 'all', | ||
sheetLargestUndimmedDetent: 'medium', | ||
sheetGrabberVisible: false, | ||
sheetCornerRadius: -1, | ||
sheetExpandsWhenScrolledToEdge: true | ||
} | ||
|
||
return ( | ||
<NavigationContainer> | ||
<Stack.Navigator | ||
screenOptions={{ | ||
// headerRight: () => <View style={styles.headerView} />, | ||
headerTitleStyle: { | ||
color: 'cyan', | ||
}, | ||
headerShown: true, | ||
headerHideBackButton: false | ||
}}> | ||
<Stack.Screen name="First" component={First} /> | ||
<Stack.Screen name="Second" component={Second} options={{ | ||
fullScreenSwipeEnabled: true, | ||
}} /> | ||
<Stack.Screen name="SheetScreen" component={SheetScreen} options={{ | ||
...initialScreenOptions | ||
}}/> | ||
<Stack.Screen name="SheetScreenWithScrollView" component={SheetScreenWithScrollView} options={{ | ||
...initialScreenOptions | ||
}}/> | ||
|
||
</Stack.Navigator> | ||
</NavigationContainer> | ||
); | ||
} | ||
|
||
function First({ | ||
navigation, | ||
}: { | ||
navigation: NativeStackNavigationProp<ParamListBase>; | ||
}) { | ||
return ( | ||
<Button | ||
title="Tap me for the second screen" | ||
onPress={() => navigation.navigate('Second')} | ||
/> | ||
); | ||
} | ||
|
||
function Second({ | ||
navigation, | ||
}: { | ||
navigation: NativeStackNavigationProp<ParamListBase>; | ||
}) { | ||
return ( | ||
<> | ||
<Button | ||
title="Open the sheet" | ||
onPress={() => navigation.navigate("SheetScreen")} | ||
/> | ||
<Button | ||
title="Open the sheet with ScrollView" | ||
onPress={() => navigation.navigate("SheetScreenWithScrollView")} | ||
/> | ||
</> | ||
) | ||
} | ||
|
||
|
||
function SheetScreen({ | ||
navigation, | ||
}: { | ||
navigation: NativeStackNavigationProp<ParamListBase>; | ||
}) { | ||
const [radius, setRadius] = React.useState(-1); | ||
const [detent, setDetent] = React.useState<SheetDetentTypes>('all'); | ||
const [largestUndimmedDetent, sheetLargestUndimmedDetent] = React.useState<SheetDetentTypes>('all'); | ||
const [isGrabberVisible, setIsGrabberVisible] = React.useState(false); | ||
// navigation | ||
const [shouldExpand, setShouldExpand] = React.useState(true) | ||
|
||
function nextDetentLevel(currDetent: SheetDetentTypes): SheetDetentTypes { | ||
if (currDetent === "all") { | ||
return "medium"; | ||
} else if (currDetent === "medium") { | ||
return "large"; | ||
} else if (currDetent === "large") { | ||
return "all"; | ||
} else { | ||
console.warn("Unhandled sheetDetent type") | ||
return "all"; | ||
} | ||
} | ||
|
||
return ( | ||
<View style={styles.centeredView}> | ||
<Button | ||
title="Tap me for the first screen" | ||
onPress={() => navigation.navigate('First')} /> | ||
<Button | ||
title="Tap me for the second screen" | ||
onPress={() => navigation.navigate('Second')} /> | ||
<Button | ||
title="Change the corner radius" | ||
onPress={() => { | ||
const newRadius = radius >= 150 ? -1.0 : radius + 50; | ||
setRadius(newRadius) | ||
navigation.setOptions({ | ||
sheetCornerRadius: newRadius | ||
}) | ||
}}/> | ||
<Text>radius: {radius}</Text> | ||
<Button | ||
title="Change detent level" | ||
onPress={() => { | ||
const newDetentLevel = nextDetentLevel(detent); | ||
setDetent(newDetentLevel) | ||
navigation.setOptions({ | ||
sheetAllowedDetents: newDetentLevel | ||
}) | ||
}} | ||
/> | ||
<Text>detent: {detent}</Text> | ||
<Button | ||
title="Change largest undimmed detent" | ||
onPress={() => { | ||
const newDetentLevel = nextDetentLevel(largestUndimmedDetent); | ||
sheetLargestUndimmedDetent(newDetentLevel); | ||
navigation.setOptions({ | ||
sheetLargestUndimmedDetent: newDetentLevel | ||
}) | ||
}} | ||
/> | ||
<Text>largestUndimmedDetent: {largestUndimmedDetent}</Text> | ||
<Button | ||
title="Toggle sheetExpandsWhenScrolledToEdge" | ||
onPress={() => { | ||
setShouldExpand(!shouldExpand); | ||
navigation.setOptions({ | ||
sheetExpandsWhenScrolledToEdge: !shouldExpand | ||
}) | ||
}} | ||
/> | ||
<Text>sheetExpandsWhenScrolledToEdge: {shouldExpand ? "true" : "false"}</Text> | ||
<Button | ||
title="Toggle grabber visibility" | ||
onPress={() => { | ||
setIsGrabberVisible(!isGrabberVisible); | ||
navigation.setOptions({ | ||
sheetGrabberVisible: !isGrabberVisible | ||
}) | ||
}} | ||
/> | ||
</View> | ||
); | ||
} | ||
|
||
function SheetScreenWithScrollView({ navigation }: { navigation: NativeStackNavigationProp<ParamListBase> }) { | ||
return ( | ||
<> | ||
<View style={styles.centeredView}> | ||
<ScrollView> | ||
<SheetScreen navigation={navigation} /> | ||
{[...Array(40).keys()].map((val) => <Text key={`${val}`}>Some component {val}</Text>)} | ||
</ScrollView> | ||
</View> | ||
</> | ||
) | ||
} | ||
|
||
|
||
const styles = StyleSheet.create({ | ||
headerView: { | ||
height: 20, | ||
width: 20, | ||
backgroundColor: 'red', | ||
}, | ||
centeredView: { | ||
justifyContent: 'center', | ||
alignItems: 'center' | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.