1import type { ReactNode } from "react";
2import { Platform, useWindowDimensions, View } from "react-native";
3
4import { cn } from "../../lib/cn";
5import { AppText } from "./AppText";
6import { SymbolView, type AppSymbolName } from "./AppSymbol";
7
8export interface ListRowContentProps {
9 readonly icon?: AppSymbolName;
10 /** Drawn in the icon column for marks a single symbol can't express. */
11 readonly leading?: ReactNode;
12 readonly label: string;
13 readonly description?: string;
14 readonly detail?: string;
15 /** Rich secondary content under the description, such as scores with emphasized values. */
16 readonly meta?: ReactNode;
17 readonly disabled?: boolean;
18}
19
20export function ListRowLayout({
21 icon,
22 leading,
23 label,
24 description,
25 detail,
26 meta,
27 disabled,
28 accessory,
29 tone = "default",
30}: ListRowContentProps & { readonly accessory?: ReactNode; readonly tone?: "default" | "danger" }) {
31 const { fontScale } = useWindowDimensions();
32 const stackDetail = fontScale >= 1.5;
33 return (
34 <View
35 className={cn(
36 "min-h-14 flex-row items-center gap-3 px-4 py-3",
37 description && "min-h-18",
38 disabled && "opacity-45",
39 )}
40 >
41 {icon ? (
42 <View className="size-6 items-center justify-center">
43 <SymbolView
44 name={icon}
45 size={Platform.OS === "android" ? 24 : 22}
46 tintColorClassName={tone === "danger" ? "accent-danger-foreground" : "accent-icon"}
47 type="monochrome"
48 weight="regular"
49 />
50 </View>
51 ) : leading ? (
52 <View className="size-6 items-center justify-center">{leading}</View>
53 ) : null}
54 <View className="min-w-0 flex-1 gap-1">
55 <AppText
56 textRole="body"
57 className={tone === "danger" ? "text-danger-foreground" : "text-foreground"}
58 >
59 {label}
60 </AppText>
61 {description ? (
62 <AppText textRole="footnote" className="text-foreground-muted">
63 {description}
64 </AppText>
65 ) : null}
66 {meta}
67 {detail && stackDetail ? (
68 <AppText textRole="footnote" className="text-foreground-muted">
69 {detail}
70 </AppText>
71 ) : null}
72 </View>
73 {detail && !stackDetail ? (
74 <AppText
75 textRole="footnote"
76 className="max-w-[40%] shrink text-right text-foreground-muted"
77 numberOfLines={1}
78 ellipsizeMode="middle"
79 >
80 {detail}
81 </AppText>
82 ) : null}
83 {accessory}
84 </View>
85 );
86}