diff --git a/frontend/src/components/EmojiIconPicker.jsx b/frontend/src/components/EmojiIconPicker.jsx index 618cf1b..e072d44 100644 --- a/frontend/src/components/EmojiIconPicker.jsx +++ b/frontend/src/components/EmojiIconPicker.jsx @@ -1,5 +1,6 @@ -import { useState, useId } from 'react' +import { useState, useId, useMemo, useEffect } from 'react' import { ChevronDown, ChevronUp } from 'lucide-react' +import { haystackForEmoji, matchesEmojiSearch } from './emojiIconPickerKeywords.js' /** * Kuratierte Emoji-Gruppen (viele Einzel-Glyphen für breite OS-Unterstützung). @@ -222,10 +223,29 @@ export default function EmojiIconPicker({ }) { const uid = useId() const inputId = idProp || `emoji-icon-${uid}` + const searchInputId = `${inputId}-picker-search` const [open, setOpen] = useState(defaultExpanded) + const [pickerSearch, setPickerSearch] = useState('') const groups = extraGroups.length > 0 ? [...EMOJI_ICON_GROUPS, ...extraGroups] : EMOJI_ICON_GROUPS + useEffect(() => { + if (!open) setPickerSearch('') + }, [open]) + + const filteredGroups = useMemo(() => { + const q = pickerSearch + if (!q.trim()) { + return groups + } + return groups + .map((g) => ({ + ...g, + emojis: g.emojis.filter((em) => matchesEmojiSearch(haystackForEmoji(em, g.label), q)) + })) + .filter((g) => g.emojis.length > 0) + }, [groups, pickerSearch]) + const handleInput = (e) => { onChange(e.target.value.slice(0, maxLength)) } @@ -308,11 +328,40 @@ export default function EmojiIconPicker({ background: 'var(--surface2)', borderRadius: 12, border: '1px solid var(--border)', - maxHeight: 'min(72vh, 420px)', + maxHeight: 'min(72vh, 460px)', overflowY: 'auto' }} > - {groups.map((group, gi) => ( + + setPickerSearch(e.target.value)} + placeholder="z. B. rollschuh, karate, apfel…" + disabled={disabled} + autoComplete="off" + spellCheck={false} + inputMode="search" + style={{ width: '100%', marginBottom: 12, boxSizing: 'border-box' }} + /> + {filteredGroups.length === 0 && pickerSearch.trim() && ( +

+ Keine Treffer für „{pickerSearch.trim()}“. Andere Begriffe probieren oder oben ein Emoji + einfügen (z. B. Win + .). +

+ )} + {filteredGroups.map((group, gi) => (
))} -

+

Du kannst auch direkt in das Feld tippen oder das Betriebssystem-Emoji-Menü nutzen - (z. B. Win + . unter Windows).{' '} - Inline Skating: Es gibt kein separates Inliner-Emoji; 🛼 (Rollschuh) wird dafür oft genutzt.{' '} + (z. B. Win + . unter Windows). Mehrere Wörter verfeinern die Suche: jedes muss in den + Stichwörtern vorkommen.{' '} + Inline Skating: Es gibt kein separates Inliner-Emoji; 🛼 (Rollschuh) wird dafür oft genutzt – Suchbegriffe: rollschuh, inline, inliner.{' '} Karate / Kampfsport mit Gi: 🥋 (gemeinsames Unicode-Symbol für u. a. Karate, Judo, Ju-Jitsu).

diff --git a/frontend/src/components/emojiIconPickerKeywords.js b/frontend/src/components/emojiIconPickerKeywords.js new file mode 100644 index 0000000..e707eb4 --- /dev/null +++ b/frontend/src/components/emojiIconPickerKeywords.js @@ -0,0 +1,529 @@ +/** + * Suchbegriffe für EmojiIconPicker (kleingeschrieben, DE + ggf. EN). + * Gruppenüberschriften werden zusätzlich immer indexiert. + * @type {Record} + */ +export const EMOJI_KEYWORDS = { + // —— Rollen / Skates (wichtig für „Inline“, „Rollschuh“) —— + '🛼': + 'rollschuh rollschuhe inline inliner skaten roller quad rollschlittschuh blade blades rollerblade skates', + + // —— Sport (typisch DE-Gruppe) —— + '⚽': 'fußball fussball soccer football', + '🤾': 'handball', + '🤾‍♂️': 'handball mann', + '🤾‍♀️': 'handball frau', + '🥋': 'karate judo ju jitsu jujitsu kampfsport gi anzug martial arts', + '🏐': 'volleyball beach', + '🏀': 'basketball', + '🎾': 'tennis', + '🏓': 'tischtennis pingpong', + '🏸': 'badminton federball', + '🏒': 'eishockey hockey', + '🏑': 'feldhockey hockey', + '🥍': 'lacrosse', + '🏈': 'american football football nfl', + '🏉': 'rugby', + '⚾': 'baseball', + '🥎': 'softball', + '⛹️': 'basketball person', + '⛹️‍♂️': 'basketball mann', + '⛹️‍♀️': 'basketball frau', + '🥅': 'tor goal netz', + '⛷️': 'ski skifahren skilaufen alpin', + '🎿': 'ski skier langlauf', + '🏂': 'snowboard', + '🛷': 'schlitten rodeln', + '⛸️': 'eislauf schlittschuh eisbahn kunstlauf', + '🥌': 'curling', + '🚴': 'rad fahrrad rennrad cycling', + '🚴‍♂️': 'rad fahrrad mann', + '🚴‍♀️': 'rad fahrrad frau', + '🚵': 'mountainbike mtb rad', + '🚵‍♂️': 'mountainbike mann', + '🚵‍♀️': 'mountainbike frau', + '🏃': 'laufen joggen jogging marathon laufsport', + '🏃‍♂️': 'laufen mann', + '🏃‍♀️': 'laufen frau', + '🏃‍➡️': 'laufen sprint', + '🚶': 'gehen spazieren walking', + '🥾': 'wandern hiking bergsport bergtouren', + '🏊': 'schwimmen schwimm sport', + '🏊‍♂️': 'schwimmen mann', + '🏊‍♀️': 'schwimmen frau', + '🤽': 'wasserball', + '🤽‍♂️': 'wasserball mann', + '🤽‍♀️': 'wasserball frau', + '🤿': 'tauchen schnorcheln diving', + '🏄': 'surfen wellen', + '🏄‍♂️': 'surfen mann', + '🏄‍♀️': 'surfen frau', + '🚣': 'rudern boot row', + '🚣‍♂️': 'rudern mann', + '🚣‍♀️': 'rudern frau', + '⛵': 'segeln segelboot yacht', + '🧗': 'klettern bouldern bergsteigen', + '🧗‍♂️': 'klettern mann', + '🧗‍♀️': 'klettern frau', + '🏋️': 'krafttraining gewichte fitnessstudio gym hanteln', + '🏋️‍♂️': 'krafttraining mann', + '🏋️‍♀️': 'krafttraining frau', + '🤸': 'turnen gymnastics', + '🤸‍♂️': 'turnen mann', + '🤸‍♀️': 'turnen frau', + '🥊': 'boxen boxing', + '🤼': 'ringen wrestling', + '🤺': 'fechten fencing', + '🏇': 'reiten pferd reitsport galopp derby', + '⛳': 'golf golfplatz loch', + '🏌️': 'golf', + '🏌️‍♂️': 'golf mann', + '🏌️‍♀️': 'golf frau', + '💃': 'tanzen tanz salsa tanzsport', + '🕺': 'tanzen mann disco', + '🧘': 'yoga pilates meditation entspannung', + '🧘‍♂️': 'yoga mann meditation', + '🧘‍♀️': 'yoga frau meditation', + '🛹': 'skateboard longboard street', + '🎯': 'dart darts schießen treffer zielscheibe', + '🎳': 'bowling kegeln', + '🏟️': 'stadion arena', + '🏆': 'pokal sieg trophy', + '🥇': 'gold medaille erste platz', + '🥈': 'silber medaille', + '🥉': 'bronze medaille', + + // —— Weitere Sport / Hobby —— + '🎱': 'billard pool snooker', + '🎣': 'angeln fishing', + '🤹': 'jonglieren zirkus', + '🪁': 'drachen steigen lassen drachen', + '🥏': 'frisbee ultimate', + '🛶': 'kanu kayak paddeln', + '🏹': 'bogenschießen pfeil bogen', + '🌊': 'welle meer wasser', + '🏖️': 'strand beach', + '🛣️': 'straße laufen strecke', + '🧭': 'kompass navigation orientierung', + '🏕️': 'camping zelten outdoor', + '⛺': 'zelt camping', + + // —— Yoga / Geist —— + '🪷': 'lotus blume meditation', + '☯️': 'yin yang', + '🕉️': 'hindu om meditation', + '🙏': 'beten danken namaste', + '🧎': 'knien', + '🧍': 'stehen', + '💭': 'gedanke idee', + '📿': 'gebetskette', + '🎼': 'noten musik', + '🎹': 'klavier keyboard piano', + '🥁': 'schlagzeug trommel', + '🎸': 'gitarre', + '🎺': 'trompete blasinstrument', + '🔔': 'glocke', + '✨': 'sterne glitzer', + '🌟': 'stern glanz', + '💫': 'schwindel meteor', + '🔮': 'kristallkugel', + + // —— Outdoor —— (Stichworte zu Bergen/Wetter) + '⛰️': 'berg berge alpen', + '🏔️': 'schneeberg gipfel', + '🗻': 'fuji vulkan berg', + '🌋': 'vulkan lava', + '🗺️': 'karte landkarte', + '🌲': 'wald tannenbaum', + '🌳': 'baum baumpark', + '🌴': 'palme urlaub', + '🍃': 'blatt grün frühling', + '🍂': 'herbst blatt', + '🌿': 'kraut pflanze', + '☘️': 'klee glück', + '🪨': 'stein fels', + '🏞️': 'nationalpark natur', + '🏜️': 'wüste', + '🌅': 'sonnenaufgang morgenrot', + '🌄': 'sonne berg horizont', + '🌈': 'regenbogen', + '⛅': 'wolke wetter', + '🌤️': 'sonne wolke', + '☀️': 'sonne sonnenschein', + '🌙': 'mond nacht', + '⭐': 'stern', + '🌠': 'sternschnuppe', + '❄️': 'schnee winter frost', + '☃️': 'schneemann', + '⛄': 'schneemann kalt', + + // —— Körper / Medizin —— + '💪': 'muskel kraft arm bizeps fit', + '🦾': 'prothese arm roboter', + '🦵': 'bein knie', + '🦶': 'fuß zehen', + '🖐️': 'hand fünf', + '✋': 'hand stop', + '👣': 'fußspuren laufen', + '❤️': 'herz liebe health', + '🩷': 'herz pink', + '💙': 'herz blau', + '💚': 'herz grün vegan gesund', + '🫀': 'herz organ anatomie', + '🫁': 'lunge atem', + '🧠': 'gehirn denken kognition', + '👁️': 'auge sehen', + '👂': 'ohr hören', + '🦷': 'zahn zahnarzt', + '🦴': 'knochen skelett', + '🧬': 'dna genetik', + '⚕️': 'medizin asclepius arzt', + '🩺': 'stethoskop arzt', + '🩹': 'pflaster verband', + '🩼': 'krücke verletzt', + '💊': 'tablette medikament', + '🌡️': 'fieberthermometer temperatur', + '🔬': 'mikroskop labor', + '🧪': 'reagenzglas chemie', + '🧫': 'petrischale', + '♿': 'rollstuhl barrierefrei', + '⚖️': 'waage gerechtigkeit gewicht', + '📏': 'lineal messen', + '📐': 'winkel geometrie', + + // —— Ernährung (Auswahl häufige Begriffe) —— + '🍎': 'apfel apple obst', + '🍐': 'birne pear', + '🍊': 'orange mandarine zitrus', + '🍋': 'zitrone lemon', + '🍌': 'banane banana', + '🍉': 'wassermelone melone', + '🍇': 'trauben weintrauben', + '🍓': 'erdbeere', + '🫐': 'heidelbeeren blaubeeren', + '🍒': 'kirschen', + '🍑': 'pfirsich', + '🥭': 'mango', + '🍍': 'ananas', + '🥝': 'kiwi', + '🍅': 'tomate', + '🥑': 'avocado', + '🥦': 'brokkoli', + '🥬': 'salat blattspinat', + '🥒': 'gurke', + '🌶️': 'chili scharf peperoni', + '🫑': 'paprika', + '🌽': 'mais', + '🥕': 'möhre karotte', + '🫒': 'olive', + '🧄': 'knoblauch', + '🧅': 'zwiebel', + '🥔': 'kartoffel', + '🍠': 'süßkartoffel', + '🥐': 'croissant frühstück', + '🍞': 'brot toast', + '🥖': 'baguette', + '🥨': 'breze brezel', + '🧀': 'käse', + '🥚': 'ei eier', + '🍳': 'braten pfanne frühstück', + '🧈': 'butter', + '🥞': 'pfannkuchen pancakes', + '🧇': 'waffel waffle', + '🥓': 'speck bacon', + '🥩': 'steak fleisch', + '🍗': 'hähnchenkeule geflügel', + '🍖': 'fleisch knochen', + '🌭': 'hotdog wurst', + '🍔': 'burger hamburger', + '🍟': 'pommes fritten', + '🍕': 'pizza', + '🫓': 'fladenbrot naan', + '🥙': 'döner wrap', + '🌮': 'taco', + '🌯': 'burrito', + '🥗': 'salat bowl', + '🍝': 'pasta spaghetti', + '🍜': 'suppe nudeln ramen', + '🍲': 'eintopf topf', + '🍛': 'curry reis', + '🍣': 'sushi', + '🍱': 'bento lunchbox', + '🥟': 'dumpling gyoza', + '🦪': 'austern', + '🍤': 'garnelen tempura', + '🍙': 'onigiri reisbällchen', + '🍚': 'reis bowl', + '🍘': 'reiscracker', + '🍥': 'narutomaki fischkuchen', + '🥠': 'glückskeks', + '🥮': 'mondkuchen', + '🍢': 'oden spieß', + '🍡': 'dango', + '🍧': 'wassereis shaved ice', + '🍨': 'eiscreme', + '🍦': 'softeis eis', + '🥧': 'kuchen tarte pie', + '🧁': 'cupcake muffin', + '🍰': 'torte kuchen slice', + '🎂': 'geburtstag torte', + '🍮': 'pudding dessert', + '🍭': 'lutscher lolly', + '🍬': 'bonbon süßigkeit', + '🍫': 'schokolade riegel', + '🍿': 'popcorn kino', + '🍩': 'donut', + '🍪': 'keks cookie', + '🌰': 'kastanie', + '🥜': 'erdnuss nüsse', + '🍯': 'honig', + '🥛': 'milch', + '🍼': 'baby flasche', + '🫖': 'teekanne tee', + '☕': 'kaffee espresso', + '🍵': 'matcha tee grüntee', + '🧃': 'saft packung', + '🥤': 'becher strohhalm', + '🧋': 'bubble tea boba', + '🍶': 'sake', + '🍺': 'bier krug', + '🍻': 'anstoßen bier', + '🥂': 'sekt champagner anstoßen', + '🍷': 'wein glas', + '🥃': 'whiskey tumbler', + '🍸': 'cocktail martini', + '🍹': 'cocktail drink', + '🧉': 'mate', + '🍾': 'sekt flasche party', + '💧': 'wasser trinken hydrieren', + '🧊': 'eiswürfel kalt', + + // —— Schlaf —— + '😴': 'schlafen müde schlaf', + '🛌': 'bett schlafen', + '🛏️': 'bett', + '💤': 'zzz schnarchen', + '🌛': 'mond halbmond', + '🌜': 'mond mondgesicht', + '💆': 'massage wellness', + '🧴': 'lotion cream', + '🛁': 'badewanne bad', + '🚿': 'dusche', + '🪥': 'zahnbürste hygiene', + '🩴': 'badelatschen flipflop', + '🕯️': 'kerze ruhe', + + // —— Smileys —— + '😊': 'lächeln glücklich', + '🙂': 'leichtes lächeln', + '😌': 'zufrieden erleichtert', + '😎': 'cool sonnenbrille', + '🤩': 'star augen begeistert', + '🥳': 'party hut feier', + '😤': 'stolz triumphiert', + '💯': 'hundert prozent perfekt', + '🙌': 'jubel hände', + '👏': 'applaus klatschen', + '🤝': 'handschlag deal', + '👍': 'daumen hoch gut', + '👎': 'daumen runter schlecht', + '✊': 'faust solidarität', + '🤛': 'faust links', + '🤜': 'faust rechts', + '😅': 'schweiß awkward', + '🤔': 'nachdenken frage', + '🧐': 'monokel prüfen', + '😇': 'heilig engel', + + // —— Tiere (Stichworte) —— + '🐶': 'hund dog', + '🐱': 'katze cat', + '🐭': 'maus mouse', + '🐹': 'hamster', + '🐰': 'hase kaninchen', + '🦊': 'fuchs', + '🐻': 'bär', + '🐼': 'panda', + '🐨': 'koala', + '🐯': 'tiger', + '🦁': 'löwe', + '🐮': 'kuh rind', + '🐷': 'schwein', + '🐸': 'frosch', + '🐵': 'affe', + '🐔': 'huhn', + '🐧': 'pinguin', + '🐦': 'vogel', + '🐤': 'küken', + '🦆': 'ente', + '🦅': 'adler', + '🦉': 'eule', + '🦇': 'fledermaus', + '🐺': 'wolf', + '🐗': 'wildschwein keiler', + '🐴': 'pferd pony', + '🦄': 'einhorn', + '🐝': 'biene', + '🐛': 'raupe', + '🦋': 'schmetterling', + '🐌': 'schnecke', + '🐞': 'marienkäfer', + '🐜': 'ameise', + '🦗': 'grille', + '🕷️': 'spinne', + '🦂': 'skorpion', + '🐢': 'schildkröte', + '🐍': 'schlange', + '🦎': 'eidechse', + '🦖': 't-rex dinosaurier', + '🦕': 'dino langhals', + '🐙': 'oktopus krake', + '🦑': 'tintenfisch', + '🦐': 'garnele', + '🦞': 'hummer', + '🐠': 'tropenfisch', + '🐟': 'fisch', + '🐬': 'delfin', + '🐳': 'wal bläst', + '🐋': 'wal', + '🦈': 'hai', + '🐊': 'krokodil alligator', + + // —— Symbole —— + '📊': 'diagramm balken statistik', + '📈': 'chart steigend trend', + '📉': 'chart fallend', + '🧮': 'abacus rechenbrett', + '📋': 'clipboard checkliste', + '📌': 'pin pinnwand', + '📍': 'ort marker standort', + '🔖': 'lesezeichen bookmark', + '🏷️': 'etikett label', + '✏️': 'bleistift schreiben', + '✒️': 'feder', + '🖊️': 'kugelschreiber', + '📎': 'büroklammer', + '🔗': 'link verknüpfung', + '⛓️': 'kette', + '🔒': 'schloss zu sicherheit', + '🔓': 'schloss offen', + '🔑': 'schlüssel', + '🗝️': 'altschlüssel', + '🔨': 'hammer', + '🛠️': 'werkzeug', + '⚙️': 'zahnrad einstellungen', + '🧰': 'werkzeugkasten', + '💡': 'idee glühbirne lampe', + '🔦': 'taschenlampe', + '🏮': 'laterne', + '🪔': 'öllampe diwali', + '📣': 'megaphone megafon', + '📢': 'lautsprecher', + '🔔': 'benachrichtigung glocke', + '🔕': 'lautlos stumm', + '⏱️': 'stoppuhr zeit', + '⏰': 'wecker uhr', + '🕐': 'uhr eins zeit', + '📅': 'kalender datum', + '🗓️': 'kalender spiral', + '✅': 'häkchen erledigt ok', + '☑️': 'box angehakt', + '✔️': 'check mark', + '❌': 'kreuz nein fehler', + '⭕': 'kreis groß', + '❗': 'ausrufezeichen wichtig', + '❓': 'fragezeichen', + '💬': 'sprechblase chat', + '🗨️': 'sprechblase links', + '📝': 'memo notizen', + '📖': 'buch lesen', + '🪄': 'zauberstab magie', + '🎪': 'zirkus zelt', + '🎭': 'theater masken', + '🎬': 'film klappe', + '🎨': 'palette malen kunst', + '🖼️': 'bild rahmen', + '🧩': 'puzzle teil', + '♟️': 'schach bauer', + '🎲': 'würfel zufall spiel', + '🧸': 'teddy bär spielzeug', + + // —— Fahrzeuge —— + '🚗': 'auto pkw', + '🚕': 'taxi', + '🚙': 'suv geländewagen', + '🚌': 'bus', + '🚎': 'oberleitungsbus trolley', + '🏎️': 'rennwagen formel', + '🚓': 'polizei streifenwagen', + '🚑': 'krankenwagen rettung', + '🚒': 'feuerwehr', + '🚐': 'kleinbus', + '🛻': 'pickup', + '🚚': 'lkw lieferwagen', + '🚛': 'sattelzug', + '🚜': 'traktor', + '🛵': 'roller motorroller', + '🏍️': 'motorrad', + '🛺': 'rikscha', + '🚲': 'fahrrad', + '🛴': 'tretroller scooter', + '🚁': 'helikopter hubschrauber', + '✈️': 'flugzeug', + '🛫': 'abflug', + '🛬': 'landung', + '🪂': 'fallschirm parachute', + '🚀': 'rakete startup', + '🛶': 'boot kanu', + '🚤': 'speedboot', + '🛥️': 'motorboot', + '🛳️': 'kreuzfahrtschiff', + '⛴️': 'fähre ferry', + '🚢': 'frachtschiff', + '⚓': 'anker hafen', + '🗼': 'turm fernsehturm' +} + +/** + * @param {string} groupLabel + */ +export function slugifyGroupLabel(groupLabel) { + return groupLabel + .toLowerCase() + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, '') + .replace(/[()]/g, ' ') + .replace(/[^\p{L}\p{N}]+/gu, ' ') + .replace(/\s+/g, ' ') + .trim() +} + +/** + * @param {string} emoji + * @param {string} groupLabel + */ +export function haystackForEmoji(emoji, groupLabel) { + const extra = EMOJI_KEYWORDS[emoji] || '' + const slug = slugifyGroupLabel(groupLabel) + return `${extra} ${slug}`.replace(/\s+/g, ' ').trim() +} + +/** + * Alle Such-Tokens müssen im Haystack vorkommen (UND). + * @param {string} haystack + * @param {string} query + */ +export function matchesEmojiSearch(haystack, query) { + const q = query.trim().toLowerCase() + if (!q) return true + const norm = haystack + .toLowerCase() + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, '') + const tokens = q + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, '') + .split(/\s+/) + .filter(Boolean) + return tokens.every((t) => norm.includes(t)) +}