xref: /MusicFree/src/components/base/appBar.tsx (revision e650bfb34226e2a09d15cbf7832c4805a87cd60e)
1*e650bfb3S猫头猫import React, {ReactNode, useEffect, useRef, useState} from 'react';
2*e650bfb3S猫头猫import {
3*e650bfb3S猫头猫    LayoutRectangle,
4*e650bfb3S猫头猫    StyleSheet,
5*e650bfb3S猫头猫    TouchableWithoutFeedback,
6*e650bfb3S猫头猫    View,
7*e650bfb3S猫头猫    StatusBar as OriginalStatusBar,
8*e650bfb3S猫头猫} from 'react-native';
9*e650bfb3S猫头猫import rpx from '@/utils/rpx';
10*e650bfb3S猫头猫import useColors from '@/hooks/useColors';
11*e650bfb3S猫头猫import StatusBar from './statusBar';
12*e650bfb3S猫头猫import color from 'color';
13*e650bfb3S猫头猫import IconButton from './iconButton';
14*e650bfb3S猫头猫import globalStyle from '@/constants/globalStyle';
15*e650bfb3S猫头猫import ThemeText from './themeText';
16*e650bfb3S猫头猫import {useNavigation} from '@react-navigation/native';
17*e650bfb3S猫头猫import Animated, {
18*e650bfb3S猫头猫    Easing,
19*e650bfb3S猫头猫    useAnimatedStyle,
20*e650bfb3S猫头猫    useSharedValue,
21*e650bfb3S猫头猫    withTiming,
22*e650bfb3S猫头猫} from 'react-native-reanimated';
23*e650bfb3S猫头猫import Portal from './portal';
24*e650bfb3S猫头猫import IconTextButton from './iconTextButton';
25*e650bfb3S猫头猫
26*e650bfb3S猫头猫interface IAppBarProps {
27*e650bfb3S猫头猫    backgroundOpacity?: number;
28*e650bfb3S猫头猫    titleTextOpacity?: number;
29*e650bfb3S猫头猫    withStatusBar?: boolean;
30*e650bfb3S猫头猫    color?: string;
31*e650bfb3S猫头猫    actions?: Array<{
32*e650bfb3S猫头猫        icon: string;
33*e650bfb3S猫头猫        onPress?: () => void;
34*e650bfb3S猫头猫    }>;
35*e650bfb3S猫头猫    menu?: Array<{
36*e650bfb3S猫头猫        icon: string;
37*e650bfb3S猫头猫        title: string;
38*e650bfb3S猫头猫        onPress?: () => void;
39*e650bfb3S猫头猫    }>;
40*e650bfb3S猫头猫    menuWithStatusBar?: boolean;
41*e650bfb3S猫头猫    children?: string | ReactNode;
42*e650bfb3S猫头猫}
43*e650bfb3S猫头猫
44*e650bfb3S猫头猫const ANIMATION_EASING: Animated.EasingFunction = Easing.out(Easing.exp);
45*e650bfb3S猫头猫const ANIMATION_DURATION = 500;
46*e650bfb3S猫头猫
47*e650bfb3S猫头猫const timingConfig = {
48*e650bfb3S猫头猫    duration: ANIMATION_DURATION,
49*e650bfb3S猫头猫    easing: ANIMATION_EASING,
50*e650bfb3S猫头猫};
51*e650bfb3S猫头猫
52*e650bfb3S猫头猫export default function AppBar(props: IAppBarProps) {
53*e650bfb3S猫头猫    const {
54*e650bfb3S猫头猫        backgroundOpacity = 1,
55*e650bfb3S猫头猫        titleTextOpacity = 1,
56*e650bfb3S猫头猫        withStatusBar,
57*e650bfb3S猫头猫        color: _color,
58*e650bfb3S猫头猫        actions = [],
59*e650bfb3S猫头猫        menu = [],
60*e650bfb3S猫头猫        menuWithStatusBar = true,
61*e650bfb3S猫头猫        children,
62*e650bfb3S猫头猫    } = props;
63*e650bfb3S猫头猫
64*e650bfb3S猫头猫    const colors = useColors();
65*e650bfb3S猫头猫    const navigation = useNavigation();
66*e650bfb3S猫头猫
67*e650bfb3S猫头猫    const bgColor = color(colors.primary).alpha(backgroundOpacity).toString();
68*e650bfb3S猫头猫    const contentColor = _color ?? colors.headerText;
69*e650bfb3S猫头猫
70*e650bfb3S猫头猫    const [showMenu, setShowMenu] = useState(false);
71*e650bfb3S猫头猫    const menuIconLayoutRef = useRef<LayoutRectangle>();
72*e650bfb3S猫头猫    const scaleRate = useSharedValue(0);
73*e650bfb3S猫头猫
74*e650bfb3S猫头猫    useEffect(() => {
75*e650bfb3S猫头猫        if (showMenu) {
76*e650bfb3S猫头猫            scaleRate.value = withTiming(1, timingConfig);
77*e650bfb3S猫头猫        } else {
78*e650bfb3S猫头猫            scaleRate.value = withTiming(0, timingConfig);
79*e650bfb3S猫头猫        }
80*e650bfb3S猫头猫    }, [showMenu]);
81*e650bfb3S猫头猫
82*e650bfb3S猫头猫    const transformStyle = useAnimatedStyle(() => {
83*e650bfb3S猫头猫        return {
84*e650bfb3S猫头猫            opacity: scaleRate.value,
85*e650bfb3S猫头猫        };
86*e650bfb3S猫头猫    });
87*e650bfb3S猫头猫
88*e650bfb3S猫头猫    return (
89*e650bfb3S猫头猫        <>
90*e650bfb3S猫头猫            {withStatusBar ? <StatusBar backgroundColor={bgColor} /> : null}
91*e650bfb3S猫头猫            <View style={[styles.container, {backgroundColor: bgColor}]}>
92*e650bfb3S猫头猫                <IconButton
93*e650bfb3S猫头猫                    name="arrow-left"
94*e650bfb3S猫头猫                    sizeType="normal"
95*e650bfb3S猫头猫                    color={contentColor}
96*e650bfb3S猫头猫                    style={globalStyle.notShrink}
97*e650bfb3S猫头猫                    onPress={() => {
98*e650bfb3S猫头猫                        navigation.goBack();
99*e650bfb3S猫头猫                    }}
100*e650bfb3S猫头猫                />
101*e650bfb3S猫头猫                <View style={[globalStyle.grow, styles.content]}>
102*e650bfb3S猫头猫                    {typeof children === 'string' ? (
103*e650bfb3S猫头猫                        <ThemeText
104*e650bfb3S猫头猫                            fontSize="title"
105*e650bfb3S猫头猫                            fontWeight="bold"
106*e650bfb3S猫头猫                            numberOfLines={1}
107*e650bfb3S猫头猫                            color={
108*e650bfb3S猫头猫                                titleTextOpacity !== 1
109*e650bfb3S猫头猫                                    ? color(contentColor)
110*e650bfb3S猫头猫                                          .alpha(titleTextOpacity)
111*e650bfb3S猫头猫                                          .toString()
112*e650bfb3S猫头猫                                    : contentColor
113*e650bfb3S猫头猫                            }>
114*e650bfb3S猫头猫                            {children}
115*e650bfb3S猫头猫                        </ThemeText>
116*e650bfb3S猫头猫                    ) : (
117*e650bfb3S猫头猫                        children
118*e650bfb3S猫头猫                    )}
119*e650bfb3S猫头猫                </View>
120*e650bfb3S猫头猫                {actions.map(action => (
121*e650bfb3S猫头猫                    <IconButton
122*e650bfb3S猫头猫                        name={action.icon}
123*e650bfb3S猫头猫                        sizeType="normal"
124*e650bfb3S猫头猫                        color={contentColor}
125*e650bfb3S猫头猫                        style={[globalStyle.notShrink, styles.rightButton]}
126*e650bfb3S猫头猫                        onPress={action.onPress}
127*e650bfb3S猫头猫                    />
128*e650bfb3S猫头猫                ))}
129*e650bfb3S猫头猫                {menu?.length ? (
130*e650bfb3S猫头猫                    <IconButton
131*e650bfb3S猫头猫                        name="dots-vertical"
132*e650bfb3S猫头猫                        sizeType="normal"
133*e650bfb3S猫头猫                        onLayout={e => {
134*e650bfb3S猫头猫                            menuIconLayoutRef.current = e.nativeEvent.layout;
135*e650bfb3S猫头猫                        }}
136*e650bfb3S猫头猫                        color={contentColor}
137*e650bfb3S猫头猫                        style={[globalStyle.notShrink, styles.rightButton]}
138*e650bfb3S猫头猫                        onPress={() => {
139*e650bfb3S猫头猫                            setShowMenu(true);
140*e650bfb3S猫头猫                        }}
141*e650bfb3S猫头猫                    />
142*e650bfb3S猫头猫                ) : null}
143*e650bfb3S猫头猫            </View>
144*e650bfb3S猫头猫            <Portal>
145*e650bfb3S猫头猫                {showMenu ? (
146*e650bfb3S猫头猫                    <TouchableWithoutFeedback
147*e650bfb3S猫头猫                        onPress={() => {
148*e650bfb3S猫头猫                            setShowMenu(false);
149*e650bfb3S猫头猫                        }}>
150*e650bfb3S猫头猫                        <View style={styles.blocker} />
151*e650bfb3S猫头猫                    </TouchableWithoutFeedback>
152*e650bfb3S猫头猫                ) : null}
153*e650bfb3S猫头猫                <>
154*e650bfb3S猫头猫                    <Animated.View
155*e650bfb3S猫头猫                        pointerEvents={showMenu ? 'auto' : 'none'}
156*e650bfb3S猫头猫                        style={[
157*e650bfb3S猫头猫                            {
158*e650bfb3S猫头猫                                borderBottomColor: colors.backdrop,
159*e650bfb3S猫头猫                                left:
160*e650bfb3S猫头猫                                    (menuIconLayoutRef.current?.x ?? 0) +
161*e650bfb3S猫头猫                                    (menuIconLayoutRef.current?.width ?? 0) /
162*e650bfb3S猫头猫                                        2 -
163*e650bfb3S猫头猫                                    rpx(10),
164*e650bfb3S猫头猫                                top:
165*e650bfb3S猫头猫                                    (menuIconLayoutRef.current?.y ?? 0) +
166*e650bfb3S猫头猫                                    (menuIconLayoutRef.current?.height ?? 0) +
167*e650bfb3S猫头猫                                    (menuWithStatusBar
168*e650bfb3S猫头猫                                        ? OriginalStatusBar.currentHeight ?? 0
169*e650bfb3S猫头猫                                        : 0),
170*e650bfb3S猫头猫                            },
171*e650bfb3S猫头猫                            transformStyle,
172*e650bfb3S猫头猫                            styles.bubbleCorner,
173*e650bfb3S猫头猫                        ]}
174*e650bfb3S猫头猫                    />
175*e650bfb3S猫头猫                    <Animated.View
176*e650bfb3S猫头猫                        pointerEvents={showMenu ? 'auto' : 'none'}
177*e650bfb3S猫头猫                        style={[
178*e650bfb3S猫头猫                            {
179*e650bfb3S猫头猫                                backgroundColor: colors.headerText,
180*e650bfb3S猫头猫                                right: rpx(24),
181*e650bfb3S猫头猫                                top:
182*e650bfb3S猫头猫                                    (menuIconLayoutRef.current?.y ?? 0) +
183*e650bfb3S猫头猫                                    (menuIconLayoutRef.current?.height ?? 0) +
184*e650bfb3S猫头猫                                    rpx(20) +
185*e650bfb3S猫头猫                                    (menuWithStatusBar
186*e650bfb3S猫头猫                                        ? OriginalStatusBar.currentHeight ?? 0
187*e650bfb3S猫头猫                                        : 0),
188*e650bfb3S猫头猫                            },
189*e650bfb3S猫头猫                            transformStyle,
190*e650bfb3S猫头猫                            styles.menu,
191*e650bfb3S猫头猫                        ]}>
192*e650bfb3S猫头猫                        {menu.map(it => (
193*e650bfb3S猫头猫                            <IconTextButton
194*e650bfb3S猫头猫                                icon={it.icon}
195*e650bfb3S猫头猫                                onPress={() => {
196*e650bfb3S猫头猫                                    it.onPress?.();
197*e650bfb3S猫头猫                                    setShowMenu(false);
198*e650bfb3S猫头猫                                }}>
199*e650bfb3S猫头猫                                {it.title}
200*e650bfb3S猫头猫                            </IconTextButton>
201*e650bfb3S猫头猫                        ))}
202*e650bfb3S猫头猫                    </Animated.View>
203*e650bfb3S猫头猫                </>
204*e650bfb3S猫头猫            </Portal>
205*e650bfb3S猫头猫        </>
206*e650bfb3S猫头猫    );
207*e650bfb3S猫头猫}
208*e650bfb3S猫头猫
209*e650bfb3S猫头猫const styles = StyleSheet.create({
210*e650bfb3S猫头猫    container: {
211*e650bfb3S猫头猫        width: '100%',
212*e650bfb3S猫头猫        zIndex: 10000,
213*e650bfb3S猫头猫        height: rpx(88),
214*e650bfb3S猫头猫        flexDirection: 'row',
215*e650bfb3S猫头猫        paddingHorizontal: rpx(24),
216*e650bfb3S猫头猫    },
217*e650bfb3S猫头猫    content: {
218*e650bfb3S猫头猫        flexDirection: 'row',
219*e650bfb3S猫头猫        flexBasis: 0,
220*e650bfb3S猫头猫        alignItems: 'center',
221*e650bfb3S猫头猫        paddingHorizontal: rpx(24),
222*e650bfb3S猫头猫    },
223*e650bfb3S猫头猫    rightButton: {
224*e650bfb3S猫头猫        marginLeft: rpx(28),
225*e650bfb3S猫头猫    },
226*e650bfb3S猫头猫    blocker: {
227*e650bfb3S猫头猫        position: 'absolute',
228*e650bfb3S猫头猫        bottom: 0,
229*e650bfb3S猫头猫        left: 0,
230*e650bfb3S猫头猫        width: '100%',
231*e650bfb3S猫头猫        height: '100%',
232*e650bfb3S猫头猫        zIndex: 10010,
233*e650bfb3S猫头猫    },
234*e650bfb3S猫头猫    bubbleCorner: {
235*e650bfb3S猫头猫        position: 'absolute',
236*e650bfb3S猫头猫        borderColor: 'transparent',
237*e650bfb3S猫头猫        borderWidth: rpx(10),
238*e650bfb3S猫头猫        zIndex: 10012,
239*e650bfb3S猫头猫        transformOrigin: 'right top',
240*e650bfb3S猫头猫        opacity: 0,
241*e650bfb3S猫头猫    },
242*e650bfb3S猫头猫    menu: {
243*e650bfb3S猫头猫        width: rpx(340),
244*e650bfb3S猫头猫        maxHeight: rpx(600),
245*e650bfb3S猫头猫        borderRadius: rpx(8),
246*e650bfb3S猫头猫        zIndex: 10011,
247*e650bfb3S猫头猫        position: 'absolute',
248*e650bfb3S猫头猫        opacity: 0,
249*e650bfb3S猫头猫        shadowColor: '#000',
250*e650bfb3S猫头猫        shadowOffset: {
251*e650bfb3S猫头猫            width: 0,
252*e650bfb3S猫头猫            height: 2,
253*e650bfb3S猫头猫        },
254*e650bfb3S猫头猫        shadowOpacity: 0.23,
255*e650bfb3S猫头猫        shadowRadius: 2.62,
256*e650bfb3S猫头猫        elevation: 4,
257*e650bfb3S猫头猫    },
258*e650bfb3S猫头猫});
259