import React, { useEffect, useMemo, useRef } from 'react'; import { Platform, Pressable, StyleSheet, Text, View } from 'react-native'; import { useFirstFocusReadyContext, usePaywallContext } from '../../context/PaywallContext'; import { applyStyles, childSpacingStyle, extractPrefixedStyles, parseColor, parseSize, shadowStyles, textStyles, } from '../../utils/styles'; import { handleAction } from '../../utils/actionHandler'; import { TemplateRenderer } from '../TemplateRenderer'; import type { TComponent } from '@namiml/sdk-core'; import { FocusedStyleProvider, useFocusableState, useFocusEnabled, useRegisterPreferredFocus } from '../../context/FocusContext'; import { useTVPreferredFocus } from '../../utils/tvFocus'; interface Props { component: any; scaleFactor: number; onClose?: () => void; parentDirection?: string; parentFormId?: string; parentOnTap?: { function: string; parameters?: Record }; } export const NamiSegmentPickerItem: React.FC = ({ component, scaleFactor, onClose, parentDirection, parentFormId, parentOnTap, }) => { const ctx = usePaywallContext(); const focusReadyCtx = useFirstFocusReadyContext(); const focusEnabled = useFocusEnabled(); const pressableRef = useRef(null); const { isFocused, onFocus, onBlur } = useFocusableState( Boolean(component.focused || component.checked), focusEnabled, ); useTVPreferredFocus( pressableRef, focusEnabled && Boolean(component.focused || component.checked), ); useRegisterPreferredFocus( pressableRef.current, focusEnabled && Boolean(component.focused || component.checked), ); const selectedFormValue = parentFormId ? ctx.state.formStates?.[parentFormId] : ''; const currentGroupId = ctx.state.currentGroupId ?? ''; const isActive = component.formId ? (selectedFormValue ? selectedFormValue === component.formId : !!component.checked) : (currentGroupId ? component.id === currentGroupId : !!component.checked); useEffect(() => { if (!component.checked) return; if (component.formId && parentFormId) { if (!selectedFormValue) { ctx.setFormState(parentFormId, component.formId); } return; } if (!ctx.state.currentGroupId && component.id) { ctx.setCurrentGroupData(component.id, component.text ?? ''); } }, [ component.checked, component.formId, component.id, component.text, parentFormId, selectedFormValue, ctx, ctx.state.currentGroupId, ]); const activeStyles = extractPrefixedStyles(component, 'active'); const inactiveStyles = extractPrefixedStyles(component, 'inactive'); const merged = { ...component, ...(isActive ? activeStyles : inactiveStyles) }; const itemStyle = useMemo(() => { const base = applyStyles(merged, scaleFactor, isFocused, parentDirection); const fill = parseColor(merged.fillColor ?? merged.selectedFillColor); if (fill) base.backgroundColor = fill; if (merged.borderRadius != null) { base.borderRadius = parseSize(merged.borderRadius, scaleFactor); } if (merged.dropShadow) { Object.assign(base, shadowStyles({ ...merged, dropShadow: merged.dropShadow } as any)); } return base; }, [merged, scaleFactor, parentDirection, isFocused]); // NAM-1213 CTV: focused fill overlay. When the remote is on this item, draw an // absolute-fill View with focusedFillColor over the active/inactive background — // same full-cell geometry as the active fill (design doc §6b fill-rule). // Item border when focused defaults to NONE if focusedBorder* are not authored (§2). const focusedOverlayColor = useMemo(() => { if (!focusEnabled || !isFocused) return null; return parseColor(component.focusedFillColor) ?? null; }, [focusEnabled, isFocused, component.focusedFillColor]); const onPress = () => { if (component.formId && parentFormId) { ctx.setFormState(parentFormId, component.formId); } else if (component.id) { ctx.setCurrentGroupData(component.id, component.text ?? ''); } if (component.onTap) { handleAction({ onTap: component.onTap, ctx, onClose, componentChange: { id: component.id, name: component.title ?? component.text, }, }); } if (parentOnTap) { handleAction({ onTap: parentOnTap, ctx, onClose, componentChange: { id: component.id, name: component.title ?? component.text, }, }); } }; const handleFocus = () => { onFocus(); focusReadyCtx.notifyFirstFocusReady( ctx.state.selectedPaywall?.id ?? 'unknown', ctx.state.currentPage ?? 'unknown', ctx.state.formFactor, ); }; const direction = component.direction ?? 'horizontal'; return ( [styles.item, itemStyle, pressed ? styles.pressed : null]} > {/* NAM-1213 CTV: focused fill overlay — drawn over the active/inactive background, under the text/children, spanning the full cell (same geometry as active fill). Only shown when the remote is on this item and focusedFillColor is authored. */} {focusedOverlayColor != null && ( )} {component.components?.length ? ( component.components.map((child: TComponent, i: number) => ( )) ) : ( {component.text ?? component.title ?? ''} )} ); }; const styles = StyleSheet.create({ item: { minWidth: 0 }, pressed: { opacity: 0.7 }, });