import { forwardRef, ReactElement } from "react" import { StyleProp, TextStyle, TouchableOpacity, TouchableOpacityProps, View, ViewStyle, } from "react-native" import { useAppTheme } from "@/theme/context" import { $styles } from "@/theme/styles" import type { ThemedStyle } from "@/theme/types" import { s } from "@/utils/responsive" import { Icon, IconTypes } from "./Icon" import { Text, TextProps } from "./Text" export interface ListItemProps extends TouchableOpacityProps { /** * How tall the list item should be. * Default: 56 */ height?: number /** * Whether to show the top separator. * Default: false */ topSeparator?: boolean /** * Whether to show the bottom separator. * Default: false */ bottomSeparator?: boolean /** * Text to display if not using `tx` or nested components. */ text?: TextProps["text"] /** * Text which is looked up via i18n. */ tx?: TextProps["tx"] /** * Children components. */ children?: TextProps["children"] /** * Optional options to pass to i18n. Useful for interpolation * as well as explicitly setting locale or translation fallbacks. */ txOptions?: TextProps["txOptions"] /** * Optional text style override. */ textStyle?: StyleProp /** * Pass any additional props directly to the Text component. */ TextProps?: TextProps /** * Optional View container style override. */ containerStyle?: StyleProp /** * Optional TouchableOpacity style override. */ style?: StyleProp /** * Icon that should appear on the left. */ leftIcon?: IconTypes /** * An optional tint color for the left icon */ leftIconColor?: string /** * Icon that should appear on the right. */ rightIcon?: IconTypes /** * An optional tint color for the right icon */ rightIconColor?: string /** * Right action custom ReactElement. * Overrides `rightIcon`. */ RightComponent?: ReactElement /** * Left action custom ReactElement. * Overrides `leftIcon`. */ LeftComponent?: ReactElement } interface ListItemActionProps { icon?: IconTypes iconColor?: string Component?: ReactElement size: number side: "left" | "right" } /** * A styled row component that can be used in FlatList, SectionList, or by itself. * @see [Documentation and Examples]{@link https://docs.infinite.red/ignite-cli/boilerplate/app/components/ListItem/} * @param {ListItemProps} props - The props for the `ListItem` component. * @returns {JSX.Element} The rendered `ListItem` component. */ export const ListItem = forwardRef(function ListItem( props: ListItemProps, ref, ) { const { bottomSeparator, children, height = s(56), LeftComponent, leftIcon, leftIconColor, RightComponent, rightIcon, rightIconColor, style, text, TextProps, topSeparator, tx, txOptions, textStyle: $textStyleOverride, containerStyle: $containerStyleOverride, ...TouchableOpacityProps } = props const { themed } = useAppTheme() const isTouchable = TouchableOpacityProps.onPress !== undefined || TouchableOpacityProps.onPressIn !== undefined || TouchableOpacityProps.onPressOut !== undefined || TouchableOpacityProps.onLongPress !== undefined const $textStyles = [$textStyle, $textStyleOverride, TextProps?.style] const $containerStyles = [ topSeparator && $separatorTop, bottomSeparator && $separatorBottom, $containerStyleOverride, ] const $touchableStyles = [$styles.row, $touchableStyle, { minHeight: height }, style] const Wrapper = isTouchable ? TouchableOpacity : View return ( {children} ) }) /** * @param {ListItemActionProps} props - The props for the `ListItemAction` component. * @returns {JSX.Element | null} The rendered `ListItemAction` component. */ function ListItemAction(props: ListItemActionProps) { const { icon, Component, iconColor, size, side } = props const { themed } = useAppTheme() const $iconContainerStyles = [$iconContainer] if (Component) { return ( {Component} ) } if (icon !== undefined) { return ( ) } return null } const $separatorTop: ThemedStyle = ({ colors }) => ({ borderTopWidth: 1, borderTopColor: colors.separator, }) const $separatorBottom: ThemedStyle = ({ colors }) => ({ borderBottomWidth: 1, borderBottomColor: colors.separator, }) const $textStyle: ThemedStyle = ({ spacing }) => ({ paddingVertical: spacing.xs, alignSelf: "center", flexGrow: 1, flexShrink: 1, }) const $touchableStyle: ViewStyle = { alignItems: "flex-start", } const $iconContainer: ViewStyle = { justifyContent: "center", alignItems: "center", flexGrow: 0, } const $iconContainerLeft: ThemedStyle = ({ spacing }) => ({ marginEnd: spacing.md, }) const $iconContainerRight: ThemedStyle = ({ spacing }) => ({ marginStart: spacing.md, })