added features and fixes
This commit is contained in:
@@ -1,10 +1,127 @@
|
||||
import { useState } from 'react';
|
||||
import { useState, useMemo } from 'react';
|
||||
|
||||
const EMOJI_LIST = [
|
||||
'👍', '❤️', '😂', '😮', '😢', '😡', '🎉', '🔥',
|
||||
'👀', '💯', '✨', '🙏', '💀', '🤡', '🤔', '😎',
|
||||
'🫡', '🫠', '🤯', '🥳', '😴', '🤮', '👻', '🎃',
|
||||
'🚀', '⭐', '🌈', '🍕', '🍺', '☕', '🎮', '🎵',
|
||||
interface KaomojiItem {
|
||||
emoji: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
interface Category {
|
||||
name: string;
|
||||
tabLabel: string;
|
||||
items: KaomojiItem[];
|
||||
}
|
||||
|
||||
const KAOMOJI_CATEGORIES: Category[] = [
|
||||
{
|
||||
name: 'Bear (Kuma)',
|
||||
tabLabel: 'Bear',
|
||||
items: [
|
||||
{ emoji: 'ʕ•ᴥ•ʔ', tags: ['bear', 'kuma', 'animal', 'happy', 'neutral', 'cute'] },
|
||||
{ emoji: 'ʕっ•ᴥ•ʔっ', tags: ['bear', 'kuma', 'hug', 'love', 'happy', 'cute'] },
|
||||
{ emoji: 'ʕ•ᴥ•ʔノ♡', tags: ['bear', 'kuma', 'love', 'wave', 'happy', 'cute'] },
|
||||
{ emoji: 'ʕ º ᴥ º ʔ', tags: ['bear', 'kuma', 'surprised', 'shocked', 'gasp'] },
|
||||
{ emoji: 'ʕ◕ᴥ◕ʔ', tags: ['bear', 'kuma', 'cute', 'happy', 'smile'] },
|
||||
{ emoji: 'ʕ ; ᴥ ; ʔ', tags: ['bear', 'kuma', 'sad', 'crying', 'tear'] },
|
||||
{ emoji: 'ʕಠᴥಠʔ', tags: ['bear', 'kuma', 'angry', 'mad', 'glare'] },
|
||||
{ emoji: 'ʕ*ノᴥノʔ', tags: ['bear', 'kuma', 'shy', 'blush', 'cute'] },
|
||||
{ emoji: 'ʕ⊙ᴥ⊙ʔ', tags: ['bear', 'kuma', 'shocked', 'surprised', 'gasp'] },
|
||||
{ emoji: 'ʕ/•ᴥ•ʔ/', tags: ['bear', 'kuma', 'happy', 'celebrate', 'dance'] },
|
||||
{ emoji: 'ʕ·ᴥ·ʔ', tags: ['bear', 'kuma', 'small', 'cute', 'neutral'] },
|
||||
{ emoji: '⊂( ̄(エ) ̄)⊃', tags: ['bear', 'kuma', 'big', 'hug', 'animal'] },
|
||||
{ emoji: '(● ̄(エ) ̄●)', tags: ['bear', 'kuma', 'chubby', 'cute', 'neutral'] },
|
||||
{ emoji: 'ʕ •ᴥ•ʔゝ☆', tags: ['bear', 'kuma', 'magic', 'star', 'happy'] },
|
||||
{ emoji: 'ʕ•ᴥ•ʔ/', tags: ['bear', 'kuma', 'wave', 'hello', 'happy'] },
|
||||
{ emoji: 'ʕっ•ᴥ•ʔ', tags: ['bear', 'kuma', 'reach', 'cute'] },
|
||||
{ emoji: 'ʕ ⊃・ ェ ・ ʔ⊃', tags: ['bear', 'kuma', 'hug', 'reach'] },
|
||||
{ emoji: 'ʕ ꈍ ᴥ ꈍ ʔ', tags: ['bear', 'kuma', 'shy', 'happy', 'smug'] },
|
||||
{ emoji: 'ʕ ·(ェ)· ʔ', tags: ['bear', 'kuma', 'classic', 'neutral'] },
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Happy / Laughing',
|
||||
tabLabel: 'Joy',
|
||||
items: [
|
||||
{ emoji: '(^_^)', tags: ['happy', 'smile', 'classic', 'joy'] },
|
||||
{ emoji: '(^o^)', tags: ['happy', 'laugh', 'excited', 'joy'] },
|
||||
{ emoji: '(*^ω^*)', tags: ['happy', 'cute', 'smile', 'blush'] },
|
||||
{ emoji: '(≧▽≦)', tags: ['happy', 'laugh', 'excited', 'joy', 'grin'] },
|
||||
{ emoji: '(✿◠‿◠)', tags: ['happy', 'flower', 'cute', 'smile', 'peace'] },
|
||||
{ emoji: '(•‿•)', tags: ['happy', 'smile', 'cute'] },
|
||||
{ emoji: '(o^^o)', tags: ['happy', 'cute', 'blush', 'smile'] },
|
||||
{ emoji: '( ‘‿’ )', tags: ['happy', 'smile', 'cute'] },
|
||||
{ emoji: '\(〇‿〇)/', tags: ['happy', 'celebrate', 'excited', 'dance'] },
|
||||
{ emoji: '(◕‿◕)', tags: ['happy', 'smile', 'cute'] },
|
||||
{ emoji: '(>ω<)', tags: ['happy', 'excited', 'cute', 'joy'] },
|
||||
{ emoji: '(★ω★)', tags: ['happy', 'excited', 'star', 'sparkle', 'eyes'] },
|
||||
{ emoji: '(*^.^*)', tags: ['happy', 'smile', 'cute', 'shy'] },
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Sad / Crying',
|
||||
tabLabel: 'Sad',
|
||||
items: [
|
||||
{ emoji: '(T_T)', tags: ['sad', 'crying', 'tear', 'classic'] },
|
||||
{ emoji: '(ಥ﹏ಥ)', tags: ['sad', 'crying', 'tear', 'cute', 'pity'] },
|
||||
{ emoji: '(;_;)', tags: ['sad', 'crying', 'tear'] },
|
||||
{ emoji: '(._.)', tags: ['sad', 'depressed', 'down', 'neutral'] },
|
||||
{ emoji: '(._. )', tags: ['sad', 'depressed', 'down', 'shy'] },
|
||||
{ emoji: '(╥﹏╥)', tags: ['sad', 'crying', 'tear', 'heavy'] },
|
||||
{ emoji: '((>_<))', tags: ['sad', 'worried', 'frustrated', 'nervous'] },
|
||||
{ emoji: '(#>_<)', tags: ['sad', 'frustrated', 'mad', 'angry'] },
|
||||
{ emoji: '(;﹏;)', tags: ['sad', 'crying', 'tear'] },
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Angry / Glaring',
|
||||
tabLabel: 'Mad',
|
||||
items: [
|
||||
{ emoji: '(ಠ_ಠ)', tags: ['angry', 'mad', 'glare', 'disapproval', 'look'] },
|
||||
{ emoji: '(╯°□°)╯︵ ┻━┻', tags: ['angry', 'mad', 'table', 'flip', 'rage'] },
|
||||
{ emoji: '┬─┬ノ( º _ ºノ)', tags: ['neutral', 'table', 'put', 'back', 'calm'] },
|
||||
{ emoji: '(#`д´)ノ', tags: ['angry', 'mad', 'yell', 'shake'] },
|
||||
{ emoji: '(¬_¬)', tags: ['suspicious', 'glare', 'annoyed', 'side-eye'] },
|
||||
{ emoji: '( ̄︿ ̄)', tags: ['annoyed', 'disappointed', 'unhappy'] },
|
||||
{ emoji: '(╬ಠ益ಠ)', tags: ['angry', 'mad', 'rage', 'furious'] },
|
||||
{ emoji: '(凸ಠ益ಠ)凸', tags: ['angry', 'mad', 'middle', 'finger', 'rage'] },
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Shy / Cute',
|
||||
tabLabel: 'Shy',
|
||||
items: [
|
||||
{ emoji: '(*n_n*)', tags: ['shy', 'blush', 'cute', 'smile'] },
|
||||
{ emoji: '(*ノ∀`*)', tags: ['shy', 'blush', 'embarrassed', 'cute'] },
|
||||
{ emoji: '(ノωヽ)', tags: ['shy', 'blush', 'embarrassed', 'hide', 'cute'] },
|
||||
{ emoji: '( ꈍ ꈍ )', tags: ['cute', 'shy', 'blush', 'happy'] },
|
||||
{ emoji: '(⁄ ⁄•⁄ω⁄•⁄ ⁄)', tags: ['cute', 'shy', 'blush', 'super', 'embarrassed'] },
|
||||
{ emoji: '(❤ω❤)', tags: ['love', 'cute', 'excited', 'heart'] },
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Cats & Animals',
|
||||
tabLabel: 'Pets',
|
||||
items: [
|
||||
{ emoji: '(=^·^=)', tags: ['cat', 'animal', 'cute', 'happy'] },
|
||||
{ emoji: '(=^·ェ·^=)', tags: ['cat', 'animal', 'cute'] },
|
||||
{ emoji: '(=^ ◡ ^=)', tags: ['cat', 'animal', 'happy', 'smile'] },
|
||||
{ emoji: '(=^·^=)ノ', tags: ['cat', 'animal', 'wave', 'hello'] },
|
||||
{ emoji: '(◕ᴥ◕)', tags: ['animal', 'cute', 'seal'] },
|
||||
{ emoji: '(=^ ᆺ ^=)', tags: ['cat', 'animal', 'cute'] },
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'Action & Funny',
|
||||
tabLabel: 'Misc',
|
||||
items: [
|
||||
{ emoji: '¯\\_(ツ)_/¯', tags: ['funny', 'shrug', 'meh', 'whatever'] },
|
||||
{ emoji: '( ͡° ͜ʖ ͡°)', tags: ['funny', 'lenny', 'smug', 'face'] },
|
||||
{ emoji: '(ง •̀_•́)ง', tags: ['action', 'fight', 'determined', 'fist'] },
|
||||
{ emoji: '(′-′)ノ(._.)', tags: ['action', 'pat', 'comfort', 'friend'] },
|
||||
{ emoji: '(/°▽°)/', tags: ['action', 'happy', 'throw', 'hands'] },
|
||||
{ emoji: '(°ロ°)', tags: ['surprised', 'shocked', 'gasp'] },
|
||||
{ emoji: 'ヽ(°〇°)ノ', tags: ['surprised', 'shocked', 'amaze'] },
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
interface EmojiPickerProps {
|
||||
@@ -14,41 +131,120 @@ interface EmojiPickerProps {
|
||||
|
||||
export function EmojiPicker({ onSelect, onClose }: EmojiPickerProps) {
|
||||
const [search, setSearch] = useState('');
|
||||
const [activeTab, setActiveTab] = useState('All');
|
||||
|
||||
const filtered = search
|
||||
? EMOJI_LIST.filter(e => e.includes(search))
|
||||
: EMOJI_LIST;
|
||||
// Search logic (checks emoji or tags matching)
|
||||
const searchResults = useMemo(() => {
|
||||
if (!search) return [];
|
||||
const query = search.toLowerCase().trim();
|
||||
const results: KaomojiItem[] = [];
|
||||
const seen = new Set<string>();
|
||||
|
||||
for (const cat of KAOMOJI_CATEGORIES) {
|
||||
for (const item of cat.items) {
|
||||
if (seen.has(item.emoji)) continue;
|
||||
const matchesEmoji = item.emoji.includes(query);
|
||||
const matchesTags = item.tags.some(tag => tag.includes(query));
|
||||
if (matchesEmoji || matchesTags) {
|
||||
results.push(item);
|
||||
seen.add(item.emoji);
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}, [search]);
|
||||
|
||||
// Tab filter logic
|
||||
const displayedItems = useMemo(() => {
|
||||
if (activeTab === 'All') {
|
||||
const all: KaomojiItem[] = [];
|
||||
const seen = new Set<string>();
|
||||
for (const cat of KAOMOJI_CATEGORIES) {
|
||||
for (const item of cat.items) {
|
||||
if (!seen.has(item.emoji)) {
|
||||
all.push(item);
|
||||
seen.add(item.emoji);
|
||||
}
|
||||
}
|
||||
}
|
||||
return all;
|
||||
}
|
||||
const cat = KAOMOJI_CATEGORIES.find(c => c.tabLabel === activeTab);
|
||||
return cat ? cat.items : [];
|
||||
}, [activeTab]);
|
||||
|
||||
return (
|
||||
<div className="absolute bottom-full left-0 mb-1 bg-gb-bg border border-gb-bg-t p-2 min-w-[200px] z-50">
|
||||
<div className="absolute bottom-full left-0 mb-1 bg-gb-bg border border-gb-bg-t p-2 w-[340px] max-h-[340px] overflow-y-auto z-50 shadow-lg font-mono">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-xs text-gb-fg-f font-mono">EMOJI</span>
|
||||
<button onClick={onClose} className="text-xs text-gb-red font-mono">[x]</button>
|
||||
<span className="text-xs text-gb-fg-f">KAOMOJI PICKER</span>
|
||||
<button onClick={onClose} className="text-xs text-gb-red hover:text-gb-orange font-mono">[x]</button>
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
placeholder="search..."
|
||||
className="w-full px-2 py-1 mb-2 text-xs font-mono bg-gb-bg-s text-gb-fg border-none outline-none"
|
||||
placeholder="search (happy, bear, sad, table...)"
|
||||
className="w-full px-2 py-1 mb-2 text-xs bg-gb-bg-s text-gb-fg border border-gb-bg-t outline-none focus:border-gb-orange"
|
||||
autoFocus
|
||||
/>
|
||||
<div className="grid grid-cols-8 gap-1">
|
||||
{filtered.map((emoji) => (
|
||||
|
||||
{/* Tabs list (only shown if not searching) */}
|
||||
{!search && (
|
||||
<div className="flex flex-wrap gap-1 mb-2 border-b border-gb-bg-t pb-1.5 text-[10px]">
|
||||
<button
|
||||
key={emoji}
|
||||
onClick={() => {
|
||||
onSelect(emoji);
|
||||
onClose();
|
||||
}}
|
||||
className="w-6 h-6 flex items-center justify-center text-sm hover:bg-gb-bg-s transition-colors"
|
||||
onClick={() => setActiveTab('All')}
|
||||
className={`px-1.5 py-0.5 border ${activeTab === 'All' ? 'border-gb-orange text-gb-orange bg-gb-bg-s' : 'border-transparent text-gb-fg-f hover:text-gb-fg'}`}
|
||||
>
|
||||
{emoji}
|
||||
[All]
|
||||
</button>
|
||||
))}
|
||||
{KAOMOJI_CATEGORIES.map(cat => (
|
||||
<button
|
||||
key={cat.tabLabel}
|
||||
onClick={() => setActiveTab(cat.tabLabel)}
|
||||
className={`px-1.5 py-0.5 border ${activeTab === cat.tabLabel ? 'border-gb-orange text-gb-orange bg-gb-bg-s' : 'border-transparent text-gb-fg-f hover:text-gb-fg'}`}
|
||||
>
|
||||
[{cat.tabLabel}]
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Emoticons grid */}
|
||||
<div className="flex flex-wrap gap-1.5 max-h-56 overflow-y-auto">
|
||||
{search ? (
|
||||
searchResults.map((item) => (
|
||||
<button
|
||||
key={item.emoji}
|
||||
onClick={() => {
|
||||
onSelect(item.emoji);
|
||||
onClose();
|
||||
}}
|
||||
className="px-2 py-1 text-xs bg-gb-bg-s text-gb-fg border border-gb-bg-t hover:border-gb-orange hover:text-gb-orange transition-colors"
|
||||
title={item.tags.join(', ')}
|
||||
>
|
||||
{item.emoji}
|
||||
</button>
|
||||
))
|
||||
) : (
|
||||
displayedItems.map((item) => (
|
||||
<button
|
||||
key={item.emoji}
|
||||
onClick={() => {
|
||||
onSelect(item.emoji);
|
||||
onClose();
|
||||
}}
|
||||
className="px-2 py-1 text-xs bg-gb-bg-s text-gb-fg border border-gb-bg-t hover:border-gb-orange hover:text-gb-orange transition-colors"
|
||||
title={item.tags.join(', ')}
|
||||
>
|
||||
{item.emoji}
|
||||
</button>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
{filtered.length === 0 && (
|
||||
<div className="text-xs text-gb-fg-f font-mono text-center py-2">[no results]</div>
|
||||
|
||||
{search && searchResults.length === 0 && (
|
||||
<div className="text-xs text-gb-fg-f text-center py-4">[no matches found]</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user