import { forwardRef, useCallback, useMemo, useState, type ComponentRef, } from 'react'; import { StyleSheet, View, type LayoutChangeEvent, type NativeSyntheticEvent, } from 'react-native'; import NativeMarqueeView from './RNMarqueeViewNativeComponent'; import { resolveAnimationState } from './internal/events'; import { resolveMeasuredContentWidth } from './internal/geometry'; import { resolveInteraction, resolveSpacing, resolveSpeed, } from './internal/options'; import type { MarqueeProps } from './types'; import { useMarqueeReducedMotion } from './use-reduced-motion'; type NativeAnimationStateEvent = NativeSyntheticEvent< Readonly<{ state: string }> >; type NativeContentLayoutEvent = NativeSyntheticEvent< Readonly<{ containerWidth: number; contentWidth: number }> >; type NativeContentPressEvent = NativeSyntheticEvent>; const NATIVE_SHORT_CONTENT_MODES = { repeat: 'repeat', static: 'static', } as const; export const Marquee = forwardRef< ComponentRef, MarqueeProps >((props, ref) => { const { accessibilityLabel, active = true, children, contentAlignment = 'start', contentContainerStyle, direction = 'left', interaction, onAnimationStateChange, onContentLayout, onContentPress, reduceMotion: reduceMotionPolicy = 'system', shortContentMode = 'static', spacing: spacingProp = 0, speed: speedProp = 25, style, ...viewProps } = props; const [contentWidth, setContentWidth] = useState(0); const reduceMotion = useMarqueeReducedMotion(reduceMotionPolicy); const resolvedInteraction = useMemo( () => resolveInteraction(interaction), [interaction] ); const speed = resolveSpeed(speedProp); const spacing = resolveSpacing(spacingProp); const handleContentContainerLayout = useCallback( (event: LayoutChangeEvent) => { const nextWidth = event.nativeEvent.layout.width; setContentWidth((currentWidth) => resolveMeasuredContentWidth(currentWidth, nextWidth) ); }, [] ); const handleAnimationStateChange = useCallback( (event: NativeAnimationStateEvent) => { const state = resolveAnimationState(event.nativeEvent.state); if (state) { onAnimationStateChange?.({ state }); } }, [onAnimationStateChange] ); const handleContentLayout = useCallback( (event: NativeContentLayoutEvent) => { onContentLayout?.(event.nativeEvent); }, [onContentLayout] ); const handleContentPress = useCallback( (event: NativeContentPressEvent) => { onContentPress?.({ x: event.nativeEvent.x }); }, [onContentPress] ); return ( {children} ); }); Marquee.displayName = 'Marquee'; const styles = StyleSheet.create({ content: { alignSelf: 'flex-start', flexDirection: 'row', flexShrink: 0, }, });