import React, { useState } from 'react'; import { View, Text, ScrollView, StyleSheet, Pressable, Alert, } from 'react-native'; import { Ux4gAvatar, Ux4gAvatarGroup, Ux4gProfileAvatar, Ux4gStatusAvatar, Ux4gAvatarSize, Ux4gAvatarShape, Ux4gStatusVariant, Ux4gProfileBadge, Ux4gProfileAction, Ux4gAvatarGroupItem, } from '../components/avatar/Avatar'; import { useUx4gTheme } from '../theme/Ux4gThemeContext'; const ALL_SIZES: Ux4gAvatarSize[] = ['xs', 's', 'm', 'l', 'xl', 'xxl', 'xxxl']; const ALL_STATUSES: Ux4gStatusVariant[] = ['online', 'offline', 'busy', 'success', 'error', 'warning']; const PLACEHOLDER_AVATAR = 'https://i.pravatar.cc/150?u=ux4g'; const GROUP_ITEMS: Ux4gAvatarGroupItem[] = [ { imageUrl: 'https://i.pravatar.cc/150?u=user1', onPress: () => Alert.alert('User 1') }, { imageUrl: 'https://i.pravatar.cc/150?u=user2', onPress: () => Alert.alert('User 2') }, { imageUrl: 'https://i.pravatar.cc/150?u=user3', onPress: () => Alert.alert('User 3') }, { initials: 'JD', onPress: () => Alert.alert('John Doe') }, { initials: 'AS', onPress: () => Alert.alert('Alice Smith') }, { initials: 'MK', onPress: () => Alert.alert('Mike Kim') }, ]; export const AvatarShowcase: React.FC = () => { const theme = useUx4gTheme(); const colors = theme.colors; const [activeSize, setActiveSize] = useState('m'); const [activeShape, setActiveShape] = useState('circle'); const [showImage, setShowImage] = useState(false); const cardStyle = [ styles.card, { backgroundColor: theme.isDark ? '#1F1F1F' : '#FFFFFF', borderColor: theme.isDark ? '#333333' : '#E4E4E7', }, ]; const titleStyle = [ styles.sectionTitle, { color: theme.isDark ? '#F4F4F5' : '#18181B' }, ]; const subtitleStyle = [ styles.subText, { color: theme.isDark ? '#A1A1AA' : '#71717A' }, ]; return ( 👤 Avatar Component React Native port of Flutter `avatar.dart`. Supports 7 sizes (xs–xxxl), image/initials/icon content, circular/rounded/square shapes, profile badges, status indicators, and grouped avatars. {/* 1. All Sizes */} 1. All Sizes (initials) `Ux4gAvatar` at each size (xs=24, s=32, m=40, l=48, xl=64, xxl=80, xxxl=120). {ALL_SIZES.map((sz) => ( {sz} ))} {/* 2. Content Priority */} 2. Content Priority Priority: image → initials → icon fallback. Image Initials Icon (default) {/* 3. Shapes */} 3. Shapes `shape` prop: `circle` (default), `rounded`, `square`. {(['circle', 'rounded', 'square'] as Ux4gAvatarShape[]).map((sh) => ( {sh} ))} {/* 4. ProfileAvatar — Badges */} 4. ProfileAvatar — Badges `Ux4gProfileAvatar` with `variant` badges: verified, star, admin. {(['verified', 'star', 'admin'] as Ux4gProfileBadge[]).map((badge) => ( Alert.alert('Badge', badge)} /> {badge} ))} {/* 5. ProfileAvatar — Actions */} 5. ProfileAvatar — Actions `Ux4gProfileAvatar` with `variant` actions: edit, camera, remove. {(['edit', 'camera', 'remove'] as Ux4gProfileAction[]).map((action) => ( Alert.alert('Action', action)} /> {action} ))} {/* 6. ProfileAvatar — with Image */} 6. ProfileAvatar — with Image ProfileAvatar combining a network image with an edit badge. Alert.alert('Edit photo')} /> Alert.alert('Take photo')} /> {/* 7. StatusAvatar */} 7. StatusAvatar `Ux4gStatusAvatar` with status variants: online, offline, busy, success, error, warning. {ALL_STATUSES.map((status) => ( {status} ))} {/* 8. StatusAvatar — with Image */} 8. StatusAvatar — with Image Status indicator overlaid on network image avatars. {/* 9. AvatarGroup — Overlapping */} 9. AvatarGroup — Overlapping `Ux4gAvatarGroup` with `collapsed: true` — avatars overlap with a negative gap. Alert.alert('Show all')} /> {/* 10. AvatarGroup — Max Limit */} 10. AvatarGroup — Max Limit `maxLimit` limits visible avatars; remaining shown as `+N` indicator. Alert.alert('Show 3 more')} /> {/* 11. AvatarGroup — Non-collapsed */} 11. AvatarGroup — Non-collapsed `collapsed: false` — avatars are spaced with 8px gaps instead of overlapping. {/* 12. Interactive Controls */} 12. Interactive Controls Toggle size, shape, and image vs initials using the controls below. Size: {ALL_SIZES.map((sz) => ( setActiveSize(sz)} style={[ styles.pill, { borderColor: colors.primary, backgroundColor: activeSize === sz ? colors.primary : 'transparent', }, ]} > {sz} ))} Shape: {(['circle', 'rounded', 'square'] as Ux4gAvatarShape[]).map((sh) => ( setActiveShape(sh)} style={[ styles.pill, { borderColor: colors.primary, backgroundColor: activeShape === sh ? colors.primary : 'transparent', }, ]} > {sh} ))} setShowImage(!showImage)} style={[styles.togglePill, { borderColor: colors.primary }]} > Image: {showImage ? 'ON' : 'OFF'} ); }; const styles = StyleSheet.create({ container: { flex: 1, }, contentContainer: { padding: 16, paddingBottom: 40, }, header: { marginBottom: 16, }, title: { fontSize: 22, fontWeight: '800', marginBottom: 6, }, subText: { fontSize: 14, lineHeight: 20, }, card: { borderWidth: 1, borderRadius: 16, padding: 16, marginBottom: 16, overflow: 'visible', }, sectionTitle: { fontSize: 16, fontWeight: '700', marginBottom: 4, }, sizeRow: { flexDirection: 'row', alignItems: 'flex-end', justifyContent: 'space-around', marginTop: 20, paddingVertical: 12, }, sizeItem: { alignItems: 'center', gap: 8, }, sizeLabel: { fontSize: 11, fontWeight: '600', }, contentRow: { flexDirection: 'row', justifyContent: 'space-around', alignItems: 'center', marginTop: 16, gap: 12, }, contentItem: { alignItems: 'center', gap: 8, }, caption: { fontSize: 11, fontWeight: '600', }, statusGrid: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'center', gap: 16, marginTop: 16, }, statusItem: { alignItems: 'center', gap: 6, }, previewBox: { padding: 20, borderRadius: 12, marginTop: 16, }, controlRow: { flexDirection: 'row', flexWrap: 'wrap', alignItems: 'center', gap: 8, marginTop: 12, }, controlLabel: { fontSize: 14, fontWeight: '700', }, pillRow: { flexDirection: 'row', gap: 6, flexWrap: 'wrap', }, pill: { paddingHorizontal: 10, paddingVertical: 6, borderWidth: 1, borderRadius: 999, }, togglePill: { paddingHorizontal: 12, paddingVertical: 8, borderWidth: 1, borderRadius: 999, }, });