@chayns-components/emoji-input
Version:
Input field that supports HTML elements and emojis
247 lines (244 loc) • 8.77 kB
JavaScript
// @ts-nocheck
import { useCombinedRefs, useIsMeasuredClone } from '@chayns-components/core';
import emojiLib from 'emojilib';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import germanEmojiLib from '../../../constants/emoji-de-DE.json';
import { useEmojiHistory } from '../../../hooks/emojiHistory';
import Emoji from './emoji/Emoji';
import { StyledEmojiPickerEmojis, StyledEmojiPickerEmojisNoContentInfo } from './EmojiPickerEmojis.styles';
const EmojiPickerEmojis = ({
accessToken,
onSelect,
personId,
searchString,
selectedCategory
}) => {
const [shouldPreventScroll, setShouldPreventScroll] = useState(false);
const [shouldShowSkinTonePopup, setShouldShowSkinTonePopup] = useState(false);
const [focusedIndex, setFocusedIndex] = useState(0);
const [emojiCategories, setEmojiCategories] = useState([]);
const [emojiList, setEmojiList] = useState([]);
useEffect(() => {
void import('unicode-emoji-json/data-by-group.json').then(({
default: categories
}) => {
setEmojiCategories(categories);
});
void import('unicode-emoji-json/data-by-emoji.json').then(({
default: emojis
}) => {
setEmojiList(emojis);
});
}, []);
const [shouldPreventListener, ref] = useIsMeasuredClone();
const emojiRef = useRef(null);
const shouldPreventEmojiControlsRef = useRef(false);
const combinedRef = useCombinedRefs(emojiRef, ref);
const {
addOrUpdateEmojiInHistory,
historyEmojis
} = useEmojiHistory({
accessToken,
personId,
selectedCategory
});
const handlePopupVisibilityChange = useCallback(isVisible => {
setShouldShowSkinTonePopup(isVisible);
shouldPreventEmojiControlsRef.current = isVisible;
setShouldPreventScroll(isVisible);
}, []);
const handleSelect = useCallback(({
emoji,
name,
skin_tone_support,
index
}) => {
onSelect(emoji);
if (index) {
setFocusedIndex(index);
}
void addOrUpdateEmojiInHistory({
emoji,
name,
skin_tone_support
});
}, [addOrUpdateEmojiInHistory, onSelect]);
useEffect(() => {
if (selectedCategory) {
setFocusedIndex(0);
const container = emojiRef.current;
if (!container) {
return;
}
container.scrollTop = 0;
}
}, [selectedCategory]);
useEffect(() => {
const handleKeyDown = event => {
if (shouldPreventListener) {
return;
}
if (!shouldPreventEmojiControlsRef.current && (event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'ArrowLeft' || event.key === 'ArrowRight')) {
event.preventDefault();
const children = emojiRef.current?.children;
if (children && children.length > 0) {
const container = emojiRef.current;
let newIndex = focusedIndex !== null ? focusedIndex : 0;
if (event.ctrlKey) {
return;
}
if (event.key === 'ArrowUp') {
newIndex = (newIndex - 6) % children.length;
} else if (event.key === 'ArrowDown') {
newIndex = (newIndex + 6) % children.length;
} else if (event.key === 'ArrowLeft') {
newIndex = (newIndex - 1) % children.length;
} else if (event.key === 'ArrowRight') {
newIndex = (newIndex + 1) % children.length;
}
// remove focus from the old element
if (focusedIndex !== null) {
const prevElement = children[focusedIndex];
prevElement.tabIndex = -1;
}
if (newIndex < 0) {
newIndex = children.length - 1;
} else if (newIndex > children.length - 1) {
newIndex = 0;
}
setFocusedIndex(newIndex);
// Set focus to the element
const newElement = children[newIndex];
newElement.tabIndex = 0;
const containerRect = container.getBoundingClientRect();
const elementRect = newElement.getBoundingClientRect();
if (elementRect.bottom > containerRect.bottom) {
container.scrollTop += elementRect.bottom - containerRect.bottom;
} else if (elementRect.top < containerRect.top) {
container.scrollTop -= containerRect.top - elementRect.top;
}
}
} else if (event.key === 'Enter' && !shouldPreventEmojiControlsRef.current && focusedIndex !== null) {
if (event.ctrlKey) {
setShouldShowSkinTonePopup(true);
return;
}
const {
dataset
} = emojiRef.current?.children[focusedIndex];
const {
emoji,
name,
skinToneSupport
} = dataset;
if (!emoji || !name || !skinToneSupport) {
return;
}
handleSelect({
emoji,
name,
skin_tone_support: skinToneSupport === 'true'
});
}
};
document.addEventListener('keydown', handleKeyDown);
return () => {
document.removeEventListener('keydown', handleKeyDown);
};
}, [focusedIndex, handleSelect, shouldPreventListener]);
const handleRightClick = useCallback(index => {
setFocusedIndex(index);
}, []);
const emojis = useMemo(() => {
if (searchString.trim() !== '') {
const lowerSearchString = searchString.toLowerCase();
const searchResults = [];
Object.entries(emojiList).forEach(([emoji, {
name,
skin_tone_support
}], index) => {
const keywords = emojiLib[emoji];
const germanKeywords = germanEmojiLib[emoji];
if (name.includes(lowerSearchString) || keywords?.some(keyword => keyword.includes(lowerSearchString)) || germanKeywords?.some(keyword => keyword.includes(lowerSearchString))) {
searchResults.push(/*#__PURE__*/React.createElement(Emoji, {
shouldShowSkinTonePopup: index === focusedIndex && skin_tone_support && shouldShowSkinTonePopup,
isSelected: index === focusedIndex,
emoji: emoji,
index: index,
onRightClick: handleRightClick,
isSkinToneSupported: skin_tone_support,
key: name,
name: name,
onPopupVisibilityChange: handlePopupVisibilityChange,
onSelect: e => handleSelect({
emoji: e,
name,
skin_tone_support,
index
}),
emojiList: emojiList
}));
}
});
return searchResults;
}
if (selectedCategory === 'history') {
return historyEmojis.map(({
emoji,
name,
skin_tone_support
}, index) => /*#__PURE__*/React.createElement(Emoji, {
isSelected: index === focusedIndex,
shouldShowSkinTonePopup: index === focusedIndex && skin_tone_support && shouldShowSkinTonePopup,
emoji: emoji,
name: name,
key: name,
index: index,
onRightClick: handleRightClick,
onPopupVisibilityChange: handlePopupVisibilityChange,
onSelect: e => handleSelect({
emoji: e,
name,
skin_tone_support,
index
}),
isSkinToneSupported: false,
emojiList: emojiList
}));
}
return emojiCategories.find(({
slug
}) => slug === selectedCategory)?.emojis.map(({
emoji,
name,
skin_tone_support
}, index) => /*#__PURE__*/React.createElement(Emoji, {
isSelected: index === focusedIndex,
shouldShowSkinTonePopup: index === focusedIndex && skin_tone_support && shouldShowSkinTonePopup,
emoji: emoji,
name: name,
index: index,
onRightClick: handleRightClick,
isSkinToneSupported: skin_tone_support,
key: name,
onPopupVisibilityChange: handlePopupVisibilityChange,
onSelect: e => handleSelect({
emoji: e,
name,
skin_tone_support,
index
}),
emojiList: emojiList
}));
}, [searchString, selectedCategory, focusedIndex, shouldShowSkinTonePopup, handleRightClick, handlePopupVisibilityChange, handleSelect, historyEmojis]);
const shouldShowNoContentInfo = !emojis || emojis.length === 0;
return /*#__PURE__*/React.createElement(StyledEmojiPickerEmojis, {
className: "chayns-scrollbar",
$shouldPreventScroll: shouldPreventScroll,
$shouldShowNoContentInfo: shouldShowNoContentInfo,
ref: combinedRef
}, emojis, shouldShowNoContentInfo && /*#__PURE__*/React.createElement(StyledEmojiPickerEmojisNoContentInfo, null, "Hier werden die zuletzt verwendeten Emojis angezeigt, die \xFCber diese Auswahl gew\xE4hlt wurden."));
};
EmojiPickerEmojis.displayName = 'EmojiPickerEmojis';
export default EmojiPickerEmojis;
//# sourceMappingURL=EmojiPickerEmojis.js.map