fix: consider header colors when managing statusbar

This commit is contained in:
Satyajit Sahoo
2021-03-13 17:37:43 +01:00
parent e1ab06d3d7
commit faee245d2e
4 changed files with 55 additions and 20 deletions

View File

@@ -5,18 +5,24 @@ import type { EdgeInsets } from 'react-native-safe-area-context';
import type { Layout } from '../types';
type Props = {
dark: boolean | undefined;
layout: Layout;
insets: EdgeInsets;
style: any;
};
export default function ModalStatusBarManager({
dark,
layout,
insets,
style,
}: Props) {
const { dark } = useTheme();
const [isDark, setIsDark] = React.useState(true);
const { dark: darkTheme } = useTheme();
const [overlapping, setOverlapping] = React.useState(true);
const enabled = layout.width && layout.height > layout.width;
const scale = 1 - 20 / layout.width;
const offset = (insets.top - 34) * scale;
const flattenedStyle = StyleSheet.flatten(style);
const translateY = flattenedStyle?.transform?.find(
@@ -24,28 +30,29 @@ export default function ModalStatusBarManager({
)?.translateY;
React.useEffect(() => {
const isLandscape = layout.width > layout.height;
const scale = 1 - 20 / layout.width;
if (dark || isLandscape || !layout.width) {
if (!enabled) {
return;
}
const listener = ({ value }: { value: number }) => {
const isOverlappingStatusBar = value / scale < insets.top / 3;
setIsDark(isOverlappingStatusBar);
setOverlapping(value < offset);
};
const sub = translateY?.addListener(listener);
return () => translateY?.removeListener(sub);
}, [dark, insets.top, layout.height, layout.width, translateY]);
}, [enabled, offset, translateY]);
if (dark) {
if (!enabled) {
return null;
}
const darkContent = dark ?? !darkTheme;
return (
<StatusBar animated barStyle={isDark ? 'dark-content' : 'light-content'} />
<StatusBar
animated
barStyle={overlapping && darkContent ? 'dark-content' : 'light-content'}
/>
);
}

View File

@@ -39,6 +39,7 @@ type Props = ViewProps & {
gesture: Animated.Value;
layout: Layout;
insets: EdgeInsets;
headerDarkContent: boolean | undefined;
pageOverflowEnabled: boolean;
gestureDirection: GestureDirection;
onOpen: () => void;
@@ -466,6 +467,7 @@ export default class Card extends React.Component<Props> {
gestureEnabled,
gestureDirection,
pageOverflowEnabled,
headerDarkContent,
children,
containerStyle: customContainerStyle,
contentStyle,
@@ -525,15 +527,21 @@ export default class Card extends React.Component<Props> {
return (
<CardAnimationContext.Provider value={animationContext}>
{index === 0 &&
next &&
styleInterpolator === forModalPresentationIOS ? (
<ModalStatusBarManager
layout={layout}
insets={insets}
style={cardStyle}
/>
) : null}
{
// StatusBar messes with translucent status bar on Android
// So we should only enable it on iOS
Platform.OS === 'ios' &&
index === 0 &&
next &&
styleInterpolator === forModalPresentationIOS ? (
<ModalStatusBarManager
dark={headerDarkContent}
layout={layout}
insets={insets}
style={cardStyle}
/>
) : null
}
<Animated.View
style={{
// This is a dummy style that doesn't actually change anything visually.

View File

@@ -27,6 +27,7 @@ type Props = TransitionPreset & {
layout: Layout;
gesture: Animated.Value;
scene: Scene;
headerDarkContent: boolean | undefined;
safeAreaInsetTop: number;
safeAreaInsetRight: number;
safeAreaInsetBottom: number;
@@ -91,6 +92,7 @@ function CardContainer({
getPreviousScene,
getFocusedRoute,
mode,
headerDarkContent,
headerMode,
headerShown,
headerStyleInterpolator,
@@ -248,6 +250,7 @@ function CardContainer({
importantForAccessibility={focused ? 'auto' : 'no-hide-descendants'}
pointerEvents={active ? 'box-none' : pointerEvents}
pageOverflowEnabled={headerMode !== 'float' && mode === 'card'}
headerDarkContent={headerDarkContent}
containerStyle={hasAbsoluteHeader ? { marginTop: headerHeight } : null}
contentStyle={[{ backgroundColor: colors.background }, cardStyle]}
style={[

View File

@@ -39,6 +39,7 @@ import type {
StackDescriptor,
Scene,
} from '../../types';
import Color from 'color';
type GestureValues = {
[key: string]: Animated.Value;
@@ -529,6 +530,8 @@ export default class CardStack extends React.Component<Props, State> {
const {
headerShown = true,
headerTransparent,
headerStyle,
headerTintColor,
cardShadowEnabled,
cardOverlayEnabled = Platform.OS !== 'ios' || mode === 'modal',
cardOverlay,
@@ -592,6 +595,19 @@ export default class CardStack extends React.Component<Props, State> {
const headerHeight =
headerShown !== false ? headerHeights[route.key] : 0;
const { backgroundColor: headerBackgroundColor } =
StyleSheet.flatten(headerStyle) || {};
let headerDarkContent: boolean | undefined;
if (headerShown) {
if (headerTintColor) {
headerDarkContent = Color(headerTintColor).isDark();
} else if (typeof headerBackgroundColor === 'string') {
headerDarkContent = !Color(headerBackgroundColor).isDark();
}
}
return (
<MaybeScreen
key={route.key}
@@ -631,6 +647,7 @@ export default class CardStack extends React.Component<Props, State> {
mode={mode}
headerMode={headerMode}
headerShown={headerShown}
headerDarkContent={headerDarkContent}
hasAbsoluteHeader={
isFloatHeaderAbsolute && !headerTransparent
}