EmojiPicker: Enter adds emoji in keyboard mode, otherwise dismisses

This commit is contained in:
Scott Nonnenberg 2024-03-19 06:23:31 -07:00 committed by GitHub
parent 28eaf1689f
commit 9533796c81
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 72 additions and 15 deletions

View file

@ -33,16 +33,28 @@ export type EmojiPickDataType = {
export type OwnProps = {
readonly i18n: LocalizerType;
readonly onPickEmoji: (o: EmojiPickDataType) => unknown;
readonly skinTone?: number;
readonly onSetSkinTone?: (tone: number) => unknown;
readonly recentEmojis?: ReadonlyArray<string>;
readonly skinTone?: number;
readonly onClickSettings?: () => unknown;
readonly onClose?: () => unknown;
readonly onPickEmoji: (o: EmojiPickDataType) => unknown;
readonly onSetSkinTone?: (tone: number) => unknown;
readonly wasInvokedFromKeyboard: boolean;
};
export type Props = OwnProps & Pick<React.HTMLProps<HTMLDivElement>, 'style'>;
function isEventFromMouse(
event:
| React.MouseEvent<HTMLButtonElement>
| React.KeyboardEvent<HTMLButtonElement>
): boolean {
return (
('clientX' in event && event.clientX !== 0) ||
('clientY' in event && event.clientY !== 0)
);
}
function focusOnRender(el: HTMLElement | null) {
if (el) {
el.focus();
@ -77,11 +89,16 @@ export const EmojiPicker = React.memo(
style,
onClickSettings,
onClose,
wasInvokedFromKeyboard,
}: Props,
ref
) => {
const isRTL = i18n.getLocaleDirection() === 'rtl';
const [isUsingKeyboard, setIsUsingKeyboard] = React.useState(
wasInvokedFromKeyboard
);
const [firstRecent] = React.useState(recentEmojis);
const [selectedCategory, setSelectedCategory] = React.useState<Category>(
categories[0]
@ -97,6 +114,9 @@ export const EmojiPicker = React.memo(
| React.MouseEvent<HTMLButtonElement>
| React.KeyboardEvent<HTMLButtonElement>
) => {
if (isEventFromMouse(e)) {
setIsUsingKeyboard(false);
}
e.stopPropagation();
e.preventDefault();
@ -129,6 +149,9 @@ export const EmojiPicker = React.memo(
| React.MouseEvent<HTMLButtonElement>
| React.KeyboardEvent<HTMLButtonElement>
) => {
if (isEventFromMouse(e)) {
setIsUsingKeyboard(false);
}
e.preventDefault();
e.stopPropagation();
@ -151,24 +174,42 @@ export const EmojiPicker = React.memo(
const { shortName } = e.currentTarget.dataset;
if ('key' in e) {
if (e.key === 'Enter') {
if (shortName) {
if (shortName && isUsingKeyboard) {
onPickEmoji({ skinTone: selectedTone, shortName });
e.stopPropagation();
e.preventDefault();
} else if (onClose) {
onClose();
e.stopPropagation();
e.preventDefault();
}
}
} else if (shortName) {
if (isEventFromMouse(e)) {
setIsUsingKeyboard(false);
}
e.stopPropagation();
e.preventDefault();
onPickEmoji({ skinTone: selectedTone, shortName });
}
},
[onPickEmoji, selectedTone]
[
onClose,
onPickEmoji,
isUsingKeyboard,
selectedTone,
setIsUsingKeyboard,
]
);
// Handle key presses, particularly Escape
React.useEffect(() => {
const handler = (event: KeyboardEvent) => {
if (event.key === 'Tab') {
// We do NOT prevent default here to allow Tab to be used normally
setIsUsingKeyboard(true);
return;
}
if (event.key === 'Escape') {
if (searchMode) {
event.preventDefault();
@ -190,7 +231,6 @@ export const EmojiPicker = React.memo(
'ArrowRight',
'Enter',
'Shift',
'Tab',
' ', // Space
].includes(event.key)
) {
@ -215,7 +255,7 @@ export const EmojiPicker = React.memo(
return () => {
document.removeEventListener('keydown', handler);
};
}, [onClose, searchMode, setSearchMode]);
}, [onClose, setIsUsingKeyboard, searchMode, setSearchMode]);
const [, ...renderableCategories] = categories;