|
| 1 | +import React from 'react'; |
| 2 | +import { |
| 3 | + StyleSheet, |
| 4 | + View, |
| 5 | + Text, |
| 6 | + Pressable, |
| 7 | + SafeAreaView, |
| 8 | + ActivityIndicator, |
| 9 | + Platform, |
| 10 | + StatusBar, |
| 11 | + StyleProp, |
| 12 | +} from 'react-native'; |
| 13 | +import Icon from '../Icon'; |
| 14 | +import MaskLayer from '../MaskLayer'; |
| 15 | + |
| 16 | +export interface ScreenRootProps { |
| 17 | + /** |
| 18 | + * 无需头部导航栏 |
| 19 | + */ |
| 20 | + noHeader?: boolean; |
| 21 | + /** |
| 22 | + * modal根结点样式,有时需要 |
| 23 | + */ |
| 24 | + rootStyle?: StyleProp<any>; |
| 25 | + /** |
| 26 | + * 导航栏中间标题,支持字符串和ReactNode |
| 27 | + */ |
| 28 | + title?: string | React.ReactNode; |
| 29 | + // 导航栏左侧显示 |
| 30 | + headerLeft?: React.ReactNode; |
| 31 | + // 导航栏右侧显示 |
| 32 | + headerRight?: React.ReactNode; |
| 33 | + // 导航栏整体样式 |
| 34 | + headerStyle?: StyleProp<any>; |
| 35 | + // 导航栏右侧显示样式 |
| 36 | + headerRightStyle?: StyleProp<any>; |
| 37 | + // 页面内容样式 |
| 38 | + containerStyle?: StyleProp<any>; |
| 39 | + loading?: boolean; |
| 40 | + // 默认左侧图标点击事件 |
| 41 | + onBack?: () => void; |
| 42 | + // 图标颜色 |
| 43 | + iconColor?: string; |
| 44 | + // screen背景色 |
| 45 | + screenBackgroundColor?: string; |
| 46 | + // modal背景色 |
| 47 | + modalBackgroundColor?: string; |
| 48 | + // 页面导航类型,screen modal |
| 49 | + pageType?: 'screen' | 'modal'; |
| 50 | + children?: React.ReactNode; |
| 51 | + barStyle?: 'default' | 'light-content' | 'dark-content'; |
| 52 | +} |
| 53 | + |
| 54 | +const ScreenRoot = (props: ScreenRootProps) => { |
| 55 | + const { |
| 56 | + // 无需头部导航栏 |
| 57 | + noHeader = false, |
| 58 | + // modal根结点样式,有时需要 |
| 59 | + rootStyle = {}, |
| 60 | + // 导航栏中间标题,支持字符串和ReactNode |
| 61 | + title = '', |
| 62 | + // 导航栏左侧显示 |
| 63 | + headerLeft = null, |
| 64 | + // 导航栏右侧显示 |
| 65 | + headerRight = null, |
| 66 | + // 导航栏整体样式 |
| 67 | + headerStyle = {}, |
| 68 | + // 页面内容样式 |
| 69 | + containerStyle = {}, |
| 70 | + loading = false, |
| 71 | + // 默认左侧图标点击事件 |
| 72 | + onBack = null, |
| 73 | + iconColor = '#333333', |
| 74 | + screenBackgroundColor = '#f9f9f9', |
| 75 | + modalBackgroundColor = '#fff', |
| 76 | + // 页面导航类型,screen modal |
| 77 | + pageType = 'screen', |
| 78 | + children = null, |
| 79 | + // 状态栏主题 default light-content dark-content |
| 80 | + barStyle = 'dark-content', |
| 81 | + headerRightStyle = {}, |
| 82 | + } = props; |
| 83 | + |
| 84 | + const isScreen = pageType === 'screen'; |
| 85 | + const isIOS = Platform.OS === 'ios'; |
| 86 | + |
| 87 | + const bgc = isScreen ? screenBackgroundColor : modalBackgroundColor; |
| 88 | + |
| 89 | + // 页面左侧 |
| 90 | + const pageHeaderLeft = ( |
| 91 | + <Pressable style={styles.headerLeft} onPress={() => onBack && onBack()}> |
| 92 | + <Icon |
| 93 | + xml={` |
| 94 | + <svg xmlns="http://www.w3.org/2000/svg" width="11" height="18" viewBox="0 0 11 18"> |
| 95 | + <polyline fill="none" stroke="${iconColor}" stroke-width="2" points="26.871 58 19 65.935 26.871 73.935" transform="translate(-17 -57)"/> |
| 96 | + </svg> |
| 97 | + `} |
| 98 | + size={18} |
| 99 | + /> |
| 100 | + </Pressable> |
| 101 | + ); |
| 102 | + |
| 103 | + // modal弹框左侧 |
| 104 | + const modalHeaderLeft = ( |
| 105 | + <Pressable style={styles.headerLeft} onPress={() => onBack && onBack()}> |
| 106 | + <Icon name="close" size={16} color={iconColor} /> |
| 107 | + </Pressable> |
| 108 | + ); |
| 109 | + |
| 110 | + const renderHeader = () => { |
| 111 | + let centerContainer = null; |
| 112 | + if (typeof title === 'string') { |
| 113 | + centerContainer = ( |
| 114 | + <Text style={styles.title} numberOfLines={1}> |
| 115 | + {title} |
| 116 | + </Text> |
| 117 | + ); |
| 118 | + } else { |
| 119 | + centerContainer = title; |
| 120 | + } |
| 121 | + if (!noHeader) { |
| 122 | + return ( |
| 123 | + <View |
| 124 | + style={[ |
| 125 | + styles.header, |
| 126 | + { |
| 127 | + backgroundColor: bgc, |
| 128 | + }, |
| 129 | + isScreen ? { elevation: 4 } : {}, |
| 130 | + isIOS ? styles.iosHeader : '', |
| 131 | + headerStyle, |
| 132 | + ]} |
| 133 | + > |
| 134 | + <View style={styles.leftContainer}> |
| 135 | + {headerLeft ? headerLeft : isScreen ? pageHeaderLeft : modalHeaderLeft} |
| 136 | + </View> |
| 137 | + <View style={styles.centerContainer}>{centerContainer}</View> |
| 138 | + <View style={[styles.rightContainer, headerRightStyle]}>{headerRight}</View> |
| 139 | + </View> |
| 140 | + ); |
| 141 | + } |
| 142 | + return null; |
| 143 | + }; |
| 144 | + |
| 145 | + return ( |
| 146 | + <SafeAreaView style={[isScreen ? {} : { backgroundColor: modalBackgroundColor, ...rootStyle }, { flex: 1 }]}> |
| 147 | + <StatusBar barStyle={barStyle} backgroundColor="red" translucent={true} animated={true} /> |
| 148 | + {loading && ( |
| 149 | + <MaskLayer visible={loading} opacity={0.3}> |
| 150 | + <ActivityIndicator color="#333" size="large" style={{ marginTop: 300 }} /> |
| 151 | + </MaskLayer> |
| 152 | + )} |
| 153 | + {renderHeader()} |
| 154 | + <View |
| 155 | + style={[ |
| 156 | + { |
| 157 | + flex: 1, |
| 158 | + backgroundColor: bgc, |
| 159 | + }, |
| 160 | + containerStyle, |
| 161 | + ]} |
| 162 | + > |
| 163 | + {children} |
| 164 | + </View> |
| 165 | + </SafeAreaView> |
| 166 | + ); |
| 167 | +}; |
| 168 | + |
| 169 | +const styles = StyleSheet.create({ |
| 170 | + modalRootStyle: { |
| 171 | + backgroundColor: '#fff', |
| 172 | + }, |
| 173 | + header: { |
| 174 | + height: 66, |
| 175 | + flexDirection: 'row', |
| 176 | + alignItems: 'center', |
| 177 | + paddingTop: 22, |
| 178 | + }, |
| 179 | + iosHeader: { |
| 180 | + height: 44, |
| 181 | + paddingTop: 0, |
| 182 | + }, |
| 183 | + leftContainer: { |
| 184 | + flex: 1, |
| 185 | + width: 60, |
| 186 | + paddingLeft: 10, |
| 187 | + flexDirection: 'row', |
| 188 | + }, |
| 189 | + headerLeft: { |
| 190 | + flex: 1, |
| 191 | + justifyContent: 'center', |
| 192 | + }, |
| 193 | + centerContainer: { |
| 194 | + flex: 4, |
| 195 | + justifyContent: 'center', |
| 196 | + alignItems: 'center', |
| 197 | + }, |
| 198 | + title: { |
| 199 | + color: '#333', |
| 200 | + fontSize: 16, |
| 201 | + fontWeight: '600', |
| 202 | + }, |
| 203 | + rightContainer: { |
| 204 | + flex: 1, |
| 205 | + width: 60, |
| 206 | + paddingRight: 10, |
| 207 | + flexDirection: 'row', |
| 208 | + }, |
| 209 | +}); |
| 210 | + |
| 211 | +export default ScreenRoot; |
0 commit comments