import React, { useCallback, useEffect, useRef, useState } from 'react' import EmojiMartPicker from '../common/EmojiMartPicker' import extractNativeEmoji from '../common/extractNativeEmoji' import isEventWithinNode from '../common/isEventWithinNode' import loadEmojiMartData from '../common/loadEmojiMartData' /** * A button that opens a floating emoji picker. * When the user selects an emoji, `onEmojiSelect(emojiNative)` is called * with the native Unicode character. * * Props: * onEmojiSelect (string) → void Called with the emoji character * disabled boolean Disables the button * className string Additional classes for the trigger button */ export default function EmojiPickerButton({ onEmojiSelect, disabled = false, className = '' }) { const [open, setOpen] = useState(false) const [pickerData, setPickerData] = useState(null) const wrapRef = useRef(null) useEffect(() => { if (!open || pickerData) return let cancelled = false loadEmojiMartData().then((data) => { if (!cancelled) { setPickerData(data) } }) return () => { cancelled = true } }, [open, pickerData]) // Close on outside click useEffect(() => { if (!open) return function handleClick(e) { if (!isEventWithinNode(e, wrapRef.current)) { setOpen(false) } } document.addEventListener('mousedown', handleClick) return () => document.removeEventListener('mousedown', handleClick) }, [open]) // Close on Escape useEffect(() => { if (!open) return function handleKey(e) { if (e.key === 'Escape') setOpen(false) } document.addEventListener('keydown', handleKey) return () => document.removeEventListener('keydown', handleKey) }, [open]) const handleSelect = useCallback( (emoji) => { const nativeEmoji = extractNativeEmoji(emoji) if (nativeEmoji) { onEmojiSelect?.(nativeEmoji) } setOpen(false) }, [onEmojiSelect], ) return (