1bcc58947S猫头猫import React, {ReactNode, useEffect, useState} from 'react'; 2e650bfb3S猫头猫import { 3e650bfb3S猫头猫 LayoutRectangle, 45589cdf3S猫头猫 StatusBar as OriginalStatusBar, 55589cdf3S猫头猫 StyleProp, 6e650bfb3S猫头猫 StyleSheet, 7e650bfb3S猫头猫 TouchableWithoutFeedback, 8e650bfb3S猫头猫 View, 91119c2eaS猫头猫 ViewStyle, 10e650bfb3S猫头猫} from 'react-native'; 11e650bfb3S猫头猫import rpx from '@/utils/rpx'; 12e650bfb3S猫头猫import useColors from '@/hooks/useColors'; 13e650bfb3S猫头猫import StatusBar from './statusBar'; 14e650bfb3S猫头猫import color from 'color'; 15e650bfb3S猫头猫import IconButton from './iconButton'; 16e650bfb3S猫头猫import globalStyle from '@/constants/globalStyle'; 17e650bfb3S猫头猫import ThemeText from './themeText'; 18e650bfb3S猫头猫import {useNavigation} from '@react-navigation/native'; 19e650bfb3S猫头猫import Animated, { 20e650bfb3S猫头猫 Easing, 21e650bfb3S猫头猫 useAnimatedStyle, 22e650bfb3S猫头猫 useSharedValue, 23e650bfb3S猫头猫 withTiming, 24e650bfb3S猫头猫} from 'react-native-reanimated'; 25e650bfb3S猫头猫import Portal from './portal'; 261119c2eaS猫头猫import ListItem from './listItem'; 275589cdf3S猫头猫import {IIconName} from '@/components/base/icon.tsx'; 28e650bfb3S猫头猫 29e650bfb3S猫头猫interface IAppBarProps { 30e650bfb3S猫头猫 titleTextOpacity?: number; 31e650bfb3S猫头猫 withStatusBar?: boolean; 32e650bfb3S猫头猫 color?: string; 33e650bfb3S猫头猫 actions?: Array<{ 345589cdf3S猫头猫 icon: IIconName; 35e650bfb3S猫头猫 onPress?: () => void; 36e650bfb3S猫头猫 }>; 37e650bfb3S猫头猫 menu?: Array<{ 385589cdf3S猫头猫 icon: IIconName; 39e650bfb3S猫头猫 title: string; 407a8d024eS猫头猫 show?: boolean; 41e650bfb3S猫头猫 onPress?: () => void; 42e650bfb3S猫头猫 }>; 43e650bfb3S猫头猫 menuWithStatusBar?: boolean; 44e650bfb3S猫头猫 children?: string | ReactNode; 451119c2eaS猫头猫 containerStyle?: StyleProp<ViewStyle>; 461119c2eaS猫头猫 contentStyle?: StyleProp<ViewStyle>; 47a27adc20S猫头猫 actionComponent?: ReactNode; 48*da2a2959S猫头猫 onBackPress?: () => void; 49e650bfb3S猫头猫} 50e650bfb3S猫头猫 51e650bfb3S猫头猫const ANIMATION_EASING: Animated.EasingFunction = Easing.out(Easing.exp); 52e650bfb3S猫头猫const ANIMATION_DURATION = 500; 53e650bfb3S猫头猫 54e650bfb3S猫头猫const timingConfig = { 55e650bfb3S猫头猫 duration: ANIMATION_DURATION, 56e650bfb3S猫头猫 easing: ANIMATION_EASING, 57e650bfb3S猫头猫}; 58e650bfb3S猫头猫 59e650bfb3S猫头猫export default function AppBar(props: IAppBarProps) { 60e650bfb3S猫头猫 const { 61e650bfb3S猫头猫 titleTextOpacity = 1, 62e650bfb3S猫头猫 withStatusBar, 63e650bfb3S猫头猫 color: _color, 64e650bfb3S猫头猫 actions = [], 65e650bfb3S猫头猫 menu = [], 66e650bfb3S猫头猫 menuWithStatusBar = true, 671119c2eaS猫头猫 containerStyle, 681119c2eaS猫头猫 contentStyle, 69e650bfb3S猫头猫 children, 70a27adc20S猫头猫 actionComponent, 71*da2a2959S猫头猫 onBackPress, 72e650bfb3S猫头猫 } = props; 73e650bfb3S猫头猫 74e650bfb3S猫头猫 const colors = useColors(); 75e650bfb3S猫头猫 const navigation = useNavigation(); 76e650bfb3S猫头猫 77f2a4767cS猫头猫 const bgColor = color(colors.appBar ?? colors.primary).toString(); 78277c5280S猫头猫 const contentColor = _color ?? colors.appBarText; 79e650bfb3S猫头猫 80e650bfb3S猫头猫 const [showMenu, setShowMenu] = useState(false); 81bcc58947S猫头猫 const [menuIconLayout, setMenuIconLayout] = 82bcc58947S猫头猫 useState<LayoutRectangle | null>(null); 83e650bfb3S猫头猫 const scaleRate = useSharedValue(0); 84e650bfb3S猫头猫 85e650bfb3S猫头猫 useEffect(() => { 86e650bfb3S猫头猫 if (showMenu) { 87e650bfb3S猫头猫 scaleRate.value = withTiming(1, timingConfig); 88e650bfb3S猫头猫 } else { 89e650bfb3S猫头猫 scaleRate.value = withTiming(0, timingConfig); 90e650bfb3S猫头猫 } 91e650bfb3S猫头猫 }, [showMenu]); 92e650bfb3S猫头猫 93e650bfb3S猫头猫 const transformStyle = useAnimatedStyle(() => { 94e650bfb3S猫头猫 return { 95e650bfb3S猫头猫 opacity: scaleRate.value, 96e650bfb3S猫头猫 }; 97e650bfb3S猫头猫 }); 98e650bfb3S猫头猫 99e650bfb3S猫头猫 return ( 100e650bfb3S猫头猫 <> 101e650bfb3S猫头猫 {withStatusBar ? <StatusBar backgroundColor={bgColor} /> : null} 1021119c2eaS猫头猫 <View 1031119c2eaS猫头猫 style={[ 1041119c2eaS猫头猫 styles.container, 1051119c2eaS猫头猫 containerStyle, 1061119c2eaS猫头猫 {backgroundColor: bgColor}, 1071119c2eaS猫头猫 ]}> 108e650bfb3S猫头猫 <IconButton 109e650bfb3S猫头猫 name="arrow-left" 110e650bfb3S猫头猫 sizeType="normal" 111e650bfb3S猫头猫 color={contentColor} 112e650bfb3S猫头猫 style={globalStyle.notShrink} 113*da2a2959S猫头猫 onPress={ 114*da2a2959S猫头猫 onBackPress || 115*da2a2959S猫头猫 (() => { 116e650bfb3S猫头猫 navigation.goBack(); 117*da2a2959S猫头猫 }) 118*da2a2959S猫头猫 } 119e650bfb3S猫头猫 /> 1201119c2eaS猫头猫 <View style={[globalStyle.grow, styles.content, contentStyle]}> 121e650bfb3S猫头猫 {typeof children === 'string' ? ( 122e650bfb3S猫头猫 <ThemeText 123e650bfb3S猫头猫 fontSize="title" 124e650bfb3S猫头猫 fontWeight="bold" 125e650bfb3S猫头猫 numberOfLines={1} 126e650bfb3S猫头猫 color={ 127e650bfb3S猫头猫 titleTextOpacity !== 1 128e650bfb3S猫头猫 ? color(contentColor) 129e650bfb3S猫头猫 .alpha(titleTextOpacity) 130e650bfb3S猫头猫 .toString() 131e650bfb3S猫头猫 : contentColor 132e650bfb3S猫头猫 }> 133e650bfb3S猫头猫 {children} 134e650bfb3S猫头猫 </ThemeText> 135e650bfb3S猫头猫 ) : ( 136e650bfb3S猫头猫 children 137e650bfb3S猫头猫 )} 138e650bfb3S猫头猫 </View> 139f643c584S猫头猫 {actions.map((action, index) => ( 140e650bfb3S猫头猫 <IconButton 141f643c584S猫头猫 key={index} 142e650bfb3S猫头猫 name={action.icon} 143e650bfb3S猫头猫 sizeType="normal" 144e650bfb3S猫头猫 color={contentColor} 145e650bfb3S猫头猫 style={[globalStyle.notShrink, styles.rightButton]} 146e650bfb3S猫头猫 onPress={action.onPress} 147e650bfb3S猫头猫 /> 148e650bfb3S猫头猫 ))} 149a27adc20S猫头猫 {actionComponent ?? null} 150e650bfb3S猫头猫 {menu?.length ? ( 151e650bfb3S猫头猫 <IconButton 1525589cdf3S猫头猫 name="ellipsis-vertical" 153e650bfb3S猫头猫 sizeType="normal" 1545589cdf3S猫头猫 onLayout={evt => { 1555589cdf3S猫头猫 setMenuIconLayout(evt.nativeEvent.layout); 156e650bfb3S猫头猫 }} 157e650bfb3S猫头猫 color={contentColor} 158e650bfb3S猫头猫 style={[globalStyle.notShrink, styles.rightButton]} 159e650bfb3S猫头猫 onPress={() => { 160e650bfb3S猫头猫 setShowMenu(true); 161e650bfb3S猫头猫 }} 162e650bfb3S猫头猫 /> 163e650bfb3S猫头猫 ) : null} 164e650bfb3S猫头猫 </View> 165e650bfb3S猫头猫 <Portal> 166e650bfb3S猫头猫 {showMenu ? ( 167e650bfb3S猫头猫 <TouchableWithoutFeedback 168e650bfb3S猫头猫 onPress={() => { 169e650bfb3S猫头猫 setShowMenu(false); 170e650bfb3S猫头猫 }}> 171e650bfb3S猫头猫 <View style={styles.blocker} /> 172e650bfb3S猫头猫 </TouchableWithoutFeedback> 173e650bfb3S猫头猫 ) : null} 174e650bfb3S猫头猫 <> 175e650bfb3S猫头猫 <Animated.View 176e650bfb3S猫头猫 pointerEvents={showMenu ? 'auto' : 'none'} 177e650bfb3S猫头猫 style={[ 178e650bfb3S猫头猫 { 179ab55f125S猫头猫 borderBottomColor: colors.background, 180e650bfb3S猫头猫 left: 181bcc58947S猫头猫 (menuIconLayout?.x ?? 0) + 182bcc58947S猫头猫 (menuIconLayout?.width ?? 0) / 2 - 183e650bfb3S猫头猫 rpx(10), 184e650bfb3S猫头猫 top: 185bcc58947S猫头猫 (menuIconLayout?.y ?? 0) + 186bcc58947S猫头猫 (menuIconLayout?.height ?? 0) + 187e650bfb3S猫头猫 (menuWithStatusBar 188e650bfb3S猫头猫 ? OriginalStatusBar.currentHeight ?? 0 189e650bfb3S猫头猫 : 0), 190e650bfb3S猫头猫 }, 191e650bfb3S猫头猫 transformStyle, 192e650bfb3S猫头猫 styles.bubbleCorner, 193e650bfb3S猫头猫 ]} 194e650bfb3S猫头猫 /> 195e650bfb3S猫头猫 <Animated.View 196e650bfb3S猫头猫 pointerEvents={showMenu ? 'auto' : 'none'} 197e650bfb3S猫头猫 style={[ 198e650bfb3S猫头猫 { 1996cfecf1cS猫头猫 backgroundColor: colors.background, 200e650bfb3S猫头猫 right: rpx(24), 201e650bfb3S猫头猫 top: 202bcc58947S猫头猫 (menuIconLayout?.y ?? 0) + 203bcc58947S猫头猫 (menuIconLayout?.height ?? 0) + 204e650bfb3S猫头猫 rpx(20) + 205e650bfb3S猫头猫 (menuWithStatusBar 206e650bfb3S猫头猫 ? OriginalStatusBar.currentHeight ?? 0 207e650bfb3S猫头猫 : 0), 2086cfecf1cS猫头猫 shadowColor: colors.shadow, 209e650bfb3S猫头猫 }, 210e650bfb3S猫头猫 transformStyle, 211e650bfb3S猫头猫 styles.menu, 212e650bfb3S猫头猫 ]}> 2137a8d024eS猫头猫 {menu.map(it => 2147a8d024eS猫头猫 it.show !== false ? ( 2151119c2eaS猫头猫 <ListItem 216f643c584S猫头猫 key={it.title} 2175589cdf3S猫头猫 withHorizontalPadding 2181119c2eaS猫头猫 heightType="small" 219e650bfb3S猫头猫 onPress={() => { 220e650bfb3S猫头猫 setShowMenu(false); 221740e3947S猫头猫 // async 222740e3947S猫头猫 setTimeout(() => { 223740e3947S猫头猫 it.onPress?.(); 224740e3947S猫头猫 }, 20); 225e650bfb3S猫头猫 }}> 2261119c2eaS猫头猫 <ListItem.ListItemIcon icon={it.icon} /> 2271119c2eaS猫头猫 <ListItem.Content title={it.title} /> 2281119c2eaS猫头猫 </ListItem> 2297a8d024eS猫头猫 ) : null, 2307a8d024eS猫头猫 )} 231e650bfb3S猫头猫 </Animated.View> 232e650bfb3S猫头猫 </> 233e650bfb3S猫头猫 </Portal> 234e650bfb3S猫头猫 </> 235e650bfb3S猫头猫 ); 236e650bfb3S猫头猫} 237e650bfb3S猫头猫 238e650bfb3S猫头猫const styles = StyleSheet.create({ 239e650bfb3S猫头猫 container: { 240e650bfb3S猫头猫 width: '100%', 241e650bfb3S猫头猫 zIndex: 10000, 242e650bfb3S猫头猫 height: rpx(88), 243e650bfb3S猫头猫 flexDirection: 'row', 2445589cdf3S猫头猫 alignItems: 'center', 245e650bfb3S猫头猫 paddingHorizontal: rpx(24), 246e650bfb3S猫头猫 }, 247e650bfb3S猫头猫 content: { 248e650bfb3S猫头猫 flexDirection: 'row', 249e650bfb3S猫头猫 flexBasis: 0, 250e650bfb3S猫头猫 alignItems: 'center', 251e650bfb3S猫头猫 paddingHorizontal: rpx(24), 252e650bfb3S猫头猫 }, 253e650bfb3S猫头猫 rightButton: { 254e650bfb3S猫头猫 marginLeft: rpx(28), 255e650bfb3S猫头猫 }, 256e650bfb3S猫头猫 blocker: { 257e650bfb3S猫头猫 position: 'absolute', 258e650bfb3S猫头猫 bottom: 0, 259e650bfb3S猫头猫 left: 0, 260e650bfb3S猫头猫 width: '100%', 261e650bfb3S猫头猫 height: '100%', 262e650bfb3S猫头猫 zIndex: 10010, 263e650bfb3S猫头猫 }, 264e650bfb3S猫头猫 bubbleCorner: { 265e650bfb3S猫头猫 position: 'absolute', 266e650bfb3S猫头猫 borderColor: 'transparent', 267e650bfb3S猫头猫 borderWidth: rpx(10), 268e650bfb3S猫头猫 zIndex: 10012, 269e650bfb3S猫头猫 transformOrigin: 'right top', 270e650bfb3S猫头猫 opacity: 0, 271e650bfb3S猫头猫 }, 272e650bfb3S猫头猫 menu: { 273e650bfb3S猫头猫 width: rpx(340), 274e650bfb3S猫头猫 maxHeight: rpx(600), 275e650bfb3S猫头猫 borderRadius: rpx(8), 276e650bfb3S猫头猫 zIndex: 10011, 277e650bfb3S猫头猫 position: 'absolute', 278e650bfb3S猫头猫 opacity: 0, 279e650bfb3S猫头猫 shadowOffset: { 280e650bfb3S猫头猫 width: 0, 281e650bfb3S猫头猫 height: 2, 282e650bfb3S猫头猫 }, 283e650bfb3S猫头猫 shadowOpacity: 0.23, 284e650bfb3S猫头猫 shadowRadius: 2.62, 285e650bfb3S猫头猫 elevation: 4, 286e650bfb3S猫头猫 }, 287e650bfb3S猫头猫}); 288