T.ME/BIBIL_0DAY
CasperSecurity


Server : Apache/2
System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User : gositeme ( 1004)
PHP Version : 8.2.29
Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Directory :  /home/gositeme/domains/lavocat.quebec/private_html/src/components/ui/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/lavocat.quebec/private_html/src/components/ui/EmojiPicker.tsx
import React, { useState, useRef, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';

interface EmojiPickerProps {
  onEmojiSelect: (emoji: string) => void;
  isOpen: boolean;
  onClose: () => void;
  position?: 'top' | 'bottom';
}

const EMOJI_CATEGORIES = {
  'Smileys & People': ['๐Ÿ˜€', '๐Ÿ˜ƒ', '๐Ÿ˜„', '๐Ÿ˜', '๐Ÿ˜†', '๐Ÿ˜…', '๐Ÿ˜‚', '๐Ÿคฃ', '๐Ÿ˜Š', '๐Ÿ˜‡', '๐Ÿ™‚', '๐Ÿ™ƒ', '๐Ÿ˜‰', '๐Ÿ˜Œ', '๐Ÿ˜', '๐Ÿฅฐ', '๐Ÿ˜˜', '๐Ÿ˜—', '๐Ÿ˜™', '๐Ÿ˜š', '๐Ÿ˜‹', '๐Ÿ˜›', '๐Ÿ˜', '๐Ÿ˜œ', '๐Ÿคช', '๐Ÿคจ', '๐Ÿง', '๐Ÿค“', '๐Ÿ˜Ž', '๐Ÿคฉ', '๐Ÿฅณ', '๐Ÿ˜', '๐Ÿ˜’', '๐Ÿ˜ž', '๐Ÿ˜”', '๐Ÿ˜Ÿ', '๐Ÿ˜•', '๐Ÿ™', 'โ˜น๏ธ', '๐Ÿ˜ฃ', '๐Ÿ˜–', '๐Ÿ˜ซ', '๐Ÿ˜ฉ', '๐Ÿฅบ', '๐Ÿ˜ข', '๐Ÿ˜ญ', '๐Ÿ˜ค', '๐Ÿ˜ ', '๐Ÿ˜ก', '๐Ÿคฌ', '๐Ÿคฏ', '๐Ÿ˜ณ', '๐Ÿฅต', '๐Ÿฅถ', '๐Ÿ˜ฑ', '๐Ÿ˜จ', '๐Ÿ˜ฐ', '๐Ÿ˜ฅ', '๐Ÿ˜“', '๐Ÿค—', '๐Ÿค”', '๐Ÿคญ', '๐Ÿคซ', '๐Ÿคฅ', '๐Ÿ˜ถ', '๐Ÿ˜', '๐Ÿ˜‘', '๐Ÿ˜ฌ', '๐Ÿ™„', '๐Ÿ˜ฏ', '๐Ÿ˜ฆ', '๐Ÿ˜ง', '๐Ÿ˜ฎ', '๐Ÿ˜ฒ', '๐Ÿฅฑ', '๐Ÿ˜ด', '๐Ÿคค', '๐Ÿ˜ช', '๐Ÿ˜ต', '๐Ÿค', '๐Ÿฅด', '๐Ÿคข', '๐Ÿคฎ', '๐Ÿคง', '๐Ÿ˜ท', '๐Ÿค’', '๐Ÿค•'],
  'Hearts & Love': ['โค๏ธ', '๐Ÿงก', '๐Ÿ’›', '๐Ÿ’š', '๐Ÿ’™', '๐Ÿ’œ', '๐Ÿ–ค', '๐Ÿค', '๐ŸคŽ', '๐Ÿ’”', 'โฃ๏ธ', '๐Ÿ’•', '๐Ÿ’ž', '๐Ÿ’“', '๐Ÿ’—', '๐Ÿ’–', '๐Ÿ’˜', '๐Ÿ’', '๐Ÿ’Ÿ'],
  'Gestures': ['๐Ÿ‘', '๐Ÿ‘Ž', '๐Ÿ‘Œ', '๐ŸคŒ', '๐Ÿค', 'โœŒ๏ธ', '๐Ÿคž', '๐ŸคŸ', '๐Ÿค˜', '๐Ÿค™', '๐Ÿ‘ˆ', '๐Ÿ‘‰', '๐Ÿ‘†', '๐Ÿ–•', '๐Ÿ‘‡', 'โ˜๏ธ', '๐Ÿ‘', '๐Ÿ™Œ', '๐Ÿ‘', '๐Ÿคฒ', '๐Ÿค', '๐Ÿ™'],
  'Celebrations': ['๐ŸŽ‰', '๐ŸŽŠ', '๐Ÿฅณ', '๐ŸŽˆ', '๐ŸŽ', '๐Ÿ†', '๐Ÿฅ‡', '๐ŸŽ–๏ธ', '๐Ÿ…', 'โญ', '๐ŸŒŸ', 'โœจ', '๐Ÿ”ฅ', '๐Ÿ’ฏ'],
  'Objects': ['๐Ÿ’ฌ', '๐Ÿ’ญ', '๐Ÿ’ก', '๐Ÿ’Ž', '๐Ÿ””', '๐Ÿ“ข', '๐Ÿ“ฃ', '๐Ÿ“ฏ', '๐ŸŽฏ', '๐ŸŽช', '๐ŸŽญ', '๐ŸŽจ', '๐ŸŽฌ', '๐Ÿ“ท', '๐Ÿ“ฑ', '๐Ÿ’ป', 'โŒš', '๐Ÿ“š', '๐Ÿ“–', '๐Ÿ“', 'โœ๏ธ', '๐Ÿ–Š๏ธ'],
  'Nature': ['๐ŸŒž', '๐ŸŒ™', 'โญ', '๐ŸŒŸ', 'โ˜€๏ธ', 'โ›…', '๐ŸŒค๏ธ', 'โ˜๏ธ', '๐ŸŒฆ๏ธ', '๐ŸŒง๏ธ', 'โ›ˆ๏ธ', '๐ŸŒฉ๏ธ', 'โ„๏ธ', '๐Ÿ”ฅ', '๐Ÿ’ง', '๐ŸŒŠ', '๐ŸŒ', '๐ŸŒŽ', '๐ŸŒ', '๐ŸŒธ', '๐ŸŒบ', '๐ŸŒป', '๐ŸŒท', '๐ŸŒน', '๐ŸŒด', '๐ŸŒฒ', '๐ŸŒณ']
};

const EmojiPicker: React.FC<EmojiPickerProps> = ({ 
  onEmojiSelect, 
  isOpen, 
  onClose, 
  position = 'bottom' 
}) => {
  const [selectedCategory, setSelectedCategory] = useState('Smileys & People');
  const [searchTerm, setSearchTerm] = useState('');
  const pickerRef = useRef<HTMLDivElement>(null);

  // Close picker when clicking outside
  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (pickerRef.current && !pickerRef.current.contains(event.target as Node)) {
        onClose();
      }
    };

    const handleEscape = (event: KeyboardEvent) => {
      if (event.key === 'Escape') {
        onClose();
      }
    };

    if (isOpen) {
      document.addEventListener('mousedown', handleClickOutside);
      document.addEventListener('keydown', handleEscape);
    }

    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
      document.removeEventListener('keydown', handleEscape);
    };
  }, [isOpen, onClose]);

  // Filter emojis based on search
  const filteredEmojis = searchTerm
    ? Object.values(EMOJI_CATEGORIES).flat().filter(emoji => 
        emoji.includes(searchTerm) || 
        getEmojiName(emoji).toLowerCase().includes(searchTerm.toLowerCase())
      )
    : EMOJI_CATEGORIES[selectedCategory as keyof typeof EMOJI_CATEGORIES];

  const handleEmojiClick = (emoji: string) => {
    // Prevent event bubbling to avoid form submission
    onEmojiSelect(emoji);
    // Don't auto-close for better UX
  };

  return (
    <AnimatePresence>
      {isOpen && (
        <motion.div
          ref={pickerRef}
          initial={{ opacity: 0, scale: 0.9, y: position === 'top' ? 10 : -10 }}
          animate={{ opacity: 1, scale: 1, y: 0 }}
          exit={{ opacity: 0, scale: 0.9, y: position === 'top' ? 10 : -10 }}
          transition={{ duration: 0.15 }}
          className={
            position === 'top' ? 'bottom-full mb-2' : 'top-full mt-2'
          }
        >
          {/* Header with search bar and close button */}
          <div className="p-3 border-b border-gray-200 dark:border-gray-700">
            <div className="flex items-center gap-2">
              <input
                type="text"
                placeholder="Search emojis..."
                value={searchTerm}
                onChange={(e) => setSearchTerm(e.target.value)}
                className="flex-1 px-3 py-2 bg-gray-100 dark:bg-gray-700 border-0 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
                autoFocus
              />
              <button
                type="button"
                onClick={onClose}
                className="p-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors"
                title="Close"
              >
                <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
                </svg>
              </button>
            </div>
          </div>

          {/* Category tabs */}
          {!searchTerm && (
            <div className="flex overflow-x-auto border-b border-gray-200 dark:border-gray-700">
              {Object.keys(EMOJI_CATEGORIES).map((category) => (
                <button
                  key={category}
                  onClick={() => setSelectedCategory(category)}
                  className={
                    selectedCategory === category
                      ? 'border-blue-500 text-blue-600 dark:text-blue-400'
                      : 'border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300'
                  }
                >
                  {category.split(' ')[0]}
                </button>
              ))}
            </div>
          )}

          {/* Emoji grid */}
          <div className="p-2 max-h-64 overflow-y-auto">
            <div className="grid grid-cols-8 gap-1">
              {filteredEmojis.map((emoji, index) => (
                <button
                  key={`${emoji}-${index}
                  type="button"
                  onClick={(e) => {
                    e.preventDefault();
                    e.stopPropagation();
                    handleEmojiClick(emoji);
                  }}
                  className="p-2 text-xl hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors"
                  title={getEmojiName(emoji)}
                >
                  {emoji}
                </button>
              ))}
            </div>
            {filteredEmojis.length === 0 && (
              <div className="text-center py-8 text-gray-500 dark:text-gray-400">
                <p className="text-sm">No emojis found</p>
              </div>
            )}
          </div>

          {/* Recent emojis section */}
          <div className="border-t border-gray-200 dark:border-gray-700 p-2">
            <div className="flex items-center gap-1">
              <span className="text-xs text-gray-500 dark:text-gray-400 font-medium">Recent:</span>
              {getRecentEmojis().map((emoji, index) => (
                <button
                  key={`recent-${emoji}-${index}
                  type="button"
                  onClick={(e) => {
                    e.preventDefault();
                    e.stopPropagation();
                    handleEmojiClick(emoji);
                  }}
                  className="p-1 text-lg hover:bg-gray-100 dark:hover:bg-gray-700 rounded"
                  title={getEmojiName(emoji)}
                >
                  {emoji}
                </button>
              ))}
            </div>
          </div>
        </motion.div>
      )}
    </AnimatePresence>
  );
};

