import React, { useState } from 'react'; import { Avatar, AvatarData } from '../atoms/avatar.tsx'; import { Button } from '../atoms/button.tsx'; import { Icon } from '../atoms/icon.tsx'; /** * Preset avatar options for users to choose from */ const PRESET_AVATARS = [ { src: 'https://api.dicebear.com/9.x/thumbs/svg?seed=Felix', label: 'Style 1' }, { src: 'https://api.dicebear.com/9.x/thumbs/svg?seed=Dusty', label: 'Style 2' }, { src: 'https://api.dicebear.com/9.x/thumbs/svg?seed=Mittens', label: 'Style 3' }, { src: 'https://api.dicebear.com/9.x/thumbs/svg?seed=Misty', label: 'Style 4' }, { src: 'https://api.dicebear.com/9.x/thumbs/svg?seed=Bailey', label: 'Style 5' }, { src: 'https://api.dicebear.com/9.x/thumbs/svg?seed=Milo', label: 'Style 6' }, ]; /** * Predefined color options for avatars */ const COLOR_OPTIONS = [ { value: 'bg-blue-500', label: 'Blue' }, { value: 'bg-purple-500', label: 'Purple' }, { value: 'bg-green-500', label: 'Green' }, { value: 'bg-orange-500', label: 'Orange' }, { value: 'bg-red-500', label: 'Red' }, { value: 'bg-pink-500', label: 'Pink' }, { value: 'bg-indigo-500', label: 'Indigo' }, { value: 'bg-yellow-500', label: 'Yellow' }, { value: 'bg-teal-500', label: 'Teal' }, { value: 'bg-cyan-500', label: 'Cyan' }, ]; export interface AvatarEditorProps { /** * Current avatar URL */ currentAvatar?: string; /** * Current avatar background color */ currentColor?: string; /** * Display name for the avatar */ displayName: string; /** * Callback when the editor is closed */ onClose: () => void; /** * Callback when the avatar is saved * @param avatar - The updated avatar data */ onSave: (avatar: Partial) => void; } /** * AvatarEditor component allows users to customize their avatar * * Features: * - Upload custom images * - Enter image URL * - Choose from preset avatars * - Select background colors * - Remove avatar * * TODO: Break up into smaller subcomponents: * - AvatarUploadTab * - AvatarPresetsTab * - AvatarCustomizeTab * - AvatarPreview */ export const AvatarEditor = ({ currentAvatar, currentColor, displayName, onClose, onSave, }: AvatarEditorProps) => { const [avatarUrl, setAvatarUrl] = useState(currentAvatar || ''); const [selectedColor, setSelectedColor] = useState(currentColor || ''); const [customInitials, setCustomInitials] = useState(''); const [activeTab, setActiveTab] = useState<'presets' | 'color'>('presets'); const [error, setError] = useState(''); /** * Handles changes to the custom initials input * Limits input to 2 characters and converts to uppercase * * @param event - The input change event */ const handleInitialsChange = (event: React.ChangeEvent) => { // Limit to 2 characters setCustomInitials(event.target.value.slice(0, 2).toUpperCase()); }; /** * Handles selection of a preset avatar * * @param presetUrl - The URL of the selected preset avatar */ const handlePresetSelect = (presetUrl: string) => { setAvatarUrl(presetUrl); setError(''); }; /** * Handles selection of a background color * * @param color - The selected color value (CSS class) */ const handleColorSelect = (color: string) => { setSelectedColor(color); }; /** * Handles saving the avatar changes * Collects all avatar data and passes it to the onSave callback */ const handleSave = () => { const avatarData: Partial = { displayName, }; if (avatarUrl) { avatarData.src = avatarUrl; } if (selectedColor) { avatarData.color = selectedColor; } if (customInitials) { avatarData.initials = customInitials; } onSave(avatarData); onClose(); }; /** * Handles removing the avatar * Clears the avatar URL and passes updated data to the onSave callback */ const handleRemove = () => { setAvatarUrl(''); onSave({ displayName, src: undefined }); onClose(); }; /** * Generates initials from the display name or returns custom initials if set * * @returns The initials to display in the avatar (max 2 characters) */ const getInitials = () => { if (customInitials) return customInitials; return displayName .split(' ') .filter((name) => name.length > 0) .map((name) => name[0]?.toUpperCase() || '') .join('') .padEnd(2, '•') .slice(0, 2); }; return (

Edit Avatar

{/* Avatar Preview */}
{/* Tabs */}
{/* Presets Tab Content */} {activeTab === 'presets' && (
{PRESET_AVATARS.map((preset, index) => (
{ handlePresetSelect(preset.src); }} >
{preset.label}
))}
)} {/* Color Tab Content */} {activeTab === 'color' && (
{COLOR_OPTIONS.map((color) => (
{ handleColorSelect(color.value); }} title={color.label} /> ))}
)} {/* Error Message */} {error &&
{error}
} {/* Actions */}
{(currentAvatar || avatarUrl) && ( )}
); };