UNPKG

@oxyhq/services

Version:

Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀

172 lines (160 loc) • 5.66 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = require("react"); var _reactNative = require("react-native"); var _styles = require("../styles"); var _fonts = require("../styles/fonts"); var _jsxRuntime = require("react/jsx-runtime"); /** * Extracts initials from a name string * - Single word: first letter (e.g., "John" -> "J") * - Multiple words: first letter of first and last word (e.g., "John Doe" -> "JD") * - Username starting with @: first two letters after @ (e.g., "@johndoe" -> "JO") */ const getInitials = nameStr => { if (!nameStr?.trim()) return ''; const trimmed = nameStr.trim(); const parts = trimmed.split(/\s+/).filter(part => part.length > 0); if (parts.length === 0) return ''; if (parts.length === 1) { const firstPart = parts[0]; // Handle username format (@username) if (firstPart.length >= 2 && firstPart.startsWith('@')) { return firstPart.substring(1, 3).toUpperCase(); } return firstPart.charAt(0).toUpperCase(); } // Multiple words: first letter of first and last word return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase(); }; /** * Generates a consistent color from a string (name) * Uses a simple hash function to create a deterministic color */ const generateColorFromString = str => { let hash = 0; for (let i = 0; i < str.length; i++) { hash = str.charCodeAt(i) + ((hash << 5) - hash); } // Generate a vibrant color with good contrast const hue = Math.abs(hash) % 360; // Use high saturation and medium lightness for vibrant, readable colors return `hsl(${hue}, 65%, 55%)`; }; /** * Calculates font size based on avatar size for letter avatars * Uses a larger ratio for better visibility */ const calculateFontSize = size => { // Use larger font size for letter avatars (50-55% of size) return size <= 40 ? Math.floor(size * 0.5) : Math.floor(size * 0.45); }; /** * Avatar component that displays either an image or text avatar * Falls back to displaying initials (1-2 letters) derived from the name if no image is provided * Supports flexible sizing via the size prop (default: 40px) * * @example * <Avatar name="John Doe" size={100} theme="light" /> * <Avatar uri="https://example.com/avatar.jpg" size={50} /> */ const Avatar = ({ uri, text, name, size = 40, theme = 'light', backgroundColor, textColor = '#FFFFFF', style, imageStyle, textStyle, isLoading = false }) => { const colors = (0, _styles.useThemeColors)(theme); const displayText = (0, _react.useMemo)(() => text || (name ? getInitials(name) : ''), [text, name]); // Generate a color from the name for letter avatars (only when no backgroundColor is provided and no uri) const generatedBgColor = (0, _react.useMemo)(() => { if (backgroundColor) return backgroundColor; if (uri) return colors.primary; // Use primary for image avatars as fallback // Generate color from name for letter avatars const nameForColor = name || text || 'User'; return generateColorFromString(nameForColor); }, [backgroundColor, uri, name, text, colors.primary]); // Memoize computed values to avoid recalculation on every render const bgColor = (0, _react.useMemo)(() => generatedBgColor, [generatedBgColor]); const fontSize = (0, _react.useMemo)(() => calculateFontSize(size), [size]); const containerStyle = (0, _react.useMemo)(() => ({ width: size, height: size, borderRadius: size / 2 }), [size]); const textStyleComputed = (0, _react.useMemo)(() => [styles.text, { fontSize, fontFamily: _fonts.fontFamilies.phuduBold, color: textColor, textAlign: 'center', lineHeight: size, // Match line height to size for perfect vertical centering ...(_reactNative.Platform.OS === 'android' && { includeFontPadding: false }) // Remove extra padding for better centering (Android only) }, textStyle], [fontSize, textColor, textStyle, size]); // Early return for loading state if (isLoading) { return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: [styles.container, containerStyle, { backgroundColor: colors.inputBackground }, style], children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, { color: colors.primary, size: size > 50 ? 'large' : 'small' }) }); } // Image avatar if (uri) { return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: [styles.container, containerStyle, { backgroundColor: bgColor }, style], children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, { source: { uri }, style: [styles.image, containerStyle, imageStyle], resizeMode: "cover" }) }); } // Text avatar with initials return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, { style: [styles.container, containerStyle, { backgroundColor: bgColor }, style], children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, { style: textStyleComputed, children: displayText }) }); }; const styles = _reactNative.StyleSheet.create({ container: { overflow: 'hidden', justifyContent: 'center', alignItems: 'center' }, image: { width: '100%', height: '100%' }, text: { fontWeight: _reactNative.Platform.OS === 'web' ? 'bold' : undefined } }); // Memoize component to prevent unnecessary re-renders when props haven't changed var _default = exports.default = /*#__PURE__*/(0, _react.memo)(Avatar); //# sourceMappingURL=Avatar.js.map