// Helper function to get emoji name (simplified)
function getEmojiName(emoji: string): string {
  const emojiNames: { [key: string]: string } = {
    '๐Ÿ˜€': 'grinning face',
    '๐Ÿ˜ƒ': 'grinning face with big eyes',
    '๐Ÿ˜„': 'grinning face with smiling eyes',
    '๐Ÿ˜': 'beaming face with smiling eyes',
    '๐Ÿ˜†': 'grinning squinting face',
    '๐Ÿ˜…': 'grinning face with sweat',
    '๐Ÿ˜‚': 'face with tears of joy',
    '๐Ÿคฃ': 'rolling on the floor laughing',
    '๐Ÿ˜Š': 'smiling face with smiling eyes',
    '๐Ÿ˜': 'smiling face with heart-eyes',
    '๐Ÿฅฐ': 'smiling face with hearts',
    '๐Ÿ˜˜': 'face blowing a kiss',
    '๐Ÿ‘': 'thumbs up',
    '๐Ÿ‘Ž': 'thumbs down',
    'โค๏ธ': 'red heart',
    '๐ŸŽ‰': 'party popper',
    '๐Ÿ”ฅ': 'fire',
    '๐Ÿ’ฏ': 'hundred points symbol',
    // Add more as needed
  };
  return emojiNames[emoji] || emoji;
}

// Helper function to get recent emojis from localStorage
function getRecentEmojis(): string[] {
  if (typeof window === 'undefined') return ['๐Ÿ‘', 'โค๏ธ', '๐Ÿ˜‚', '๐ŸŽ‰'];
  
  try {
    const recent = localStorage.getItem('recentEmojis');
    return recent ? JSON.parse(recent) : ['๐Ÿ‘', 'โค๏ธ', '๐Ÿ˜‚', '๐ŸŽ‰'];
  } catch {
    return ['๐Ÿ‘', 'โค๏ธ', '๐Ÿ˜‚', '๐ŸŽ‰'];
  }
}

// Helper function to save recent emoji
export function saveRecentEmoji(emoji: string) {
  if (typeof window === 'undefined') return;
  
  try {
    const recent = getRecentEmojis();
    const filtered = recent.filter(e => e !== emoji);
    const updated = [emoji, ...filtered].slice(0, 8);
    localStorage.setItem('recentEmojis', JSON.stringify(updated));
  } catch {
    // Ignore localStorage errors
  }
}

export default EmojiPicker; 

CasperSecurity Mini