From 6a4be6b93956c3b478af13bd37daa98bdef1c17e Mon Sep 17 00:00:00 2001 From: SlavaVlad Date: Mon, 15 Jun 2026 02:58:18 +0300 Subject: [PATCH] feat: move emoji picker from ResourceBrowser into item editor form --- components/ItemEditor.tsx | 117 +++++++++++++++++---------------- components/ResourceBrowser.tsx | 62 +---------------- 2 files changed, 61 insertions(+), 118 deletions(-) diff --git a/components/ItemEditor.tsx b/components/ItemEditor.tsx index 15ef662..c39033c 100644 --- a/components/ItemEditor.tsx +++ b/components/ItemEditor.tsx @@ -5,7 +5,7 @@ import { Card, CardContent } from "./ui/card"; import { Button } from "./ui/button"; import { Input } from "./ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"; -import { SOUND_CATEGORIES, EMOJIS } from "@/lib/bingo-data"; +import { SOUND_CATEGORIES } from "@/lib/bingo-data"; import { Badge } from "./ui/badge"; import { ResourceBrowser, DragData } from "./ResourceBrowser"; import { playSoundOnce } from "@/lib/sounds"; @@ -27,43 +27,52 @@ type Campaign = { gridSize: number; }; -function EmojiPicker({ value, onChange }: { value: string; onChange: (v: string) => void }) { - const [open, setOpen] = useState(false); - const ref = useRef(null); +const EMOJI_LIST = [ + "๐Ÿ˜€", "๐Ÿ˜‚", "๐Ÿคฃ", "๐Ÿ˜", "๐Ÿฅฐ", "๐Ÿ˜˜", "๐Ÿ˜Š", "๐Ÿ˜Ž", "๐Ÿคฉ", "๐Ÿฅณ", "๐Ÿคฏ", "๐Ÿ˜ฑ", + "๐Ÿ˜ˆ", "๐Ÿคก", "๐Ÿ’€", "๐Ÿ‘ป", "๐Ÿ‘ฝ", "๐Ÿค–", "๐ŸŽƒ", "๐Ÿ˜บ", "๐Ÿ™ˆ", "๐Ÿ™‰", "๐Ÿ™Š", "๐Ÿ’ฉ", + "๐Ÿ”ฅ", "๐ŸŒŠ", "๐Ÿ’ฅ", "๐Ÿ’ซ", "โญ", "๐ŸŒŸ", "โœจ", "โšก", "โ˜„๏ธ", "๐Ÿ’ง", "๐ŸงŠ", "๐ŸŒ‹", + "๐ŸŽ‰", "๐ŸŽŠ", "๐ŸŽˆ", "๐ŸŽ", "๐Ÿ†", "๐Ÿฅ‡", "๐Ÿ’Ž", "๐Ÿ”ฎ", "๐Ÿช„", "๐Ÿงจ", "๐Ÿ”ซ", "โš”๏ธ", + "๐Ÿ›ก๏ธ", "๐Ÿš€", "๐Ÿ›ธ", "๐Ÿš", "โš“", "๐ŸŒ€", "๐ŸŒˆ", "๐ŸŒช๏ธ", "โ˜ข๏ธ", "โ˜ฃ๏ธ", "โš•๏ธ", "๐Ÿงฌ", + "๐Ÿ’‰", "๐Ÿ’Š", "๐Ÿฉธ", "๐Ÿงช", "๐Ÿ”ฌ", "๐Ÿ”ญ", "๐Ÿ“ก", "๐ŸŽฏ", "๐ŸŽฒ", "โ™Ÿ๏ธ", "๐Ÿงฉ", "๐ŸŽญ", + "๐ŸŽต", "๐ŸŽถ", "๐ŸŽบ", "๐Ÿ“ฏ", "๐Ÿ””", "๐ŸŽค", "๐ŸŽง", "๐Ÿ“ข", "๐Ÿ“ฃ", "๐Ÿ”Š", "๐Ÿ”‡", "๐Ÿ’ค", + "๐Ÿ’ฃ", "๐Ÿ”ช", "๐Ÿชฆ", "โšฐ๏ธ", "๐Ÿชค", "๐Ÿงฒ", "๐Ÿ—๏ธ", "๐Ÿ”‘", "๐Ÿ”“", "๐Ÿ”’", "๐Ÿ› ๏ธ", "โ›“๏ธ", + "๐Ÿงน", "๐Ÿช ", "๐Ÿ”ง", "โš™๏ธ", "๐Ÿ“ฆ", "๐Ÿ“€", "๐Ÿ’ฟ", "๐Ÿ“น", "๐Ÿ“ธ", "๐Ÿ“ท", "๐Ÿ–ผ๏ธ", "๐ŸŽจ", + "๐Ÿบ", "๐Ÿป", "๐Ÿท", "๐Ÿฅƒ", "๐Ÿธ", "๐Ÿน", "๐Ÿง‰", "๐Ÿ•", "๐Ÿ”", "๐ŸŒญ", "๐Ÿฟ", "๐Ÿง€", + "๐Ÿฅœ", "๐ŸŒถ๏ธ", "๐Ÿ„", "๐Ÿฅš", "๐Ÿง…", "๐Ÿฅ•", "๐Ÿฅฆ", "๐Ÿซ", "๐Ÿง ", "๐Ÿ‘€", "๐Ÿ‘๏ธ", "๐Ÿซ€", + "๐Ÿ’‹", "๐Ÿซ‚", "๐Ÿค", "๐Ÿ‘", "๐Ÿ‘Ž", "๐Ÿ‘Š", "โœŠ", "๐Ÿค›", "๐Ÿคœ", "๐Ÿ‘†", "๐Ÿ‘‡", "๐Ÿ–•", + "๐Ÿ’ช", "๐Ÿฆต", "๐Ÿฆถ", "๐Ÿ‘‚", "๐Ÿ‘ƒ", "๐Ÿง‘โ€๐Ÿš€", "๐Ÿง‘โ€๐Ÿ”ง", "๐Ÿง‘โ€โš•๏ธ", "๐Ÿง‘โ€โœˆ๏ธ", "๐Ÿง‘โ€๐Ÿซ", "๐Ÿง‘โ€๐ŸŽค", "๐Ÿง‘โ€๐Ÿณ", + "๐Ÿถ", "๐Ÿฑ", "๐Ÿญ", "๐Ÿน", "๐Ÿฐ", "๐ŸฆŠ", "๐Ÿป", "๐Ÿผ", "๐Ÿจ", "๐Ÿฏ", "๐Ÿฆ", "๐Ÿฎ", + "๐Ÿฆˆ", "๐Ÿ™", "๐Ÿฆ‘", "๐Ÿ‹", "๐Ÿฌ", "๐Ÿฆญ", "๐ŸŠ", "๐ŸฆŽ", "๐Ÿ", "๐Ÿข", "๐Ÿฆ–", "๐Ÿฆ•", + "๐ŸฆŸ", "๐Ÿฆ—", "๐Ÿ›", "๐Ÿชฑ", "๐Ÿฆ‹", "๐ŸŒ", "๐Ÿž", "๐Ÿœ", "๐Ÿฆ‚", "๐Ÿ•ท๏ธ", "๐Ÿฆ€", "๐Ÿชธ", +]; - useEffect(() => { - function handleClick(e: MouseEvent) { - if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false); - } - document.addEventListener("mousedown", handleClick); - return () => document.removeEventListener("mousedown", handleClick); - }, []); +function EmojiPickerGrid({ value, onChange }: { value: string; onChange: (v: string) => void }) { + const [search, setSearch] = useState(""); + const filtered = search ? EMOJI_LIST.filter(e => e.includes(search)) : EMOJI_LIST; return ( -
- - {open && ( -
-
- {EMOJIS.map(e => ( - - ))} -
-
- )} +
+ setSearch(e.target.value)} + className="w-full bg-slate-800/60 border border-slate-700/30 rounded px-2 py-1 text-[10px] font-mono text-slate-200 placeholder:text-slate-600 outline-none focus:border-cyan-500/50" + /> +
+ {filtered.map(e => ( + + ))} +
); } @@ -95,15 +104,7 @@ export function ItemEditor({ campaign }: { campaign: Campaign }) { useEffect(() => { fetchItems(); }, [campaign.id]); - // Listen for emoji-select from ResourceBrowser EmojisTab - useEffect(() => { - const handler = (e: Event) => { - const detail = (e as CustomEvent).detail; - if (detail?.emoji) setEditEmoji(detail.emoji); - }; - window.addEventListener("emoji-select", handler); - return () => window.removeEventListener("emoji-select", handler); - }, []); + const updateItem = async (item: Item) => { await fetch(`/api/campaigns/${campaign.id}/items`, { @@ -259,20 +260,22 @@ export function ItemEditor({ campaign }: { campaign: Campaign }) {
+
+ + setEditText(e.target.value)} + className="font-mono text-sm" + /> +
+ +
+ + +
+
-
- - setEditText(e.target.value)} - className="font-mono text-sm" - /> -
-
- - -
setSearch(e.target.value)} - className="w-full bg-slate-800/60 border border-slate-700/30 rounded px-2 py-1.5 text-xs font-mono text-slate-200 placeholder:text-slate-600 outline-none focus:border-cyan-500/50" - /> -
- {filtered.map(e => ( - - ))} -
-
- ); -} // โ”€โ”€โ”€ Sounds Tab โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ @@ -376,7 +325,7 @@ function FreesoundRow({ name, duration, onPreview, onImport }: { // โ”€โ”€โ”€ Main ResourceBrowser โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -type Tab = "sounds" | "images" | "emojis"; +type Tab = "sounds" | "images"; export function ResourceBrowser({ visible, onClose }: { visible: boolean; onClose: () => void }) { const [tab, setTab] = useState("sounds"); @@ -390,7 +339,6 @@ export function ResourceBrowser({ visible, onClose }: { visible: boolean; onClos {([ { key: "sounds" as const, label: "๐Ÿ”Š Sounds" }, { key: "images" as const, label: "๐Ÿ–ผ๏ธ Images" }, - { key: "emojis" as const, label: "๐Ÿ˜€ Emojis" }, ]).map(t => (
); } -function EmojisTab() { - const handleSelect = (emoji: string) => { - // Dispatch a custom event so ItemEditor can pick it up - window.dispatchEvent(new CustomEvent("emoji-select", { detail: { emoji } })); - }; - return
; -}