UNPKG

6.55 kBTypeScriptView Raw
1import color from 'color';
2import * as React from 'react';
3import {
4 StyleProp,
5 StyleSheet,
6 TextStyle,
7 View,
8 ViewStyle,
9} from 'react-native';
10
11import TouchableRipple from '../TouchableRipple/TouchableRipple';
12import Text from '../Typography/Text';
13import { withTheme } from '../../core/theming';
14import type { $RemoveChildren, EllipsizeProp } from '../../types';
15
16type Title =
17 | React.ReactNode
18 | ((props: {
19 selectable: boolean;
20 ellipsizeMode: EllipsizeProp | undefined;
21 color: string;
22 fontSize: number;
23 }) => React.ReactNode);
24
25type Description =
26 | React.ReactNode
27 | ((props: {
28 selectable: boolean;
29 ellipsizeMode: EllipsizeProp | undefined;
30 color: string;
31 fontSize: number;
32 }) => React.ReactNode);
33
34type Props = $RemoveChildren<typeof TouchableRipple> & {
35 /**
36 * Title text for the list item.
37 */
38 title: Title;
39 /**
40 * Description text for the list item or callback which returns a React element to display the description.
41 */
42 description?: Description;
43 /**
44 * Callback which returns a React element to display on the left side.
45 */
46 left?: (props: {
47 color: string;
48 style: {
49 marginLeft: number;
50 marginRight: number;
51 marginVertical?: number;
52 };
53 }) => React.ReactNode;
54 /**
55 * Callback which returns a React element to display on the right side.
56 */
57 right?: (props: {
58 color: string;
59 style?: {
60 marginRight: number;
61 marginVertical?: number;
62 };
63 }) => React.ReactNode;
64 /**
65 * Function to execute on press.
66 */
67 onPress?: () => void;
68 /**
69 * @optional
70 */
71 theme: ReactNativePaper.Theme;
72 /**
73 * Style that is passed to the wrapping TouchableRipple element.
74 */
75 style?: StyleProp<ViewStyle>;
76 /**
77 * Style that is passed to Title element.
78 */
79 titleStyle?: StyleProp<TextStyle>;
80 /**
81 * Style that is passed to Description element.
82 */
83 descriptionStyle?: StyleProp<TextStyle>;
84 /**
85 * Truncate Title text such that the total number of lines does not
86 * exceed this number.
87 */
88 titleNumberOfLines?: number;
89 /**
90 * Truncate Description text such that the total number of lines does not
91 * exceed this number.
92 */
93 descriptionNumberOfLines?: number;
94 /**
95 * Ellipsize Mode for the Title. One of `'head'`, `'middle'`, `'tail'`, `'clip'`.
96 *
97 * See [`ellipsizeMode`](https://reactnative.dev/docs/text#ellipsizemode)
98 */
99 titleEllipsizeMode?: EllipsizeProp;
100 /**
101 * Ellipsize Mode for the Description. One of `'head'`, `'middle'`, `'tail'`, `'clip'`.
102 *
103 * See [`ellipsizeMode`](https://reactnative.dev/docs/text#ellipsizemode)
104 */
105 descriptionEllipsizeMode?: EllipsizeProp;
106};
107
108/**
109 * A component to show tiles inside a List.
110 *
111 * <div class="screenshots">
112 * <img class="medium" src="screenshots/list-item-1.png" />
113 * <img class="medium" src="screenshots/list-item-2.png" />
114 * <img class="medium" src="screenshots/list-item-3.png" />
115 * </div>
116 *
117 * ## Usage
118 * ```js
119 * import * as React from 'react';
120 * import { List } from 'react-native-paper';
121 *
122 * const MyComponent = () => (
123 * <List.Item
124 * title="First Item"
125 * description="Item description"
126 * left={props => <List.Icon {...props} icon="folder" />}
127 * />
128 * );
129 *
130 * export default MyComponent;
131 * ```
132 *
133 * @extends TouchableRipple props https://callstack.github.io/react-native-paper/touchable-ripple.html
134 */
135const 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
243ListItem.displayName = 'List.Item';
244
245const 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
271export default withTheme(ListItem);