diff --git a/guides/GUIDE_FOR_LIBRARY_AUTHORS.md b/guides/GUIDE_FOR_LIBRARY_AUTHORS.md index 0f0a72d4b1..4689105491 100644 --- a/guides/GUIDE_FOR_LIBRARY_AUTHORS.md +++ b/guides/GUIDE_FOR_LIBRARY_AUTHORS.md @@ -150,17 +150,23 @@ Sets the current screen's available orientations and forces rotation if current Defaults to `default` on iOS. -### `sheetAllowedDetents` (iOS only) +### `sheetAllowedDetents` Describes heights where a sheet can rest. -Works only when `stackPresentation` is set to `formSheet`. +Works only when `presentation` is set to `formSheet`. Heights should be described as fraction (a number from `[0, 1]` interval) of screen height / maximum detent height. -There is also possibility to specify `[-1]` literal array with single element, which intets to set the sheet height +There is also possibility to specify `fitToContents` literal, which intents to set the sheet height to the height of its contents. Please note that the array **must** be sorted in ascending order. +There are also legacy & **deprecated** options available: + +* `medium` - corresponds to `[0.5]` detent value, around half of the screen height, +* `large` - corresponds to `[1.0]` detent value, maximum height, +* `all` - corresponds to `[0.5, 1.0]` value, the name is deceiving due to compatibility reasons. + Defaults to `[1.0]` literal. ### `sheetExpandsWhenScrolledToEdge` (iOS only) @@ -170,7 +176,7 @@ Works only when `stackPresentation` is set to `formSheet`. Defaults to `true`. -### `sheetCornerRadius (iOS only) +### `sheetCornerRadius` The corner radius that the sheet will try to render with. Works only when `stackPresentation` is set to `formSheet`. @@ -188,15 +194,15 @@ Defaults to `false`. ### `sheetLargestUndimmedDetent` (iOS only) The largest sheet detent for which a view underneath won't be dimmed. -Works only when `stackPresentation` is set to `formSheet`. +Works only when `presentation` is set to `formSheet`. -If this prop is set to: +This prop can be set to an number, which indicates index of detent in `sheetAllowedDetents` array for which +there won't be a dimming view beneath the sheet. -- `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 +There also legacy & **deprecated** prop values available, which work in tandem with +corresponding legacy proop values for `sheetAllowedDetents` prop. -Defaults to `all`. +Defaults to `-1`, indicating that the dimming view should be always present. ### `stackAnimation` @@ -365,7 +371,7 @@ function Home() { } ``` -### unstable_footerComponent +### unstable_sheetFooter (Android only) Footer component that can be used alongside form sheet stack presentation style. @@ -375,6 +381,8 @@ to implement such layout with JS-only code. Please note that this prop is marked as unstable and might be subject of breaking changes, even removal. +Currently supported on Android only. + ## `` diff --git a/native-stack/README.md b/native-stack/README.md index d0a6563da2..9c8f6429d0 100644 --- a/native-stack/README.md +++ b/native-stack/README.md @@ -247,14 +247,28 @@ Defaults to `pop`. Describes heights where a sheet can rest. Works only when `stackPresentation` is set to `formSheet`. -Heights should be described as fraction (a number from [0, 1] interval) of screen height / maximum detent height. -There is also possibility to specify `fitToContents` literal, which intents to set the sheet height +Heights should be described as fraction (a number from `[0, 1]` interval) of screen height / maximum detent height. +There is also possibility to specify `[-1]` literal array with single element, which intets to set the sheet height to the height of its contents. Please note that the array **must** be sorted in ascending order. +There are also legacy & **deprecated** options available: + +* 'medium' - corresponds to `[0.5]` detent value, around half of the screen height, +* 'large' - corresponds to `[1.0]` detent value, maximum height, +* 'all' - corresponds to `[0.5, 1.0]` value, the name is deceiving due to compatibility reasons. + Defaults to `[1.0]` literal. +#### `sheetElevation` (Android only) + +Integer value describing elevation of the sheet, impacting shadow on the top edge of the sheet. + +Not dynamic - changing it after the component is rendered won't have an effect. + +Defaults to `24`. + #### `sheetExpandsWhenScrolledToEdge` (iOS only) Whether the sheet should expand to larger detent when scrolling. @@ -290,6 +304,9 @@ Works only when `stackPresentation` is set to `formSheet`. This prop can be set to an number, which indicates index of detent in `sheetAllowedDetents` array for which there won't be a dimming view beneath the sheet. +There also legacy & **deprecated** prop values available, which work in tandem with +corresponding legacy prop values for `sheetAllowedDetents` prop. + Defaults to `-1`, indicating that the dimming view should be always present. #### `stackAnimation` diff --git a/react-navigation b/react-navigation index 248fcf7d32..c7cbe96430 160000 --- a/react-navigation +++ b/react-navigation @@ -1 +1 @@ -Subproject commit 248fcf7d328b7a813106f5306a78ffd7eef5bcb3 +Subproject commit c7cbe9643091a9a91dbed6b18fd579166d34107a diff --git a/src/components/Screen.tsx b/src/components/Screen.tsx index dec294fa2d..aa1c1440d0 100644 --- a/src/components/Screen.tsx +++ b/src/components/Screen.tsx @@ -43,6 +43,50 @@ interface ViewConfig extends View { }; } +// This value must be kept in sync with native side. +const SHEET_FIT_TO_CONTENTS = [-1]; +const SHEET_COMPAT_LARGE = [1.0]; +const SHEET_COMPAT_MEDIUM = [0.5]; +const SHEET_COMPAT_ALL = [0.5, 1.0]; + +// These exist to transform old 'legacy' values used by the formsheet API to the new API shape. +// We can get rid of it, once we get rid of support for legacy values: 'large', 'medium', 'all'. +function resolveSheetAllowedDetents( + allowedDetentsCompat: ScreenProps['sheetAllowedDetents'], +): number[] { + if (Array.isArray(allowedDetentsCompat)) { + return allowedDetentsCompat; + } else if (allowedDetentsCompat === 'fitToContents') { + return SHEET_FIT_TO_CONTENTS; + } else if (allowedDetentsCompat === 'large') { + return SHEET_COMPAT_LARGE; + } else if (allowedDetentsCompat === 'medium') { + return SHEET_COMPAT_MEDIUM; + } else if (allowedDetentsCompat === 'all') { + return SHEET_COMPAT_ALL; + } else { + // Safe default, only large detent is allowed. + return [1.0]; + } +} + +function resolveSheetLargestUndimmedDetent( + lud: ScreenProps['sheetLargestUndimmedDetent'], +): number { + if (typeof lud === 'number') { + return lud; + } else if (lud === 'large') { + return 1; + } else if (lud === 'medium') { + return 0; + } else if (lud === 'all') { + return -1; + } else { + // Safe default, every detent is dimmed + return -1; + } +} + export const InnerScreen = React.forwardRef( function InnerScreen(props, ref) { const innerRef = React.useRef(null); @@ -66,6 +110,7 @@ export const InnerScreen = React.forwardRef( // To maintain default behavior of formSheet stack presentation style and to have reasonable // defaults for new medium-detent iOS API we need to set defaults here const { + // formSheet presentation related props sheetAllowedDetents = [1.0], sheetLargestUndimmedDetent = -1, sheetGrabberVisible = false, @@ -73,10 +118,16 @@ export const InnerScreen = React.forwardRef( sheetExpandsWhenScrolledToEdge = true, sheetElevation = 24, sheetInitialDetent = 0, + + // Other stackPresentation, } = rest; if (enabled && isNativePlatformSupported) { + const resolvedSheetAllowedDetents = + resolveSheetAllowedDetents(sheetAllowedDetents); + const resolvedSheetLargestUndimmedDetent = + resolveSheetLargestUndimmedDetent(sheetLargestUndimmedDetent); // Due to how Yoga resolves layout, we need to have different components for modal nad non-modal screens const AnimatedScreen = Platform.OS === 'android' || @@ -134,8 +185,8 @@ export const InnerScreen = React.forwardRef( // Detailed information can be found here https://github.com/software-mansion/react-native-screens/pull/2351 style={[style, { zIndex: undefined }]} activityState={activityState} - sheetAllowedDetents={sheetAllowedDetents} - sheetLargestUndimmedDetent={sheetLargestUndimmedDetent} + sheetAllowedDetents={resolvedSheetAllowedDetents} + sheetLargestUndimmedDetent={resolvedSheetLargestUndimmedDetent} sheetElevation={sheetElevation} sheetGrabberVisible={sheetGrabberVisible} sheetCornerRadius={sheetCornerRadius} diff --git a/src/native-stack/types.tsx b/src/native-stack/types.tsx index fadde7ee47..12ff6f82a3 100644 --- a/src/native-stack/types.tsx +++ b/src/native-stack/types.tsx @@ -517,7 +517,7 @@ export type NativeStackNavigationOptions = { * * @platform android */ - unstable_footerComponent?: React.ReactNode; + unstable_sheetFooter?: () => React.ReactNode; }; export type NativeStackNavigatorProps = diff --git a/src/native-stack/views/NativeStackView.tsx b/src/native-stack/views/NativeStackView.tsx index 0f4717121a..dec52bb618 100644 --- a/src/native-stack/views/NativeStackView.tsx +++ b/src/native-stack/views/NativeStackView.tsx @@ -215,7 +215,7 @@ const RouteView = ({ swipeDirection = 'horizontal', transitionDuration, freezeOnBlur, - unstable_footerComponent = null, + unstable_sheetFooter = null, } = options; let { @@ -229,9 +229,10 @@ const RouteView = ({ } = options; // We only want to allow backgroundColor for now - unstable_screenStyle = unstable_screenStyle - ? { backgroundColor: unstable_screenStyle.backgroundColor } - : null; + unstable_screenStyle = + stackPresentation === 'formSheet' && unstable_screenStyle + ? { backgroundColor: unstable_screenStyle.backgroundColor } + : null; if (sheetAllowedDetents === 'fitToContents') { sheetAllowedDetents = [-1]; @@ -450,8 +451,8 @@ const RouteView = ({ route={route} headerShown={isHeaderInPush} /> - {unstable_footerComponent && ( - {unstable_footerComponent} + {stackPresentation === 'formSheet' && unstable_sheetFooter && ( + {unstable_sheetFooter()} )} diff --git a/src/types.tsx b/src/types.tsx index 1ccd801e66..ceb580ccb7 100644 --- a/src/types.tsx +++ b/src/types.tsx @@ -104,7 +104,6 @@ export interface ScreenProps extends ViewProps { active?: 0 | 1 | Animated.AnimatedInterpolation; activityState?: 0 | 1 | 2 | Animated.AnimatedInterpolation; children?: React.ReactNode; - unstable_footer?: React.ReactNode; /** * Boolean indicating that swipe dismissal should trigger animation provided by `stackAnimation`. Defaults to `false`. * @@ -299,9 +298,15 @@ export interface ScreenProps extends ViewProps { * * Please note that the array **must** be sorted in ascending order. * + * There are also legacy & **deprecated** options available: + * + * * 'medium' - corresponds to `[0.5]` detent value, around half of the screen height, + * * 'large' - corresponds to `[1.0]` detent value, maximum height, + * * 'all' - corresponds to `[0.5, 1.0]` value, the name is deceiving due to compatibility reasons. + * * Defaults to `[1.0]` literal. */ - sheetAllowedDetents?: number[]; + sheetAllowedDetents?: number[] | 'fitToContents' | 'medium' | 'large' | 'all'; /** * Integer value describing elevation of the sheet, impacting shadow on the top edge of the sheet. * @@ -346,9 +351,12 @@ export interface ScreenProps extends ViewProps { * This prop can be set to an number, which indicates index of detent in `sheetAllowedDetents` array for which * there won't be a dimming view beneath the sheet. * + * There also legacy & **deprecated** prop values available, which work in tandem with + * corresponding legacy prop values for `sheetAllowedDetents` prop. + * * Defaults to `-1`, indicating that the dimming view should be always present. */ - sheetLargestUndimmedDetent?: number; + sheetLargestUndimmedDetent?: number | 'medium' | 'large' | 'all'; /** * Index of the detent the sheet should expand to after being opened. * Works only when `stackPresentation` is set to `formSheet`. @@ -426,6 +434,19 @@ export interface ScreenProps extends ViewProps { * @platform ios */ transitionDuration?: number; + /** + * Footer component that can be used alongside formSheet stack presentation style. + * + * This option is provided, because due to implementation details it might be problematic + * to implement such layout with JS-only code. + * + * Please note that this prop is marked as unstable and might be subject of breaking changes, + * including removal, in particular when we find solution that will make implementing it with JS + * straightforward. + * + * @platform android + */ + unstable_sheetFooter?: () => React.ReactNode; } export interface ScreenContainerProps extends ViewProps {