xref: /MusicFree/src/components/base/SortableFlatList.tsx (revision 5589cdf32b2bb0f641e5ac7bf1f6152cd6b9b70e)
14de3fbc9S猫头猫/**
24de3fbc9S猫头猫 * 支持长按拖拽排序的flatlist,右边加个固定的按钮,拖拽排序。
34de3fbc9S猫头猫 * 考虑到方便实现+节省性能,整个app内的拖拽排序都遵守以下实现。
44de3fbc9S猫头猫 * 点击会出现
54de3fbc9S猫头猫 */
64de3fbc9S猫头猫
73b155a65S猫头猫import globalStyle from '@/constants/globalStyle';
84de3fbc9S猫头猫import {iconSizeConst} from '@/constants/uiConst';
94de3fbc9S猫头猫import useTextColor from '@/hooks/useTextColor';
104de3fbc9S猫头猫import rpx from '@/utils/rpx';
116cb76c0cS猫头猫import {FlashList} from '@shopify/flash-list';
124de3fbc9S猫头猫import React, {
134de3fbc9S猫头猫    ForwardedRef,
144de3fbc9S猫头猫    forwardRef,
154de3fbc9S猫头猫    memo,
164de3fbc9S猫头猫    useEffect,
17ef40425aS猫头猫    useMemo,
184de3fbc9S猫头猫    useRef,
194de3fbc9S猫头猫    useState,
204de3fbc9S猫头猫} from 'react';
21ccf779d0S猫头猫import {LayoutRectangle, Pressable, StyleSheet, View} from 'react-native';
22bec1e603S猫头猫import {
23bec1e603S猫头猫    runOnJS,
24bec1e603S猫头猫    useDerivedValue,
25bec1e603S猫头猫    useSharedValue,
26bec1e603S猫头猫} from 'react-native-reanimated';
27*5589cdf3S猫头猫import Icon from '@/components/base/icon.tsx';
284de3fbc9S猫头猫
294de3fbc9S猫头猫const defaultZIndex = 10;
304de3fbc9S猫头猫
314de3fbc9S猫头猫interface ISortableFlatListProps<T> {
324de3fbc9S猫头猫    data: T[];
334de3fbc9S猫头猫    renderItem: (props: {item: T; index: number}) => JSX.Element;
344de3fbc9S猫头猫    // 高度
354de3fbc9S猫头猫    itemHeight: number;
3634588741S猫头猫    itemJustifyContent?:
3734588741S猫头猫        | 'flex-start'
3834588741S猫头猫        | 'flex-end'
3934588741S猫头猫        | 'center'
4034588741S猫头猫        | 'space-between'
4134588741S猫头猫        | 'space-around'
4234588741S猫头猫        | 'space-evenly';
434de3fbc9S猫头猫    // 滚动list距离顶部的距离, 这里写的不好
444de3fbc9S猫头猫    marginTop: number;
454de3fbc9S猫头猫    /** 拖拽时的背景色 */
464de3fbc9S猫头猫    activeBackgroundColor?: string;
474de3fbc9S猫头猫    /** 交换结束 */
484de3fbc9S猫头猫    onSortEnd?: (newData: T[]) => void;
494de3fbc9S猫头猫}
504de3fbc9S猫头猫
514de3fbc9S猫头猫export default function SortableFlatList<T extends any = any>(
524de3fbc9S猫头猫    props: ISortableFlatListProps<T>,
534de3fbc9S猫头猫) {
544de3fbc9S猫头猫    const {
554de3fbc9S猫头猫        data,
564de3fbc9S猫头猫        renderItem,
574de3fbc9S猫头猫        itemHeight,
5834588741S猫头猫        itemJustifyContent,
594de3fbc9S猫头猫        marginTop,
604de3fbc9S猫头猫        activeBackgroundColor,
614de3fbc9S猫头猫        onSortEnd,
624de3fbc9S猫头猫    } = props;
634de3fbc9S猫头猫
644de3fbc9S猫头猫    // 不要干扰原始数据
654de3fbc9S猫头猫    const [_data, _setData] = useState([...(data ?? [])]);
664de3fbc9S猫头猫    // 是否禁止滚动
674de3fbc9S猫头猫    const [scrollEnabled, setScrollEnabled] = useState(true);
684de3fbc9S猫头猫    // 是否处在激活状态, -1表示无,其他表示当前激活的下标
694de3fbc9S猫头猫    const activeRef = useRef(-1);
704de3fbc9S猫头猫    const [activeItem, setActiveItem] = useState<T | null>(null);
714de3fbc9S猫头猫
724de3fbc9S猫头猫    const layoutRef = useRef<LayoutRectangle>();
734de3fbc9S猫头猫    // listref
746cb76c0cS猫头猫    const listRef = useRef<FlashList<T> | null>(null);
754de3fbc9S猫头猫    // fakeref
764de3fbc9S猫头猫    const fakeItemRef = useRef<View | null>(null);
774de3fbc9S猫头猫    // contentoffset
789cc05961S猫头猫    const contentOffsetYRef = useRef<number>(-1);
79ef40425aS猫头猫    const targetOffsetYRef = useRef<number>(0);
80ef40425aS猫头猫
81ef40425aS猫头猫    const direction = useSharedValue(0);
824de3fbc9S猫头猫
834de3fbc9S猫头猫    useEffect(() => {
844de3fbc9S猫头猫        _setData([...(data ?? [])]);
854de3fbc9S猫头猫    }, [data]);
864de3fbc9S猫头猫
874de3fbc9S猫头猫    const initDragPageY = useRef<number>(0);
884de3fbc9S猫头猫    const initDragLocationY = useRef<number>(0);
894de3fbc9S猫头猫    const offsetRef = useRef<number>(0);
904de3fbc9S猫头猫
914de3fbc9S猫头猫    //#region 滚动
92ef40425aS猫头猫    const scrollingRef = useRef(false);
934de3fbc9S猫头猫
94ef40425aS猫头猫    // 列表整体的高度
954ddc4470S猫头猫    const listContentHeight = useMemo(
964ddc4470S猫头猫        () => itemHeight * data.length,
974ddc4470S猫头猫        [data, itemHeight],
984ddc4470S猫头猫    );
99ef40425aS猫头猫
100ef40425aS猫头猫    function scrollToTarget(forceScroll = false) {
101ef40425aS猫头猫        // 未选中
102ef40425aS猫头猫        if (activeRef.current === -1) {
103ef40425aS猫头猫            scrollingRef.current = false;
1044de3fbc9S猫头猫            return;
1054de3fbc9S猫头猫        }
106ef40425aS猫头猫
107ef40425aS猫头猫        // 滚动中就不滚了 /
108ef40425aS猫头猫        if (scrollingRef.current && !forceScroll) {
109ef40425aS猫头猫            scrollingRef.current = true;
110ef40425aS猫头猫            return;
1114de3fbc9S猫头猫        }
112ef40425aS猫头猫        // 方向是0
113ef40425aS猫头猫        if (direction.value === 0) {
114ef40425aS猫头猫            scrollingRef.current = false;
115ef40425aS猫头猫            return;
116ef40425aS猫头猫        }
117ef40425aS猫头猫
118ef40425aS猫头猫        const nextTarget =
119ef40425aS猫头猫            Math.sign(direction.value) *
120ef40425aS猫头猫                Math.max(Math.abs(direction.value), 0.3) *
121ef40425aS猫头猫                300 +
122ef40425aS猫头猫            contentOffsetYRef.current;
123ef40425aS猫头猫        // 当前到极限了
1244de3fbc9S猫头猫        if (
125ef40425aS猫头猫            (contentOffsetYRef.current <= 2 &&
126ef40425aS猫头猫                nextTarget < contentOffsetYRef.current) ||
127ef40425aS猫头猫            (contentOffsetYRef.current >=
12815a52c01S猫头猫                listContentHeight - (layoutRef.current?.height ?? 0) - 2 &&
129ef40425aS猫头猫                nextTarget > contentOffsetYRef.current)
1304de3fbc9S猫头猫        ) {
131ef40425aS猫头猫            scrollingRef.current = false;
132ef40425aS猫头猫            return;
1334de3fbc9S猫头猫        }
134ef40425aS猫头猫        scrollingRef.current = true;
135ef40425aS猫头猫        // 超出区域
136ef40425aS猫头猫        targetOffsetYRef.current = Math.min(
137ef40425aS猫头猫            Math.max(0, nextTarget),
13815a52c01S猫头猫            listContentHeight - (layoutRef.current?.height ?? 0),
139ef40425aS猫头猫        );
140ef40425aS猫头猫        listRef.current?.scrollToOffset({
141ef40425aS猫头猫            animated: true,
142ef40425aS猫头猫            offset: targetOffsetYRef.current,
143ef40425aS猫头猫        });
1444de3fbc9S猫头猫    }
1454de3fbc9S猫头猫
146ef40425aS猫头猫    useDerivedValue(() => {
147ef40425aS猫头猫        // 正在滚动
148ef40425aS猫头猫        if (scrollingRef.current) {
149ef40425aS猫头猫            return;
150ef40425aS猫头猫        } else if (direction.value !== 0) {
151ef40425aS猫头猫            // 开始滚动
152bec1e603S猫头猫            runOnJS(scrollToTarget)();
153ef40425aS猫头猫        }
1544de3fbc9S猫头猫    }, []);
1554de3fbc9S猫头猫
1564de3fbc9S猫头猫    //#endregion
1574de3fbc9S猫头猫
1584de3fbc9S猫头猫    return (
1593b155a65S猫头猫        <View style={globalStyle.fwflex1}>
1604de3fbc9S猫头猫            {/* 纯展示 */}
1614de3fbc9S猫头猫            <FakeFlatListItem
1624de3fbc9S猫头猫                ref={_ => (fakeItemRef.current = _)}
1634de3fbc9S猫头猫                backgroundColor={activeBackgroundColor}
1644de3fbc9S猫头猫                renderItem={renderItem}
1654de3fbc9S猫头猫                itemHeight={itemHeight}
1664de3fbc9S猫头猫                item={activeItem}
16734588741S猫头猫                itemJustifyContent={itemJustifyContent}
1684de3fbc9S猫头猫            />
1696cb76c0cS猫头猫            <FlashList
1704de3fbc9S猫头猫                scrollEnabled={scrollEnabled}
1714de3fbc9S猫头猫                ref={_ => {
1724de3fbc9S猫头猫                    listRef.current = _;
1734de3fbc9S猫头猫                }}
1744de3fbc9S猫头猫                onLayout={evt => {
1754de3fbc9S猫头猫                    layoutRef.current = evt.nativeEvent.layout;
1764de3fbc9S猫头猫                }}
1774de3fbc9S猫头猫                data={_data}
1786cb76c0cS猫头猫                estimatedItemSize={itemHeight}
179ef40425aS猫头猫                scrollEventThrottle={16}
1804de3fbc9S猫头猫                onTouchStart={e => {
1814de3fbc9S猫头猫                    if (activeRef.current !== -1) {
1824de3fbc9S猫头猫                        // 相对于整个页面顶部的距离
1834de3fbc9S猫头猫                        initDragPageY.current = e.nativeEvent.pageY;
1844de3fbc9S猫头猫                        initDragLocationY.current = e.nativeEvent.locationY;
1854de3fbc9S猫头猫                    }
1864de3fbc9S猫头猫                }}
1874de3fbc9S猫头猫                onTouchMove={e => {
1884de3fbc9S猫头猫                    if (activeRef.current !== -1) {
1894de3fbc9S猫头猫                        offsetRef.current =
1904de3fbc9S猫头猫                            e.nativeEvent.pageY -
1914de3fbc9S猫头猫                            (marginTop ?? layoutRef.current?.y ?? 0) -
1924de3fbc9S猫头猫                            itemHeight / 2;
1934de3fbc9S猫头猫
1944de3fbc9S猫头猫                        if (offsetRef.current < 0) {
1954de3fbc9S猫头猫                            offsetRef.current = 0;
1964de3fbc9S猫头猫                        } else if (
1974de3fbc9S猫头猫                            offsetRef.current >
1984de3fbc9S猫头猫                            (layoutRef.current?.height ?? 0) - itemHeight
1994de3fbc9S猫头猫                        ) {
2004de3fbc9S猫头猫                            offsetRef.current =
2014de3fbc9S猫头猫                                (layoutRef.current?.height ?? 0) - itemHeight;
2024de3fbc9S猫头猫                        }
2034de3fbc9S猫头猫                        fakeItemRef.current!.setNativeProps({
2044de3fbc9S猫头猫                            top: offsetRef.current,
2054de3fbc9S猫头猫                            opacity: 1,
2064de3fbc9S猫头猫                            zIndex: 100,
2074de3fbc9S猫头猫                        });
2084de3fbc9S猫头猫
2094de3fbc9S猫头猫                        // 如果超出范围,停止
2104de3fbc9S猫头猫                        if (offsetRef.current < itemHeight * 2) {
2114de3fbc9S猫头猫                            // 上滑
212ef40425aS猫头猫                            direction.value =
213ef40425aS猫头猫                                offsetRef.current / itemHeight / 2 - 1;
2144de3fbc9S猫头猫                        } else if (
2154de3fbc9S猫头猫                            offsetRef.current >
2164de3fbc9S猫头猫                            (layoutRef.current?.height ?? 0) - 3 * itemHeight
2174de3fbc9S猫头猫                        ) {
2184de3fbc9S猫头猫                            // 下滑
219ef40425aS猫头猫                            direction.value =
220ef40425aS猫头猫                                (offsetRef.current -
221ef40425aS猫头猫                                    (layoutRef.current?.height ?? 0) +
222ef40425aS猫头猫                                    3 * itemHeight) /
223ef40425aS猫头猫                                itemHeight /
224ef40425aS猫头猫                                2;
2254de3fbc9S猫头猫                        } else {
226ef40425aS猫头猫                            // 不滑动
227ef40425aS猫头猫                            direction.value = 0;
2284de3fbc9S猫头猫                        }
2294de3fbc9S猫头猫                    }
2304de3fbc9S猫头猫                }}
2314de3fbc9S猫头猫                onTouchEnd={e => {
2324de3fbc9S猫头猫                    if (activeRef.current !== -1) {
2334de3fbc9S猫头猫                        // 计算最终的位置,触发onSortEnd
2344de3fbc9S猫头猫                        let index = activeRef.current;
2359cc05961S猫头猫                        if (contentOffsetYRef.current !== -1) {
2364de3fbc9S猫头猫                            index = Math.round(
2374de3fbc9S猫头猫                                (contentOffsetYRef.current +
2384de3fbc9S猫头猫                                    offsetRef.current) /
2394de3fbc9S猫头猫                                    itemHeight,
2404de3fbc9S猫头猫                            );
2414de3fbc9S猫头猫                        } else {
2424de3fbc9S猫头猫                            // 拖动的距离
2434de3fbc9S猫头猫                            index =
2444de3fbc9S猫头猫                                activeRef.current +
2454de3fbc9S猫头猫                                Math.round(
2464de3fbc9S猫头猫                                    (e.nativeEvent.pageY -
2474de3fbc9S猫头猫                                        initDragPageY.current +
2484de3fbc9S猫头猫                                        initDragLocationY.current) /
2494de3fbc9S猫头猫                                        itemHeight,
2504de3fbc9S猫头猫                                );
2514de3fbc9S猫头猫                        }
2524de3fbc9S猫头猫                        index = Math.min(data.length, Math.max(index, 0));
2534de3fbc9S猫头猫                        // from: activeRef.current to: index
2544de3fbc9S猫头猫                        if (activeRef.current !== index) {
2554de3fbc9S猫头猫                            let nData = _data
2564de3fbc9S猫头猫                                .slice(0, activeRef.current)
2574de3fbc9S猫头猫                                .concat(_data.slice(activeRef.current + 1));
2584de3fbc9S猫头猫                            nData.splice(index, 0, activeItem as T);
2594de3fbc9S猫头猫                            onSortEnd?.(nData);
2604de3fbc9S猫头猫                            // 测试用,正式时移除掉
2614de3fbc9S猫头猫                            // _setData(nData);
2624de3fbc9S猫头猫                        }
2634de3fbc9S猫头猫                    }
264ef40425aS猫头猫                    scrollingRef.current = false;
2654de3fbc9S猫头猫                    activeRef.current = -1;
2664de3fbc9S猫头猫                    setScrollEnabled(true);
2674de3fbc9S猫头猫                    setActiveItem(null);
2684de3fbc9S猫头猫                    fakeItemRef.current!.setNativeProps({
2694de3fbc9S猫头猫                        top: 0,
2704de3fbc9S猫头猫                        opacity: 0,
2714de3fbc9S猫头猫                        zIndex: -1,
2724de3fbc9S猫头猫                    });
2734de3fbc9S猫头猫                }}
2744de3fbc9S猫头猫                onTouchCancel={() => {
27599b6e7ceS猫头猫                    // todo: 滑动很快的时候会触发取消,native的flatlist就这样
27676cce596S猫头猫                    activeRef.current = -1;
277ef40425aS猫头猫                    scrollingRef.current = false;
27876cce596S猫头猫                    setScrollEnabled(true);
27976cce596S猫头猫                    setActiveItem(null);
28076cce596S猫头猫                    fakeItemRef.current!.setNativeProps({
28176cce596S猫头猫                        top: 0,
28276cce596S猫头猫                        opacity: 0,
28376cce596S猫头猫                        zIndex: -1,
28476cce596S猫头猫                    });
2859cc05961S猫头猫                    contentOffsetYRef.current = -1;
2864de3fbc9S猫头猫                }}
2874de3fbc9S猫头猫                onScroll={e => {
2884de3fbc9S猫头猫                    contentOffsetYRef.current = e.nativeEvent.contentOffset.y;
289ef40425aS猫头猫                    if (
290ef40425aS猫头猫                        activeRef.current !== -1 &&
291ef40425aS猫头猫                        Math.abs(
292ef40425aS猫头猫                            contentOffsetYRef.current -
293ef40425aS猫头猫                                targetOffsetYRef.current,
294ef40425aS猫头猫                        ) < 2
295ef40425aS猫头猫                    ) {
296ef40425aS猫头猫                        scrollToTarget(true);
297ef40425aS猫头猫                    }
2984de3fbc9S猫头猫                }}
2994de3fbc9S猫头猫                renderItem={({item, index}) => {
3004de3fbc9S猫头猫                    return (
3014de3fbc9S猫头猫                        <SortableFlatListItem
3024de3fbc9S猫头猫                            setScrollEnabled={setScrollEnabled}
3034de3fbc9S猫头猫                            activeRef={activeRef}
3044de3fbc9S猫头猫                            renderItem={renderItem}
3054de3fbc9S猫头猫                            item={item}
3064de3fbc9S猫头猫                            index={index}
3074de3fbc9S猫头猫                            setActiveItem={setActiveItem}
30834588741S猫头猫                            itemJustifyContent={itemJustifyContent}
3094de3fbc9S猫头猫                            itemHeight={itemHeight}
3104de3fbc9S猫头猫                        />
3114de3fbc9S猫头猫                    );
3124de3fbc9S猫头猫                }}
3134de3fbc9S猫头猫            />
3144de3fbc9S猫头猫        </View>
3154de3fbc9S猫头猫    );
3164de3fbc9S猫头猫}
3174de3fbc9S猫头猫
3184de3fbc9S猫头猫interface ISortableFlatListItemProps<T extends any = any> {
3194de3fbc9S猫头猫    item: T;
3204de3fbc9S猫头猫    index: number;
3214de3fbc9S猫头猫    // 高度
3224de3fbc9S猫头猫    itemHeight: number;
32334588741S猫头猫    itemJustifyContent?:
32434588741S猫头猫        | 'flex-start'
32534588741S猫头猫        | 'flex-end'
32634588741S猫头猫        | 'center'
32734588741S猫头猫        | 'space-between'
32834588741S猫头猫        | 'space-around'
32934588741S猫头猫        | 'space-evenly';
3304de3fbc9S猫头猫    setScrollEnabled: (scrollEnabled: boolean) => void;
3314de3fbc9S猫头猫    renderItem: (props: {item: T; index: number}) => JSX.Element;
3324de3fbc9S猫头猫    setActiveItem: (item: T | null) => void;
3334de3fbc9S猫头猫    activeRef: React.MutableRefObject<number>;
3344de3fbc9S猫头猫}
3354de3fbc9S猫头猫function _SortableFlatListItem(props: ISortableFlatListItemProps) {
3364de3fbc9S猫头猫    const {
3374de3fbc9S猫头猫        itemHeight,
3384de3fbc9S猫头猫        setScrollEnabled,
3394de3fbc9S猫头猫        renderItem,
3404de3fbc9S猫头猫        setActiveItem,
34134588741S猫头猫        itemJustifyContent,
3424de3fbc9S猫头猫        item,
3434de3fbc9S猫头猫        index,
3444de3fbc9S猫头猫        activeRef,
3454de3fbc9S猫头猫    } = props;
3464de3fbc9S猫头猫
3474de3fbc9S猫头猫    // 省一点性能,height是顺着传下来的,放ref就好了
3484de3fbc9S猫头猫    const styleRef = useRef(
3494de3fbc9S猫头猫        StyleSheet.create({
3504de3fbc9S猫头猫            viewWrapper: {
3514de3fbc9S猫头猫                height: itemHeight,
352c446f2b8S猫头猫                width: '100%',
3534de3fbc9S猫头猫                flexDirection: 'row',
35434588741S猫头猫                justifyContent: itemJustifyContent ?? 'flex-end',
3554de3fbc9S猫头猫                zIndex: defaultZIndex,
3564de3fbc9S猫头猫            },
3574de3fbc9S猫头猫            btn: {
3584de3fbc9S猫头猫                height: itemHeight,
3594de3fbc9S猫头猫                justifyContent: 'center',
3604de3fbc9S猫头猫                alignItems: 'center',
3613b155a65S猫头猫                position: 'absolute',
3623b155a65S猫头猫                top: 0,
3633b155a65S猫头猫                right: 0,
3643b155a65S猫头猫                width: rpx(100),
3653b155a65S猫头猫                textAlignVertical: 'center',
3664de3fbc9S猫头猫            },
3674de3fbc9S猫头猫        }),
3684de3fbc9S猫头猫    );
3694de3fbc9S猫头猫    const textColor = useTextColor();
3704de3fbc9S猫头猫
3714de3fbc9S猫头猫    return (
3724de3fbc9S猫头猫        <View style={styleRef.current.viewWrapper}>
3734de3fbc9S猫头猫            {renderItem({item, index})}
3744de3fbc9S猫头猫            <Pressable
3754de3fbc9S猫头猫                onTouchStart={() => {
3764de3fbc9S猫头猫                    if (activeRef.current !== -1) {
3774de3fbc9S猫头猫                        return;
3784de3fbc9S猫头猫                    }
3794de3fbc9S猫头猫                    /** 使用ref避免其它组件重新渲染; 由于事件冒泡,这里会先触发 */
3804de3fbc9S猫头猫                    activeRef.current = index;
3814de3fbc9S猫头猫                    /** 锁定滚动 */
3824de3fbc9S猫头猫                    setScrollEnabled(false);
3834de3fbc9S猫头猫                    setActiveItem(item);
3844de3fbc9S猫头猫                }}
3854de3fbc9S猫头猫                style={styleRef.current.btn}>
3864de3fbc9S猫头猫                <Icon
387*5589cdf3S猫头猫                    name="bars-3"
3884de3fbc9S猫头猫                    size={iconSizeConst.normal}
3894de3fbc9S猫头猫                    color={textColor}
3904de3fbc9S猫头猫                />
3914de3fbc9S猫头猫            </Pressable>
3924de3fbc9S猫头猫        </View>
3934de3fbc9S猫头猫    );
3944de3fbc9S猫头猫}
3954de3fbc9S猫头猫
3964de3fbc9S猫头猫const SortableFlatListItem = memo(
3974de3fbc9S猫头猫    _SortableFlatListItem,
3984de3fbc9S猫头猫    (prev, curr) => prev.index === curr.index && prev.item === curr.item,
3994de3fbc9S猫头猫);
4004de3fbc9S猫头猫
4014de3fbc9S猫头猫const FakeFlatListItem = forwardRef(function (
4024de3fbc9S猫头猫    props: Pick<
4034de3fbc9S猫头猫        ISortableFlatListItemProps,
40434588741S猫头猫        'itemHeight' | 'renderItem' | 'item' | 'itemJustifyContent'
4054de3fbc9S猫头猫    > & {
4064de3fbc9S猫头猫        backgroundColor?: string;
4074de3fbc9S猫头猫    },
4084de3fbc9S猫头猫    ref: ForwardedRef<View>,
4094de3fbc9S猫头猫) {
41034588741S猫头猫    const {itemHeight, renderItem, item, backgroundColor, itemJustifyContent} =
41134588741S猫头猫        props;
4124de3fbc9S猫头猫
4134de3fbc9S猫头猫    const styleRef = useRef(
4144de3fbc9S猫头猫        StyleSheet.create({
4154de3fbc9S猫头猫            viewWrapper: {
4164de3fbc9S猫头猫                height: itemHeight,
417c446f2b8S猫头猫                width: '100%',
4184de3fbc9S猫头猫                flexDirection: 'row',
41934588741S猫头猫                justifyContent: itemJustifyContent ?? 'flex-end',
4204de3fbc9S猫头猫                zIndex: defaultZIndex,
4214de3fbc9S猫头猫            },
4224de3fbc9S猫头猫            btn: {
4234de3fbc9S猫头猫                height: itemHeight,
4244de3fbc9S猫头猫                paddingHorizontal: rpx(28),
4254de3fbc9S猫头猫                justifyContent: 'center',
4264de3fbc9S猫头猫                alignItems: 'center',
4273b155a65S猫头猫                position: 'absolute',
4283b155a65S猫头猫                top: 0,
4293b155a65S猫头猫                right: 0,
4303b155a65S猫头猫                width: rpx(100),
4313b155a65S猫头猫                textAlignVertical: 'center',
4324de3fbc9S猫头猫            },
4334de3fbc9S猫头猫        }),
4344de3fbc9S猫头猫    );
4354de3fbc9S猫头猫    const textColor = useTextColor();
4364de3fbc9S猫头猫
4374de3fbc9S猫头猫    return (
4384de3fbc9S猫头猫        <View
4394de3fbc9S猫头猫            ref={ref}
4404de3fbc9S猫头猫            style={[
4414de3fbc9S猫头猫                styleRef.current.viewWrapper,
4424de3fbc9S猫头猫                style.activeItemDefault,
4434de3fbc9S猫头猫                backgroundColor ? {backgroundColor} : {},
4444de3fbc9S猫头猫            ]}>
4451fbef60aS猫头猫            {item ? renderItem({item, index: -1}) : null}
4464de3fbc9S猫头猫            <Pressable style={styleRef.current.btn}>
4474de3fbc9S猫头猫                <Icon
448*5589cdf3S猫头猫                    name="bars-3"
4494de3fbc9S猫头猫                    size={iconSizeConst.normal}
4504de3fbc9S猫头猫                    color={textColor}
4514de3fbc9S猫头猫                />
4524de3fbc9S猫头猫            </Pressable>
4534de3fbc9S猫头猫        </View>
4544de3fbc9S猫头猫    );
4554de3fbc9S猫头猫});
4564de3fbc9S猫头猫
4574de3fbc9S猫头猫const style = StyleSheet.create({
4584de3fbc9S猫头猫    activeItemDefault: {
4594de3fbc9S猫头猫        opacity: 0,
4604de3fbc9S猫头猫        zIndex: -1,
4614de3fbc9S猫头猫        position: 'absolute',
4624de3fbc9S猫头猫        top: 0,
4634de3fbc9S猫头猫        left: 0,
4644de3fbc9S猫头猫    },
4654de3fbc9S猫头猫});
466