1 | import color from 'color';
|
2 | import * as React from 'react';
|
3 | import {
|
4 | StyleProp,
|
5 | StyleSheet,
|
6 | TextStyle,
|
7 | View,
|
8 | ViewStyle,
|
9 | } from 'react-native';
|
10 |
|
11 | import TouchableRipple from '../TouchableRipple/TouchableRipple';
|
12 | import Text from '../Typography/Text';
|
13 | import { withTheme } from '../../core/theming';
|
14 | import type { $RemoveChildren, EllipsizeProp } from '../../types';
|
15 |
|
16 | type Title =
|
17 | | React.ReactNode
|
18 | | ((props: {
|
19 | selectable: boolean;
|
20 | ellipsizeMode: EllipsizeProp | undefined;
|
21 | color: string;
|
22 | fontSize: number;
|
23 | }) => React.ReactNode);
|
24 |
|
25 | type Description =
|
26 | | React.ReactNode
|
27 | | ((props: {
|
28 | selectable: boolean;
|
29 | ellipsizeMode: EllipsizeProp | undefined;
|
30 | color: string;
|
31 | fontSize: number;
|
32 | }) => React.ReactNode);
|
33 |
|
34 | type Props = $RemoveChildren<typeof TouchableRipple> & {
|
35 | |
36 |
|
37 |
|
38 | title: Title;
|
39 | |
40 |
|
41 |
|
42 | description?: Description;
|
43 | |
44 |
|
45 |
|
46 | left?: (props: {
|
47 | color: string;
|
48 | style: {
|
49 | marginLeft: number;
|
50 | marginRight: number;
|
51 | marginVertical?: number;
|
52 | };
|
53 | }) => React.ReactNode;
|
54 | |
55 |
|
56 |
|
57 | right?: (props: {
|
58 | color: string;
|
59 | style?: {
|
60 | marginRight: number;
|
61 | marginVertical?: number;
|
62 | };
|
63 | }) => React.ReactNode;
|
64 | |
65 |
|
66 |
|
67 | onPress?: () => void;
|
68 | |
69 |
|
70 |
|
71 | theme: ReactNativePaper.Theme;
|
72 | |
73 |
|
74 |
|
75 | style?: StyleProp<ViewStyle>;
|
76 | |
77 |
|
78 |
|
79 | titleStyle?: StyleProp<TextStyle>;
|
80 | |
81 |
|
82 |
|
83 | descriptionStyle?: StyleProp<TextStyle>;
|
84 | |
85 |
|
86 |
|
87 |
|
88 | titleNumberOfLines?: number;
|
89 | |
90 |
|
91 |
|
92 |
|
93 | descriptionNumberOfLines?: number;
|
94 | |
95 |
|
96 |
|
97 |
|
98 |
|
99 | titleEllipsizeMode?: EllipsizeProp;
|
100 | |
101 |
|
102 |
|
103 |
|
104 |
|
105 | descriptionEllipsizeMode?: EllipsizeProp;
|
106 | };
|
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 | const ListItem = ({
|
136 | left,
|
137 | right,
|
138 | title,
|
139 | description,
|
140 | onPress,
|
141 | theme,
|
142 | style,
|
143 | titleStyle,
|
144 | titleNumberOfLines = 1,
|
145 | descriptionNumberOfLines = 2,
|
146 | titleEllipsizeMode,
|
147 | descriptionEllipsizeMode,
|
148 | descriptionStyle,
|
149 | ...rest
|
150 | }: Props) => {
|
151 | const renderDescription = (
|
152 | descriptionColor: string,
|
153 | description?: Description | null
|
154 | ) => {
|
155 | return typeof description === 'function' ? (
|
156 | description({
|
157 | selectable: false,
|
158 | ellipsizeMode: descriptionEllipsizeMode,
|
159 | color: descriptionColor,
|
160 | fontSize: styles.description.fontSize,
|
161 | })
|
162 | ) : (
|
163 | <Text
|
164 | selectable={false}
|
165 | numberOfLines={descriptionNumberOfLines}
|
166 | ellipsizeMode={descriptionEllipsizeMode}
|
167 | style={[
|
168 | styles.description,
|
169 | { color: descriptionColor },
|
170 | descriptionStyle,
|
171 | ]}
|
172 | >
|
173 | {description}
|
174 | </Text>
|
175 | );
|
176 | };
|
177 |
|
178 | const renderTitle = () => {
|
179 | const titleColor = color(theme.colors.text).alpha(0.87).rgb().string();
|
180 |
|
181 | return typeof title === 'function' ? (
|
182 | title({
|
183 | selectable: false,
|
184 | ellipsizeMode: titleEllipsizeMode,
|
185 | color: titleColor,
|
186 | fontSize: styles.title.fontSize,
|
187 | })
|
188 | ) : (
|
189 | <Text
|
190 | selectable={false}
|
191 | ellipsizeMode={titleEllipsizeMode}
|
192 | numberOfLines={titleNumberOfLines}
|
193 | style={[styles.title, { color: titleColor }, titleStyle]}
|
194 | >
|
195 | {title}
|
196 | </Text>
|
197 | );
|
198 | };
|
199 |
|
200 | const descriptionColor = color(theme.colors.text).alpha(0.54).rgb().string();
|
201 |
|
202 | return (
|
203 | <TouchableRipple
|
204 | {...rest}
|
205 | style={[styles.container, style]}
|
206 | onPress={onPress}
|
207 | >
|
208 | <View style={styles.row}>
|
209 | {left
|
210 | ? left({
|
211 | color: descriptionColor,
|
212 | style: description
|
213 | ? styles.iconMarginLeft
|
214 | : {
|
215 | ...styles.iconMarginLeft,
|
216 | ...styles.marginVerticalNone,
|
217 | },
|
218 | })
|
219 | : null}
|
220 | <View style={[styles.item, styles.content]}>
|
221 | {renderTitle()}
|
222 |
|
223 | {description
|
224 | ? renderDescription(descriptionColor, description)
|
225 | : null}
|
226 | </View>
|
227 | {right
|
228 | ? right({
|
229 | color: descriptionColor,
|
230 | style: description
|
231 | ? styles.iconMarginRight
|
232 | : {
|
233 | ...styles.iconMarginRight,
|
234 | ...styles.marginVerticalNone,
|
235 | },
|
236 | })
|
237 | : null}
|
238 | </View>
|
239 | </TouchableRipple>
|
240 | );
|
241 | };
|
242 |
|
243 | ListItem.displayName = 'List.Item';
|
244 |
|
245 | const styles = StyleSheet.create({
|
246 | container: {
|
247 | padding: 8,
|
248 | },
|
249 | row: {
|
250 | flexDirection: 'row',
|
251 | },
|
252 | title: {
|
253 | fontSize: 16,
|
254 | },
|
255 | description: {
|
256 | fontSize: 14,
|
257 | },
|
258 | marginVerticalNone: { marginVertical: 0 },
|
259 | iconMarginLeft: { marginLeft: 0, marginRight: 16 },
|
260 | iconMarginRight: { marginRight: 0 },
|
261 | item: {
|
262 | marginVertical: 6,
|
263 | paddingLeft: 8,
|
264 | },
|
265 | content: {
|
266 | flex: 1,
|
267 | justifyContent: 'center',
|
268 | },
|
269 | });
|
270 |
|
271 | export default withTheme(ListItem);
|