import React, { useMemo } from 'react'; import { ScrollView, StyleSheet, View, type ViewStyle } from 'react-native'; import type { TContainer, TComponent } from '@namiml/sdk-core'; import { applyStyles, childSpacingStyle, flexDirectionFromConfig } from '../../utils/styles'; import { usePaywallContext } from '../../context/PaywallContext'; import { TemplateRenderer } from '../TemplateRenderer'; interface Props { component: TContainer; scaleFactor: number; onClose?: () => void; } function splitContainerStyles(style: ViewStyle): { outer: ViewStyle; inner: ViewStyle } { const { paddingLeft, paddingRight, paddingTop, paddingBottom, flexDirection, alignItems, justifyContent, ...rest } = style; const outer: ViewStyle = { ...rest }; // The content container is always the page's vertical fill region: styles.outer // declares flex:1 and the body scrolls internally. A payload height of // 'fitContent' makes sizeStyles emit flexGrow:0, which would otherwise override // that fill and collapse the body to 0 height — the flex:1 ScrollView child // can't give an unbounded parent a height. Drop the flex-sizing keys so the // fill stays authoritative, matching the native renderers which always // fill-and-scroll this region regardless of the authored height. delete outer.flex; delete outer.flexGrow; delete outer.flexBasis; delete outer.flexShrink; const inner: ViewStyle = { ...(paddingLeft != null ? { paddingLeft } : {}), ...(paddingRight != null ? { paddingRight } : {}), ...(paddingTop != null ? { paddingTop } : {}), ...(paddingBottom != null ? { paddingBottom } : {}), ...(flexDirection ? { flexDirection } : {}), ...(alignItems ? { alignItems } : {}), ...(justifyContent ? { justifyContent } : {}), }; return { outer, inner }; } export const NamiContentContainer: React.FC = ({ component, scaleFactor, onClose, }) => { const ctx = usePaywallContext(); if (!component || component.hidden) return null; const baseStyle = useMemo( () => applyStyles(component as any, scaleFactor) as ViewStyle, [component, scaleFactor] ); const { outer, inner } = splitContainerStyles(baseStyle); const formFactor = ctx.state.formFactor; const scrollEnabled = formFactor !== 'television'; const direction = component.direction ?? 'vertical'; const flexDir = flexDirectionFromConfig(direction); const contentAlignment = useMemo(() => { const vertical = component.verticalAlignment; const horizontal = component.horizontalAlignment; const isRow = flexDir === 'row' || flexDir === 'row-reverse'; return { alignItems: isRow ? (vertical === 'top' ? 'flex-start' : vertical === 'bottom' ? 'flex-end' : vertical === 'center' ? 'center' : 'center') : (horizontal === 'left' ? 'flex-start' : horizontal === 'right' ? 'flex-end' : horizontal === 'center' ? 'center' : 'center'), justifyContent: isRow ? (horizontal === 'left' ? 'flex-start' : horizontal === 'right' ? 'flex-end' : horizontal === 'center' ? 'center' : 'flex-start') : (vertical === 'top' ? 'flex-start' : vertical === 'bottom' ? 'flex-end' : vertical === 'center' ? 'center' : 'flex-start'), } as const; }, [component.verticalAlignment, component.horizontalAlignment, flexDir]); const renderedChildren = useMemo(() => { return component.components?.map((comp: TComponent, i: number) => ( )); }, [component.components, component.spacing, direction, scaleFactor, onClose]); const innerContentStyle = useMemo( () => [ styles.inner, { flexDirection: flexDir, }, inner, contentAlignment, ], [flexDir, inner, contentAlignment], ); if (!scrollEnabled) { return ( {renderedChildren} ); } return ( {renderedChildren} ); }; const styles = StyleSheet.create({ outer: { flex: 1, minHeight: 0, zIndex: 2, }, scroll: { flex: 1, }, inner: { width: '100%', flexGrow: 1, }, fullWidth: { width: '100%', }, });