import classNames from 'clsx'; import React, { useRef } from 'react'; import { Emoji, HStack } from 'soapbox/components/ui'; interface IEmojiButton { /** Unicode emoji character. */ emoji: string, /** Event handler when the emoji is clicked. */ onClick: React.EventHandler, /** Keyboard event handler. */ onKeyDown?: React.EventHandler, /** Extra class name on the ); }; interface IEmojiSelector { /** List of Unicode emoji characters. */ emojis: Iterable, /** Event handler when an emoji is clicked. */ onReact: (emoji: string) => void, /** Event handler when selector is escaped. */ onUnfocus: React.KeyboardEventHandler, /** Whether the selector should be visible. */ visible?: boolean, /** Whether the selector should be focused. */ focused?: boolean, } /** Panel with a row of emoji buttons. */ const EmojiSelector: React.FC = ({ emojis, onReact, onUnfocus, visible = false, focused = false }): JSX.Element => { const emojiList = Array.from(emojis); const node = useRef(null); const handleReact = (emoji: string): React.EventHandler => { return (e) => { onReact(emoji); e.preventDefault(); e.stopPropagation(); }; }; const selectPreviousEmoji = (i: number): void => { if (!node.current) return; if (i !== 0) { const button: HTMLButtonElement | null = node.current.querySelector(`.emoji-react-selector__emoji:nth-child(${i})`); button?.focus(); } else { const button: HTMLButtonElement | null = node.current.querySelector('.emoji-react-selector__emoji:last-child'); button?.focus(); } }; const selectNextEmoji = (i: number) => { if (!node.current) return; if (i !== emojiList.length - 1) { const button: HTMLButtonElement | null = node.current.querySelector(`.emoji-react-selector__emoji:nth-child(${i + 2})`); button?.focus(); } else { const button: HTMLButtonElement | null = node.current.querySelector('.emoji-react-selector__emoji:first-child'); button?.focus(); } }; const handleKeyDown = (i: number): React.KeyboardEventHandler => e => { switch (e.key) { case 'Enter': handleReact(emojiList[i])(e as any); break; case 'Tab': e.preventDefault(); if (e.shiftKey) selectPreviousEmoji(i); else selectNextEmoji(i); break; case 'Left': case 'ArrowLeft': selectPreviousEmoji(i); break; case 'Right': case 'ArrowRight': selectNextEmoji(i); break; case 'Escape': onUnfocus(e); break; } }; return ( {emojiList.map((emoji, i) => ( ))} ); }; export default EmojiSelector;