diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 8e557da1..aacaceef 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -636,7 +636,7 @@ EXTERNAL SOURCES: :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" hermes-engine: :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" - :tag: hermes-2023-03-07-RNv0.71.4-31fdcf738940875c9bacf251e149006cf515d763 + :tag: hermes-2023-03-20-RNv0.72.0-49794cfc7c81fb8f69fd60c3bbf85a7480cc5a77 RCT-Folly: :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" RCTRequired: diff --git a/example/src/navigation/navigator.tsx b/example/src/navigation/navigator.tsx index 851c5695..2a6501f0 100644 --- a/example/src/navigation/navigator.tsx +++ b/example/src/navigation/navigator.tsx @@ -103,6 +103,7 @@ const AppNavigator = () => ( name={APP_ROUTES.COMPONENTS.ACCORDION.route} component={Accordion} options={{ + presentation: "modal", headerTitle: APP_ROUTES.COMPONENTS.ACCORDION.title, headerBackTitleVisible: false }} diff --git a/example/src/pages/Accordion.tsx b/example/src/pages/Accordion.tsx index 7d5c76b3..fcbf23c5 100644 --- a/example/src/pages/Accordion.tsx +++ b/example/src/pages/Accordion.tsx @@ -1,44 +1,90 @@ import { + AccordionItem, Body, H3, H5, - IOAccordion, IOColors, IOStyles, IOVisualCostants, Icon, - Label, RawAccordion, VSpacer } from "@pagopa/io-app-design-system"; import * as React from "react"; -import { View } from "react-native"; +import { FlatList, ListRenderItemInfo, View } from "react-native"; import { Screen } from "../components/Screen"; -export const Accordion = () => ( - - - - - - - - - - - <> - - - - - +const assistanceData: Array = [ + { + id: 1, + title: "Come posso pagare su IO?", + body: "Puoi pagare con carte di debito, credito e prepagate, con PayPal o BANCOMAT Pay." + }, + { + id: 2, + title: "Come posso eliminare un metodo di pagamento?", + body: "I tuoi metodi di pagamento sono visualizzati come card nella parte alta dello schermo del Portafoglio. Seleziona la card del metodo che vuoi eliminare e poi premi 'Elimina questo metodo!" + }, + { + id: 3, + title: + "Nel 2021 ho maturato degli importi che non mi sono ancora stati rimborsati. Cosa posso fare?", + body: "Probabilmente non hai indicato l'IBAN del conto su cui desideri ricevere l'accredito, oppure quello che hai indicato non è valido.Per inserire l'IBAN o verificarne la correttezza, apri l'app, vai al Portafoglio e premi sulla card del Cashback. Poi, inseriscilo o modificalo e seleziona 'Continua'. Entro 90 giorni riceverai tramite l'app 10 un messaggio sullo stato del rimborso. Le attività tra PagoPA S.p.A. e Consap S.p.A. per i pagamenti dei rimborsi sono in corso di dismissione, quindi, se non inserisci o non correggi l'IBAN entro il 31/07/2022, potrebbe non essere più possibile effettuare il bonifico per il rimborso." + }, + { + id: 4, + title: "Come posso eliminare un metodo di pagamento?", + body: ( + + I tuoi metodi di pagamento sono visualizzati come card nella parte alta + dello schermo del Portafoglio. Seleziona la card del metodo che vuoi + eliminare e poi premi Elimina questo metodo! + + ) + }, + { + id: 5, + title: + "Nel 2021 ho maturato degli importi che non mi sono ancora stati rimborsati. Cosa posso fare?", + body: "Probabilmente non hai indicato l'IBAN del conto su cui desideri ricevere l'accredito, oppure quello che hai indicato non è valido.Per inserire l'IBAN o verificarne la correttezza, apri l'app, vai al Portafoglio e premi sulla card del Cashback. Poi, inseriscilo o modificalo e seleziona 'Continua'. Entro 90 giorni riceverai tramite l'app 10 un messaggio sullo stato del rimborso. Le attività tra PagoPA S.p.A. e Consap S.p.A. per i pagamenti dei rimborsi sono in corso di dismissione, quindi, se non inserisci o non correggi l'IBAN entro il 31/07/2022, potrebbe non essere più possibile effettuare il bonifico per il rimborso." + } +]; + +export const Accordion = () => { + // const renderAccordionHeader = () => ( + // + //

Accordion

+ //
+ // ); + + const renderItem = ({ item }: ListRenderItemInfo) => ( + + ); + + return ( + + } + keyExtractor={(item, index) => `${item.id}-${index}`} + renderItem={renderItem} + /> + + {renderRawAccordion()} + + ); +}; + +const renderRawAccordion = () => ( + <> - +

RawAccordion

