xref: /MusicFree/src/components/base/listItem.tsx (revision 1119c2ea435417cd5c53719f91691ff2b1aa8670)
1import React, {ReactNode} from 'react';
2import {
3    StyleProp,
4    StyleSheet,
5    TextProps,
6    TextStyle,
7    TouchableHighlight,
8    TouchableOpacity,
9    View,
10    ViewStyle,
11} from 'react-native';
12import rpx from '@/utils/rpx';
13import useColors from '@/hooks/useColors';
14import ThemeText from './themeText';
15import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
16import {
17    fontSizeConst,
18    fontWeightConst,
19    iconSizeConst,
20} from '@/constants/uiConst';
21import FastImage from './fastImage';
22import {ImageStyle} from 'react-native-fast-image';
23
24interface IListItemProps {
25    // 是否有左右边距
26    withHorizonalPadding?: boolean;
27    // 左边距
28    leftPadding?: number;
29    // 右边距
30    rightPadding?: number;
31    // height:
32    style?: StyleProp<ViewStyle>;
33    // 高度类型
34    heightType?: 'big' | 'small' | 'normal' | 'none';
35    children?: ReactNode;
36    onPress?: () => void;
37    onLongPress?: () => void;
38}
39
40const defaultPadding = rpx(24);
41const defaultActionWidth = rpx(80);
42
43const Size = {
44    big: rpx(120),
45    normal: rpx(108),
46    small: rpx(96),
47    none: undefined,
48};
49
50function ListItem(props: IListItemProps) {
51    const {
52        withHorizonalPadding,
53        leftPadding = defaultPadding,
54        rightPadding = defaultPadding,
55        style,
56        heightType = 'normal',
57        children,
58        onPress,
59        onLongPress,
60    } = props;
61
62    const defaultStyle: StyleProp<ViewStyle> = {
63        paddingLeft: withHorizonalPadding ? leftPadding : 0,
64        paddingRight: withHorizonalPadding ? rightPadding : 0,
65        height: Size[heightType],
66    };
67
68    const colors = useColors();
69
70    return (
71        <TouchableHighlight
72            style={styles.container}
73            underlayColor={colors.listActive}
74            onPress={onPress}
75            onLongPress={onLongPress}>
76            <View style={[styles.container, defaultStyle, style]}>
77                {children}
78            </View>
79        </TouchableHighlight>
80    );
81}
82
83interface IListItemTextProps {
84    children?: number | string;
85    fontSize?: keyof typeof fontSizeConst;
86    fontWeight?: keyof typeof fontWeightConst;
87    width?: number;
88    position?: 'left' | 'right' | 'none';
89    fixedWidth?: boolean;
90    containerStyle?: StyleProp<ViewStyle>;
91    contentStyle?: StyleProp<TextStyle>;
92    contentProps?: TextProps;
93}
94
95function ListItemText(props: IListItemTextProps) {
96    const {
97        children,
98        fontSize,
99        fontWeight,
100        position = 'left',
101        fixedWidth,
102        width,
103        containerStyle,
104        contentStyle,
105        contentProps = {},
106    } = props;
107
108    const defaultStyle: StyleProp<ViewStyle> = {
109        marginRight: position === 'left' ? defaultPadding : 0,
110        marginLeft: position === 'right' ? defaultPadding : 0,
111        width: fixedWidth ? width ?? defaultActionWidth : undefined,
112        flexBasis: fixedWidth ? width ?? defaultActionWidth : undefined,
113    };
114
115    return (
116        <View style={[styles.actionBase, defaultStyle, containerStyle]}>
117            <ThemeText
118                fontSize={fontSize}
119                style={contentStyle}
120                fontWeight={fontWeight}
121                {...contentProps}>
122                {children}
123            </ThemeText>
124        </View>
125    );
126}
127
128interface IListItemIconProps {
129    icon: string;
130    iconSize?: number;
131    width?: number;
132    position?: 'left' | 'right' | 'none';
133    fixedWidth?: boolean;
134    containerStyle?: StyleProp<ViewStyle>;
135    contentStyle?: StyleProp<TextStyle>;
136    onPress?: () => void;
137}
138
139function ListItemIcon(props: IListItemIconProps) {
140    const {
141        icon,
142        iconSize = iconSizeConst.normal,
143        position = 'left',
144        fixedWidth,
145        width,
146        containerStyle,
147        contentStyle,
148        onPress,
149    } = props;
150
151    const defaultStyle: StyleProp<ViewStyle> = {
152        marginRight: position === 'left' ? defaultPadding : 0,
153        marginLeft: position === 'right' ? defaultPadding : 0,
154        width: fixedWidth ? width ?? defaultActionWidth : undefined,
155        flexBasis: fixedWidth ? width ?? defaultActionWidth : undefined,
156    };
157
158    const innerContent = (
159        <View style={[styles.actionBase, defaultStyle, containerStyle]}>
160            <Icon name={icon} size={iconSize} style={contentStyle} />
161        </View>
162    );
163
164    return onPress ? (
165        <TouchableOpacity onPress={onPress}>{innerContent}</TouchableOpacity>
166    ) : (
167        innerContent
168    );
169}
170
171interface IListItemImageProps {
172    uri?: string;
173    fallbackImg?: number;
174    imageSize?: number;
175    width?: number;
176    position?: 'left' | 'right';
177    fixedWidth?: boolean;
178    containerStyle?: StyleProp<ViewStyle>;
179    contentStyle?: StyleProp<ImageStyle>;
180}
181
182function ListItemImage(props: IListItemImageProps) {
183    const {
184        uri,
185        fallbackImg,
186        position = 'left',
187        fixedWidth,
188        width,
189        containerStyle,
190        contentStyle,
191    } = props;
192
193    const defaultStyle: StyleProp<ViewStyle> = {
194        marginRight: position === 'left' ? defaultPadding : 0,
195        marginLeft: position === 'right' ? defaultPadding : 0,
196        width: fixedWidth ? width ?? defaultActionWidth : undefined,
197        flexBasis: fixedWidth ? width ?? defaultActionWidth : undefined,
198    };
199
200    return (
201        <View style={[styles.actionBase, defaultStyle, containerStyle]}>
202            <FastImage
203                style={[styles.leftImage, contentStyle]}
204                uri={uri}
205                emptySrc={fallbackImg}
206            />
207        </View>
208    );
209}
210
211interface IContentProps {
212    title?: ReactNode;
213    description?: ReactNode;
214    containerStyle?: StyleProp<ViewStyle>;
215}
216
217function Content(props: IContentProps) {
218    const {title, description = null, containerStyle} = props;
219
220    let realTitle;
221    let realDescription;
222
223    if (typeof title === 'string' || typeof title === 'number') {
224        realTitle = <ThemeText numberOfLines={1}>{title}</ThemeText>;
225    } else {
226        realTitle = title;
227    }
228
229    if (typeof description === 'string' || typeof description === 'number') {
230        realDescription = (
231            <ThemeText
232                numberOfLines={1}
233                fontSize="description"
234                fontColor="secondary"
235                style={styles.contentDesc}>
236                {description}
237            </ThemeText>
238        );
239    } else {
240        realDescription = description;
241    }
242
243    return (
244        <View style={[styles.itemContentContainer, containerStyle]}>
245            {realTitle}
246            {realDescription}
247        </View>
248    );
249}
250
251const styles = StyleSheet.create({
252    /** listitem */
253    container: {
254        width: '100%',
255        flexDirection: 'row',
256        alignItems: 'center',
257    },
258    /** left */
259    actionBase: {
260        height: '100%',
261        flexShrink: 0,
262        flexGrow: 0,
263        flexBasis: 0,
264        flexDirection: 'row',
265        justifyContent: 'center',
266        alignItems: 'center',
267    },
268
269    leftImage: {
270        width: rpx(80),
271        height: rpx(80),
272        borderRadius: rpx(16),
273    },
274    itemContentContainer: {
275        flex: 1,
276        height: '100%',
277        justifyContent: 'center',
278    },
279    contentDesc: {
280        marginTop: rpx(16),
281    },
282});
283
284ListItem.Size = Size;
285ListItem.ListItemIcon = ListItemIcon;
286ListItem.ListItemImage = ListItemImage;
287ListItem.ListItemText = ListItemText;
288ListItem.Content = Content;
289
290export default ListItem;
291