import { LinearTransition } from 'react-native-reanimated'; import { CheckLine as Check } from 'rn-motion-ui-icons/icons/check-line'; import { InformationLine as AlertCircle } from 'rn-motion-ui-icons/icons/information-line'; import { EASE_OUT } from '../../../lib/ease'; import { MotiView } from '../../../moti/components/view'; import { AnimatePresence } from '../../../moti/presence/animate-presence'; import { useThemeColors } from '../../../theme/use-theme-color'; import { Loader } from '../../display/Loader/loader'; import { ThemedIcon } from '../../icon/themed-icon'; import { type ActionFeedbackState, MORPH_CONTAINER_TRANSITION, MORPH_GLYPH_TRANSITION, MORPH_SPINNER_TRANSITION, RM_TRANSITION, } from './action-feedback-motion'; // A single circular vessel that morphs its size + fill colour as `state` // changes, while the glyph inside cross-fades (spinner ↔ check ↔ close). The // icon persists across state transitions so the morph reads as one continuous // shape-change rather than three static icons swapping in/out. Ported from a // web MorphIcon (framer-motion → moti). // // Purely decorative: the outcome is carried by the text beside it, which sits // in a live region, so the vessel and its glyphs stay out of the a11y tree // rather than announcing "image" between the announcements that matter. const MORPH_SIZE: Record = { loading: 40, success: 44, error: 44 }; /** Vessel resize rides a Fabric-safe layout transition instead of animating * `width`/`height` through `useAnimatedStyle` (layout props don't round-trip * Yoga on Fabric). Timing mirrors `MORPH_CONTAINER_TRANSITION`. */ const MORPH_LAYOUT = LinearTransition.duration(300).easing(EASE_OUT); export type MorphIconProps = { state: ActionFeedbackState; reduced: boolean }; export function MorphIcon({ state, reduced }: MorphIconProps) { const colors = useThemeColors(); const morphBackground: Record = { loading: 'transparent', success: colors.success, error: colors.danger, }; const size = MORPH_SIZE[state]; const backgroundColor = morphBackground[state]; return ( {state === 'loading' && ( )} {state === 'success' && ( )} {state === 'error' && ( )} ); }