import color from "color"; import * as React from "react"; import { View, ViewStyle, StyleSheet, StyleProp, TextStyle, I18nManager, } from "react-native"; import { DefaultTheme, ThemeContext } from "styled-components"; import ChevronUpIcon from "@mdi/svg/svg/chevron-up.svg"; import ChevronDownIcon from "@mdi/svg/svg/chevron-down.svg"; import TouchableRipple from "../TouchableRipple"; import Text from "../Text"; import { ListAccordionGroupContext } from "./ListAccordionGroup"; type Props = { /** * Title text for the list accordion. */ title: React.ReactNode; /** * Description text for the list accordion. */ description?: React.ReactNode; /** * Callback which returns a React element to display on the left side. */ left?: (props: { color: string }) => React.ReactNode; /** * Whether the accordion is expanded * If this prop is provided, the accordion will behave as a "controlled component". * You'll need to update this prop when you want to toggle the component or on `onPress`. */ expanded?: boolean; /** * Function to execute on press. */ onPress?: () => void; /** * Function to execute on long press. */ onLongPress?: () => void; /** * Content of the section. */ children: React.ReactNode; /** * @optional */ theme?: DefaultTheme; /** * Style that is passed to the wrapping TouchableRipple element. */ style?: StyleProp; /** * Style that is passed to Title element. */ titleStyle?: StyleProp; /** * Style that is passed to Description element. */ descriptionStyle?: StyleProp; /** * Truncate Title text such that the total number of lines does not * exceed this number. */ titleNumberOfLines?: number; /** * Truncate Description text such that the total number of lines does not * exceed this number. */ descriptionNumberOfLines?: number; /** * Id is used for distinguishing specific accordion when using List.AccordionGroup. Property is required when using List.AccordionGroup and has no impact on behavior when using standalone List.Accordion. */ id?: string | number; /** * TestID used for testing purposes */ testID?: string; }; /** * A component used to display an expandable list item. * *
* * * *
* * ## Usage * ```js * import * as React from 'react'; * import { ListItem } from 'react-native-simple-elements/components/List'; * * const MyComponent = () => { * const [expanded, setExpanded] = React.useState(true); * * const handlePress = () => setExpanded(!expanded); * * return ( * * }> * * * * * } * expanded={expanded} * onPress={handlePress}> * * * * * ); * }; * * export default MyComponent; * ``` */ const ListAccordion = ({ left, title, description, children, titleStyle, descriptionStyle, titleNumberOfLines = 1, descriptionNumberOfLines = 2, style, id, testID, onPress, onLongPress, expanded: expandedProp, }: Props) => { const theme = React.useContext(ThemeContext); const [expanded, setExpanded] = React.useState( expandedProp || false ); const handlePressAction = () => { onPress?.(); if (expandedProp === undefined) { // Only update state of the `expanded` prop was not passed // If it was passed, the component will act as a controlled component setExpanded((expanded) => !expanded); } }; const titleColor = color(theme.colors.text).alpha(0.87).rgb().string(); const descriptionColor = color(theme.colors.text).alpha(0.54).rgb().string(); const expandedInternal = expandedProp !== undefined ? expandedProp : expanded; const groupContext = React.useContext(ListAccordionGroupContext); if (groupContext !== null && !id) { throw new Error( "List.Accordion is used inside a List.AccordionGroup without specifying an id prop." ); } const isExpanded = groupContext ? groupContext.expandedId === id : expandedInternal; const handlePress = groupContext && id !== undefined ? () => groupContext.onAccordionPress(id) : handlePressAction; const ChevronIcon = isExpanded ? ChevronUpIcon : ChevronDownIcon; return ( {left ? left({ color: isExpanded ? theme.colors.primary : descriptionColor, }) : null} {title} {description && ( {description} )} {isExpanded ? React.Children.map(children, (child) => { if ( left && React.isValidElement(child) && !child.props.left && !child.props.right ) { return React.cloneElement(child, { style: [styles.child, child.props.style], }); } return child; }) : null} ); }; ListAccordion.displayName = "List.Accordion"; const styles = StyleSheet.create({ container: { padding: 8, }, row: { flexDirection: "row", alignItems: "center", }, multiline: { height: 40, alignItems: "center", justifyContent: "center", }, title: { fontSize: 16, }, description: { fontSize: 14, }, item: { margin: 8, }, child: { paddingLeft: 64, }, content: { flex: 1, justifyContent: "center", }, }); export default ListAccordion;