xref: /MusicFree/src/components/base/appBar.tsx (revision da2a2959bbf8b96423ead2452a5f60867d05a8b3)
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