230 lines
7.0 KiB
JavaScript
230 lines
7.0 KiB
JavaScript
import * as React from 'react';
|
|
import { Animated, Platform, StyleSheet, View } from 'react-native';
|
|
import { useSafeAreaFrame, useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
import { getDefaultHeaderHeight } from './getDefaultHeaderHeight';
|
|
import { HeaderBackground } from './HeaderBackground';
|
|
import { HeaderShownContext } from './HeaderShownContext';
|
|
import { HeaderTitle } from './HeaderTitle';
|
|
const warnIfHeaderStylesDefined = styles => {
|
|
Object.keys(styles).forEach(styleProp => {
|
|
const value = styles[styleProp];
|
|
if (styleProp === 'position' && value === 'absolute') {
|
|
console.warn("position: 'absolute' is not supported on headerStyle. If you would like to render content under the header, use the 'headerTransparent' option.");
|
|
} else if (value !== undefined) {
|
|
console.warn(`${styleProp} was given a value of ${value}, this has no effect on headerStyle.`);
|
|
}
|
|
});
|
|
};
|
|
export function Header(props) {
|
|
const insets = useSafeAreaInsets();
|
|
const frame = useSafeAreaFrame();
|
|
const isParentHeaderShown = React.useContext(HeaderShownContext);
|
|
const {
|
|
layout = frame,
|
|
modal = false,
|
|
title,
|
|
headerTitle: customTitle,
|
|
headerTitleAlign = Platform.select({
|
|
ios: 'center',
|
|
default: 'left'
|
|
}),
|
|
headerLeft,
|
|
headerLeftLabelVisible,
|
|
headerTransparent,
|
|
headerTintColor,
|
|
headerBackground,
|
|
headerRight,
|
|
headerTitleAllowFontScaling: titleAllowFontScaling,
|
|
headerTitleStyle: titleStyle,
|
|
headerLeftContainerStyle: leftContainerStyle,
|
|
headerRightContainerStyle: rightContainerStyle,
|
|
headerTitleContainerStyle: titleContainerStyle,
|
|
headerBackgroundContainerStyle: backgroundContainerStyle,
|
|
headerStyle: customHeaderStyle,
|
|
headerShadowVisible,
|
|
headerPressColor,
|
|
headerPressOpacity,
|
|
headerStatusBarHeight = isParentHeaderShown ? 0 : insets.top
|
|
} = props;
|
|
const defaultHeight = getDefaultHeaderHeight(layout, modal, headerStatusBarHeight);
|
|
const {
|
|
height = defaultHeight,
|
|
minHeight,
|
|
maxHeight,
|
|
backgroundColor,
|
|
borderBottomColor,
|
|
borderBottomEndRadius,
|
|
borderBottomLeftRadius,
|
|
borderBottomRightRadius,
|
|
borderBottomStartRadius,
|
|
borderBottomWidth,
|
|
borderColor,
|
|
borderEndColor,
|
|
borderEndWidth,
|
|
borderLeftColor,
|
|
borderLeftWidth,
|
|
borderRadius,
|
|
borderRightColor,
|
|
borderRightWidth,
|
|
borderStartColor,
|
|
borderStartWidth,
|
|
borderStyle,
|
|
borderTopColor,
|
|
borderTopEndRadius,
|
|
borderTopLeftRadius,
|
|
borderTopRightRadius,
|
|
borderTopStartRadius,
|
|
borderTopWidth,
|
|
borderWidth,
|
|
// @ts-expect-error: web support for shadow
|
|
boxShadow,
|
|
elevation,
|
|
shadowColor,
|
|
shadowOffset,
|
|
shadowOpacity,
|
|
shadowRadius,
|
|
opacity,
|
|
transform,
|
|
...unsafeStyles
|
|
} = StyleSheet.flatten(customHeaderStyle || {});
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
warnIfHeaderStylesDefined(unsafeStyles);
|
|
}
|
|
const safeStyles = {
|
|
backgroundColor,
|
|
borderBottomColor,
|
|
borderBottomEndRadius,
|
|
borderBottomLeftRadius,
|
|
borderBottomRightRadius,
|
|
borderBottomStartRadius,
|
|
borderBottomWidth,
|
|
borderColor,
|
|
borderEndColor,
|
|
borderEndWidth,
|
|
borderLeftColor,
|
|
borderLeftWidth,
|
|
borderRadius,
|
|
borderRightColor,
|
|
borderRightWidth,
|
|
borderStartColor,
|
|
borderStartWidth,
|
|
borderStyle,
|
|
borderTopColor,
|
|
borderTopEndRadius,
|
|
borderTopLeftRadius,
|
|
borderTopRightRadius,
|
|
borderTopStartRadius,
|
|
borderTopWidth,
|
|
borderWidth,
|
|
// @ts-expect-error: boxShadow is only for Web
|
|
boxShadow,
|
|
elevation,
|
|
shadowColor,
|
|
shadowOffset,
|
|
shadowOpacity,
|
|
shadowRadius,
|
|
opacity,
|
|
transform
|
|
};
|
|
|
|
// Setting a property to undefined triggers default style
|
|
// So we need to filter them out
|
|
// Users can use `null` instead
|
|
for (const styleProp in safeStyles) {
|
|
// @ts-expect-error: typescript wrongly complains that styleProp cannot be used to index safeStyles
|
|
if (safeStyles[styleProp] === undefined) {
|
|
// @ts-expect-error
|
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
delete safeStyles[styleProp];
|
|
}
|
|
}
|
|
const backgroundStyle = [safeStyles, headerShadowVisible === false && {
|
|
elevation: 0,
|
|
shadowOpacity: 0,
|
|
borderBottomWidth: 0
|
|
}];
|
|
const leftButton = headerLeft ? headerLeft({
|
|
tintColor: headerTintColor,
|
|
pressColor: headerPressColor,
|
|
pressOpacity: headerPressOpacity,
|
|
labelVisible: headerLeftLabelVisible
|
|
}) : null;
|
|
const rightButton = headerRight ? headerRight({
|
|
tintColor: headerTintColor,
|
|
pressColor: headerPressColor,
|
|
pressOpacity: headerPressOpacity
|
|
}) : null;
|
|
const headerTitle = typeof customTitle !== 'function' ? props => /*#__PURE__*/React.createElement(HeaderTitle, props) : customTitle;
|
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Animated.View, {
|
|
pointerEvents: "box-none",
|
|
style: [StyleSheet.absoluteFill, {
|
|
zIndex: 0
|
|
}, backgroundContainerStyle]
|
|
}, headerBackground ? headerBackground({
|
|
style: backgroundStyle
|
|
}) : headerTransparent ? null : /*#__PURE__*/React.createElement(HeaderBackground, {
|
|
style: backgroundStyle
|
|
})), /*#__PURE__*/React.createElement(Animated.View, {
|
|
pointerEvents: "box-none",
|
|
style: [{
|
|
height,
|
|
minHeight,
|
|
maxHeight,
|
|
opacity,
|
|
transform
|
|
}]
|
|
}, /*#__PURE__*/React.createElement(View, {
|
|
pointerEvents: "none",
|
|
style: {
|
|
height: headerStatusBarHeight
|
|
}
|
|
}), /*#__PURE__*/React.createElement(View, {
|
|
pointerEvents: "box-none",
|
|
style: styles.content
|
|
}, /*#__PURE__*/React.createElement(Animated.View, {
|
|
pointerEvents: "box-none",
|
|
style: [styles.left, headerTitleAlign === 'center' && styles.expand, {
|
|
marginStart: insets.left
|
|
}, leftContainerStyle]
|
|
}, leftButton), /*#__PURE__*/React.createElement(Animated.View, {
|
|
pointerEvents: "box-none",
|
|
style: [styles.title, {
|
|
// Avoid the title from going offscreen or overlapping buttons
|
|
maxWidth: headerTitleAlign === 'center' ? layout.width - ((leftButton ? headerLeftLabelVisible !== false ? 80 : 32 : 16) + Math.max(insets.left, insets.right)) * 2 : layout.width - ((leftButton ? 72 : 16) + (rightButton ? 72 : 16) + insets.left - insets.right)
|
|
}, titleContainerStyle]
|
|
}, headerTitle({
|
|
children: title,
|
|
allowFontScaling: titleAllowFontScaling,
|
|
tintColor: headerTintColor,
|
|
style: titleStyle
|
|
})), /*#__PURE__*/React.createElement(Animated.View, {
|
|
pointerEvents: "box-none",
|
|
style: [styles.right, styles.expand, {
|
|
marginEnd: insets.right
|
|
}, rightContainerStyle]
|
|
}, rightButton))));
|
|
}
|
|
const styles = StyleSheet.create({
|
|
content: {
|
|
flex: 1,
|
|
flexDirection: 'row',
|
|
alignItems: 'stretch'
|
|
},
|
|
title: {
|
|
marginHorizontal: 16,
|
|
justifyContent: 'center'
|
|
},
|
|
left: {
|
|
justifyContent: 'center',
|
|
alignItems: 'flex-start'
|
|
},
|
|
right: {
|
|
justifyContent: 'center',
|
|
alignItems: 'flex-end'
|
|
},
|
|
expand: {
|
|
flexGrow: 1,
|
|
flexBasis: 0
|
|
}
|
|
});
|
|
//# sourceMappingURL=Header.js.map
|