diff --git a/src/design-system/components/Text/Text.tsx b/src/design-system/components/Text/Text.tsx index 18bc7717225..06a45c47258 100644 --- a/src/design-system/components/Text/Text.tsx +++ b/src/design-system/components/Text/Text.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useMemo, type ReactNode, type Ref } from 'react'; -import { Text as NativeText, type StyleProp, type TextStyle } from 'react-native'; +import { Text as NativeText, type NativeSyntheticEvent, type StyleProp, type TextLayoutEventData, type TextStyle } from 'react-native'; import { SILENCE_EMOJI_WARNINGS } from 'react-native-dotenv'; @@ -30,6 +30,7 @@ export type TextProps = { uppercase?: boolean; weight?: TextWeight; onPress?: () => void; + onTextLayout?: (event: NativeSyntheticEvent) => void; } & ( | { containsEmoji: true; @@ -54,6 +55,7 @@ export function Text({ uppercase, weight, onPress, + onTextLayout, style, ref, }: TextProps) { @@ -95,6 +97,7 @@ export function Text({ style={[textStyle, style || {}]} testID={testID} onPress={onPress} + onTextLayout={onTextLayout} > {IS_IOS && containsEmojiProp && nodeIsString(children) ? renderStringWithEmoji(children) : children} {lineHeightFixNode} diff --git a/src/features/polymarket/screens/polymarket-event-screen/AboutSection.tsx b/src/features/polymarket/screens/polymarket-event-screen/AboutSection.tsx index 14c96b8330e..c15e05ee3a7 100644 --- a/src/features/polymarket/screens/polymarket-event-screen/AboutSection.tsx +++ b/src/features/polymarket/screens/polymarket-event-screen/AboutSection.tsx @@ -5,10 +5,10 @@ import { format } from 'date-fns'; import { getColorValueForThemeWorklet } from '@/__swaps__/utils/swaps'; import ButtonPressAnimation from '@/components/animations/ButtonPressAnimation'; -import { EasingGradient } from '@/components/easing-gradient/EasingGradient'; import { Box, globalColors, Text, TextIcon, TextShadow, useColorMode } from '@/design-system'; import { CATEGORIES } from '@/features/polymarket/constants'; import { type PolymarketEvent, type PolymarketMarketEvent } from '@/features/polymarket/types/polymarket-event'; +import { ExpandableDescriptionCard } from '@/framework/ui/components/ExpandableDescriptionCard'; import { opacity } from '@/framework/ui/utils/opacity'; import * as i18n from '@/languages'; import Navigation from '@/navigation/Navigation'; @@ -54,48 +54,22 @@ const Description = memo(function Description({ const backgroundColor = isDarkMode ? getSolidColorEquivalent({ background: screenBackgroundColor, foreground: '#F5F8FF', opacity: 0.04 }) : getSolidColorEquivalent({ background: screenBackgroundColor, foreground: '#09111F', opacity: 0.02 }); + const borderColor = opacity(isDarkMode ? '#F5F8FF' : '#09111F', 0.02); return ( - { Navigation.handleAction(Routes.POLYMARKET_MARKET_DESCRIPTION_SHEET, { description }); }} scaleTo={0.95} - > - - - {description} - - - - - - - {i18n.t(i18n.l.predictions.event.show_rules)} - - - {'􀆊'} - - - - - - + /> ); }); diff --git a/src/framework/ui/components/ExpandableDescriptionCard.tsx b/src/framework/ui/components/ExpandableDescriptionCard.tsx new file mode 100644 index 00000000000..101df78c4f5 --- /dev/null +++ b/src/framework/ui/components/ExpandableDescriptionCard.tsx @@ -0,0 +1,106 @@ +import { memo, useCallback, useState } from 'react'; +import { StyleSheet, View, type NativeSyntheticEvent, type TextLayoutEventData } from 'react-native'; + +import ButtonPressAnimation from '@/components/animations/ButtonPressAnimation'; +import { EasingGradient } from '@/components/easing-gradient/EasingGradient'; +import { Box, Text, TextIcon } from '@/design-system'; +import { type TextProps } from '@/design-system/components/Text/Text'; + +const CTA_GRADIENT_WIDTH = 100; +const DEFAULT_NUMBER_OF_LINES = 3; + +type ExpandableDescriptionCardProps = { + backgroundColor: string; + borderColor: string; + borderWidth: number; + ctaColor: TextProps['color']; + ctaIcon: string; + ctaLabel: string; + description: string; + onPress: () => void; + numberOfLines?: number; + scaleTo?: number; + textColor?: TextProps['color']; + textSize?: TextProps['size']; + textWeight?: TextProps['weight']; +}; + +export const ExpandableDescriptionCard = memo(function ExpandableDescriptionCard({ + backgroundColor, + borderColor, + borderWidth, + ctaColor, + ctaIcon, + ctaLabel, + description, + onPress, + numberOfLines = DEFAULT_NUMBER_OF_LINES, + scaleTo = 0.95, + textColor = 'labelTertiary', + textSize = '17pt / 150%', + textWeight = 'medium', +}: ExpandableDescriptionCardProps) { + const [showCallToAction, setShowCallToAction] = useState(false); + + const onDescriptionLayout = useCallback( + ({ nativeEvent: { lines } }: NativeSyntheticEvent) => { + const nextShowCallToAction = lines.length >= numberOfLines; + setShowCallToAction(currentValue => (currentValue === nextShowCallToAction ? currentValue : nextShowCallToAction)); + }, + [numberOfLines] + ); + + return ( + + + + {description} + + {showCallToAction && ( + + + + + + {ctaLabel} + + + {ctaIcon} + + + + + )} + + + ); +}); + +const styles = StyleSheet.create({ + callToActionGradient: { + height: 26, + width: CTA_GRADIENT_WIDTH, + }, + callToActionRow: { + bottom: 12, + height: 26, + position: 'absolute', + right: 14, + zIndex: 1, + }, +});