Skip to content

Commit

Permalink
Feat: Add navigationBarTranslucent modal prop
Browse files Browse the repository at this point in the history
  • Loading branch information
zoontek committed Oct 22, 2024
1 parent 3dfe22b commit bbeb78e
Show file tree
Hide file tree
Showing 11 changed files with 118 additions and 3 deletions.
5 changes: 5 additions & 0 deletions packages/react-native/Libraries/Modal/Modal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ export interface ModalPropsAndroid {
* Determines whether your modal should go under the system statusbar.
*/
statusBarTranslucent?: boolean | undefined;

/**
* Determines whether your modal should go under the system navigationbar.
*/
navigationBarTranslucent?: boolean | undefined;
}

export type ModalProps = ModalBaseProps &
Expand Down
9 changes: 9 additions & 0 deletions packages/react-native/Libraries/Modal/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ export type Props = $ReadOnly<{|
*/
statusBarTranslucent?: ?boolean,

/**
* The `navigationBarTranslucent` prop determines whether your modal should go under
* the system navigationbar.
*
* See https://reactnative.dev/docs/modal.html#navigationbartranslucent-android
*/
navigationBarTranslucent?: ?boolean,

/**
* The `hardwareAccelerated` prop controls whether to force hardware
* acceleration for the underlying window.
Expand Down Expand Up @@ -301,6 +309,7 @@ class Modal extends React.Component<Props, State> {
onDismiss={onDismiss}
visible={this.props.visible}
statusBarTranslucent={this.props.statusBarTranslucent}
navigationBarTranslucent={this.props.navigationBarTranslucent}
identifier={this._identifier}
style={styles.modal}
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6411,6 +6411,7 @@ export type Props = $ReadOnly<{|
),
transparent?: ?boolean,
statusBarTranslucent?: ?boolean,
navigationBarTranslucent?: ?boolean,
hardwareAccelerated?: ?boolean,
visible?: ?boolean,
onRequestClose?: ?DirectEventHandler<null>,
Expand Down
1 change: 1 addition & 0 deletions packages/react-native/React/Views/RCTModalHostView.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

// Android only
@property (nonatomic, assign) BOOL statusBarTranslucent;
@property (nonatomic, assign) BOOL navigationBarTranslucent;
@property (nonatomic, assign) BOOL hardwareAccelerated;
@property (nonatomic, assign) BOOL animated;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ - (void)invalidate
RCT_EXPORT_VIEW_PROPERTY(presentationStyle, UIModalPresentationStyle)
RCT_EXPORT_VIEW_PROPERTY(transparent, BOOL)
RCT_EXPORT_VIEW_PROPERTY(statusBarTranslucent, BOOL)
RCT_EXPORT_VIEW_PROPERTY(navigationBarTranslucent, BOOL)
RCT_EXPORT_VIEW_PROPERTY(hardwareAccelerated, BOOL)
RCT_EXPORT_VIEW_PROPERTY(animated, BOOL)
RCT_EXPORT_VIEW_PROPERTY(onShow, RCTDirectEventBlock)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ public class ReactModalHostManager :
view.statusBarTranslucent = statusBarTranslucent
}

@ReactProp(name = "navigationBarTranslucent")
public override fun setNavigationBarTranslucent(
view: ReactModalHostView,
navigationBarTranslucent: Boolean
) {
view.navigationBarTranslucent = navigationBarTranslucent
}

@ReactProp(name = "hardwareAccelerated")
public override fun setHardwareAccelerated(
view: ReactModalHostView,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import com.facebook.react.uimanager.events.EventDispatcher
import com.facebook.react.views.common.ContextUtils
import com.facebook.react.views.view.ReactViewGroup
import com.facebook.react.views.view.setStatusBarTranslucency
import com.facebook.react.views.view.setSystemBarsTranslucency
import java.util.Objects

/**
Expand Down Expand Up @@ -79,6 +80,12 @@ public class ReactModalHostView(context: ThemedReactContext) :
createNewDialog = true
}

public var navigationBarTranslucent: Boolean = false
set(value) {
field = value
createNewDialog = true
}

public var animationType: String? = null
set(value) {
field = value
Expand Down Expand Up @@ -328,7 +335,12 @@ public class ReactModalHostView(context: ThemedReactContext) :
}
}

dialogWindow.setStatusBarTranslucency(statusBarTranslucent)
// Navigation bar cannot be translucent without status bar being translucent too
dialogWindow.setSystemBarsTranslucency(navigationBarTranslucent)

if (!navigationBarTranslucent) {
dialogWindow.setStatusBarTranslucency(statusBarTranslucent)
}

if (transparent) {
dialogWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@

package com.facebook.react.views.view

import android.content.res.Configuration
import android.graphics.Color
import android.os.Build
import android.view.Window
import android.view.WindowManager
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat

@Suppress("DEPRECATION")
public fun Window.setStatusBarTranslucency(isTranslucent: Boolean) {
Expand Down Expand Up @@ -61,3 +65,39 @@ private fun Window.statusBarShow() {
addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
}

@Suppress("DEPRECATION")
public fun Window.setSystemBarsTranslucency(isTranslucent: Boolean) {
WindowCompat.setDecorFitsSystemWindows(this, !isTranslucent)

if (isTranslucent) {
val isDarkMode =
context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
Configuration.UI_MODE_NIGHT_YES

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
isStatusBarContrastEnforced = false
isNavigationBarContrastEnforced = true
}

statusBarColor = Color.TRANSPARENT
navigationBarColor = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> Color.TRANSPARENT
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 && !isDarkMode ->
Color.argb(0xe6, 0xFF, 0xFF, 0xFF)
else -> Color.argb(0x80, 0x1b, 0x1b, 0x1b)
}

WindowInsetsControllerCompat(this, this.decorView).run {
isAppearanceLightNavigationBars = !isDarkMode
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
attributes.layoutInDisplayCutoutMode = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R ->
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
else -> WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ type NativeProps = $ReadOnly<{|
*/
statusBarTranslucent?: WithDefault<boolean, false>,

/**
* The `navigationBarTranslucent` prop determines whether your modal should go under
* the system navigationbar.
*
* See https://reactnative.dev/docs/modal#navigationBarTranslucent
*/
navigationBarTranslucent?: WithDefault<boolean, false>,

/**
* The `hardwareAccelerated` prop controls whether to force hardware
* acceleration for the underlying window.
Expand Down
33 changes: 31 additions & 2 deletions packages/rn-tester/js/examples/Modal/ModalPresentation.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,15 @@ import {RNTesterThemeContext} from '../../components/RNTesterTheme';
import RNTOption from '../../components/RNTOption';
import * as React from 'react';
import {useCallback, useContext, useState} from 'react';
import {Modal, Platform, StyleSheet, Switch, Text, View} from 'react-native';
import {
Modal,
Platform,
StatusBar,
StyleSheet,
Switch,
Text,
View,
} from 'react-native';

const animationTypes = ['slide', 'none', 'fade'];
const presentationStyles = [
Expand Down Expand Up @@ -56,6 +64,7 @@ function ModalPresentation() {
transparent: false,
hardwareAccelerated: false,
statusBarTranslucent: false,
navigationBarTranslucent: false,
presentationStyle: Platform.select({
ios: 'fullScreen',
default: undefined,
Expand All @@ -72,6 +81,7 @@ function ModalPresentation() {
const presentationStyle = props.presentationStyle;
const hardwareAccelerated = props.hardwareAccelerated;
const statusBarTranslucent = props.statusBarTranslucent;
const navigationBarTranslucent = props.navigationBarTranslucent;
const backdropColor = props.backdropColor;
const backgroundColor = useContext(RNTesterThemeContext).BackgroundColor;

Expand All @@ -92,10 +102,29 @@ function ModalPresentation() {
<Switch
value={statusBarTranslucent}
onValueChange={enabled =>
setProps(prev => ({...prev, statusBarTranslucent: enabled}))
setProps(prev => ({
...prev,
statusBarTranslucent: enabled,
navigationBarTranslucent: false,
}))
}
/>
</View>
<View style={styles.inlineBlock}>
<RNTesterText style={styles.title}>
Navigation Bar Translucent 🟢
</RNTesterText>
<Switch
value={navigationBarTranslucent}
onValueChange={enabled => {
setProps(prev => ({
...prev,
statusBarTranslucent: enabled,
navigationBarTranslucent: enabled,
}));
}}
/>
</View>
<View style={styles.inlineBlock}>
<RNTesterText style={styles.title}>
Hardware Acceleration 🟢
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export function ScrollViewIndicatorInsetsExample() {
onRequestClose={() => setModalVisible(false)}
presentationStyle="fullScreen"
statusBarTranslucent={false}
navigationBarTranslucent={false}
supportedOrientations={['portrait', 'landscape']}>
<View style={styles.modal}>
<ScrollView
Expand Down

0 comments on commit bbeb78e

Please sign in to comment.