( -
+ ); diff --git a/src/components/accordion/AccordionItem.tsx b/src/components/accordion/AccordionItem.tsx new file mode 100644 index 00000000..a3262530 --- /dev/null +++ b/src/components/accordion/AccordionItem.tsx @@ -0,0 +1,165 @@ +import React, { useState } from "react"; +import { + Text, + View, + StyleSheet, + TouchableWithoutFeedback, + LayoutChangeEvent +} from "react-native"; +import Animated, { + useAnimatedStyle, + withSpring +} from "react-native-reanimated"; +import LinearGradient from "react-native-linear-gradient"; +import { IOAccordionRadius, type IOSpacingScale } from "../../core"; +import { makeFontStyleObject } from "../../utils/fonts"; +import { IOColors, hexToRgba } from "../../core/IOColors"; +import { H6 } from "../typography"; +import { IOSpringValues } from "../../core/IOAnimations"; +import { Icon } from "../icons/Icon"; + +export type AccordionItem = { + id: number; + title: string; + body: string | React.ReactNode; +}; + +type AccordionBody = { + children: React.ReactNode; + expanded: boolean; +}; + +const accordionBodySpacing: IOSpacingScale = 16; +const accordionIconMargin: IOSpacingScale = 8; +const accordionBorder: IOColors = "grey-200"; +const accordionBackground: IOColors = "white"; + +/* The code below is a re-adaptation of Dima Portenko's code: +https://github.com/dimaportenko/reanimated-collapsable-card-tutorial +*/ +export const AccordionBody = ({ children, expanded }: AccordionBody) => { + const [height, setHeight] = useState(0); + + const onLayout = (event: LayoutChangeEvent) => { + const { height: onLayoutHeight } = event.nativeEvent.layout; + + if (onLayoutHeight > 0 && height !== onLayoutHeight) { + setHeight(onLayoutHeight); + } + }; + + const animatedHeightStyle = useAnimatedStyle( + () => ({ + height: expanded + ? withSpring(height, IOSpringValues.accordion) + : withSpring(0, IOSpringValues.accordion) + }), + [expanded] + ); + + return ( + + + {children} + + + ); +}; + +export const AccordionItem = ({ title, body }: AccordionItem) => { + const [expanded, setExpanded] = useState(false); + + const onItemPress = () => { + setExpanded(!expanded); + }; + + const animatedChevron = useAnimatedStyle( + () => ({ + transform: [ + { + rotate: expanded + ? withSpring(`180deg`, IOSpringValues.accordion) + : withSpring(`0deg`, IOSpringValues.accordion) + } + ] + }), + [expanded] + ); + + return ( + + + + +
{title}
+
+ + + +
+
+ + + {typeof body === "string" ? ( + {body} + ) : ( + body + )} + + {/* This gradient adds a smooth end to the content. If it is missing, + the content will be cut sharply during the height transition. */} + +
+ ); +}; + +const styles = StyleSheet.create({ + accordionWrapper: { + borderColor: IOColors[accordionBorder], + borderWidth: 1, + borderRadius: IOAccordionRadius, + backgroundColor: IOColors[accordionBackground] + }, + accordionCollapsableContainer: { + overflow: "hidden" + }, + accordionBodyContainer: { + position: "absolute", + padding: accordionBodySpacing, + paddingTop: 0 + }, + accordionBodyText: { + fontSize: 14, + lineHeight: 21, + color: IOColors["grey-700"], + ...makeFontStyleObject("Regular", undefined, "TitilliumWeb") + }, + textContainer: { + padding: accordionBodySpacing, + flexGrow: 1, + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between" + } +}); diff --git a/src/components/accordion/IOAccordion.tsx b/src/components/accordion/IOAccordion.tsx deleted file mode 100644 index 35692259..00000000 --- a/src/components/accordion/IOAccordion.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import * as React from "react"; -import { StyleSheet } from "react-native"; -import { H3 } from "../typography/H3"; -import { IOStyles, IOVisualCostants } from "../../core/IOStyles"; -import { RawAccordion } from "./RawAccordion"; - -type Props = Omit, "header"> & { - title: string; -}; - -const styles = StyleSheet.create({ - header: { - marginVertical: IOVisualCostants.appMarginDefault - } -}); - -/** - * A simplified accordion that accepts a title and one child and uses {@link RawAccordion} - * @param props - * @constructor - */ -export const IOAccordion = (props: Props): React.ReactElement => ( - - {props.title} - - } - > - {props.children} - -); diff --git a/src/components/accordion/index.tsx b/src/components/accordion/index.tsx index 9317f876..62a3aa61 100644 --- a/src/components/accordion/index.tsx +++ b/src/components/accordion/index.tsx @@ -1,2 +1,2 @@ export * from "./RawAccordion"; -export * from "./IOAccordion"; +export * from "./AccordionItem"; diff --git a/src/core/IOAnimations.ts b/src/core/IOAnimations.ts index 89848090..831921e5 100644 --- a/src/core/IOAnimations.ts +++ b/src/core/IOAnimations.ts @@ -9,6 +9,11 @@ export const IOSpringValues = { mass: 0.5, stiffness: 300 }, + accordion: { + damping: 30, + mass: 1, + stiffness: 325 + }, /* Used by selection items (checkbox, radio, etc…) */ selection: { damping: 10, diff --git a/src/core/IOShapes.ts b/src/core/IOShapes.ts index 03e3fa2c..cfaec68b 100644 --- a/src/core/IOShapes.ts +++ b/src/core/IOShapes.ts @@ -18,3 +18,4 @@ export const IOTagRadius: IORadiusScale = 6; export const IOBottomSheetHeaderRadius: IORadiusScale = 24; export const IOListItemIDPRadius: IORadiusScale = IODefaultRadius; export const IOBadgeRadius: IORadiusScale = 24; +export const IOAccordionRadius: IORadiusScale = IODefaultRadius;