emoji-picker-react
Version:
Emoji Picker component for React Applications on the web
182 lines (138 loc) • 4.61 kB
text/typescript
import { elementCountInRow } from './elementPositionInRow';
import { focusElement } from './focusElement';
import { scrollEmojiAboveLabel } from './scrollTo';
import {
allVisibleEmojis,
closestCategory,
firstVisibleEmoji,
lastVisibleEmoji,
nextCategory,
nextVisibleEmoji,
NullableElement,
prevCategory,
prevVisibleEmoji,
closestCategoryContent
} from './selectors';
export function focusFirstVisibleEmoji(parent: NullableElement) {
const emoji = firstVisibleEmoji(parent);
focusElement(emoji);
scrollEmojiAboveLabel(emoji);
}
export function focusAndClickFirstVisibleEmoji(parent: NullableElement) {
const firstEmoji = firstVisibleEmoji(parent);
focusElement(firstEmoji);
firstEmoji?.click();
}
export function focusLastVisibleEmoji(parent: NullableElement) {
focusElement(lastVisibleEmoji(parent));
}
export function focusNextVisibleEmoji(element: NullableElement) {
if (!element) {
return;
}
const next = nextVisibleEmoji(element);
if (!next) {
return focusFirstVisibleEmoji(nextCategory(element));
}
focusElement(next);
scrollEmojiAboveLabel(next);
}
export function focusPrevVisibleEmoji(element: NullableElement) {
if (!element) {
return;
}
const prev = prevVisibleEmoji(element);
if (!prev) {
return focusLastVisibleEmoji(prevCategory(element));
}
focusElement(prev);
scrollEmojiAboveLabel(prev);
}
export function focusVisibleEmojiOneRowUp(
element: NullableElement,
exitUp: () => void
) {
if (!element) {
return;
}
const prev = visibleEmojiOneRowUp(element);
if (!prev) {
return exitUp();
}
focusElement(prev);
scrollEmojiAboveLabel(prev);
}
export function focusVisibleEmojiOneRowDown(element: NullableElement) {
if (!element) {
return;
}
const next = visibleEmojiOneRowDown(element);
return focusElement(next);
}
function visibleEmojiOneRowUp(element: HTMLElement) {
if (!element) {
return null;
}
const categoryContent = closestCategoryContent(element);
const category = closestCategory(categoryContent);
const countInRow = elementCountInRow(categoryContent, element);
const emojisInCurrentCategory = allVisibleEmojis(category);
const currentEmojiIndex = emojisInCurrentCategory.indexOf(element);
const indexInRow = currentEmojiIndex % countInRow;
if (currentEmojiIndex === -1) {
return null;
}
if (emojisInCurrentCategory[currentEmojiIndex - countInRow]) {
return emojisInCurrentCategory[currentEmojiIndex - countInRow];
}
const prevVisibleCategory = prevCategory(category);
if (!prevVisibleCategory) {
return null;
}
const allPrevEmojis = allVisibleEmojis(prevVisibleCategory);
// if there is an emoji in the same index of `indexInRow` in the previous category, return it
// for this we need to find the last emoji in the previous category that shares the same indexInRow
// this is by using the % operator to find the last emoji that matches
const lastIndexInRow = (allPrevEmojis.length % countInRow) - 1;
if (indexInRow > lastIndexInRow) {
return allPrevEmojis.at(-1);
}
// otherwise, return the last emoji that shares the same indexInRow
for (let i = allPrevEmojis.length - 1; i >= 0; i--) {
if (i % countInRow === indexInRow) {
return allPrevEmojis[i];
}
}
return allPrevEmojis.at(-1);
}
function visibleEmojiOneRowDown(element: HTMLElement) {
if (!element) {
return null;
}
const categoryContent = closestCategoryContent(element);
const category = closestCategory(categoryContent);
const countInRow = elementCountInRow(categoryContent, element);
const emojisInCurrentCategory = allVisibleEmojis(category);
const currentEmojiIndex = emojisInCurrentCategory.indexOf(element);
if (currentEmojiIndex === -1) {
return null;
}
// the remainder until the end of the row
const remainder = countInRow - (currentEmojiIndex % countInRow) - 1;
const firstInNextRow = currentEmojiIndex + remainder + 1;
if (emojisInCurrentCategory[firstInNextRow]) {
// if we have a next row, search in the next row for the last available emoji
for (let p = currentEmojiIndex + countInRow; p % countInRow >= 0; p--) {
if (emojisInCurrentCategory[p]) {
return emojisInCurrentCategory[p];
}
}
}
const indexInRow = currentEmojiIndex % countInRow;
const nextVisibleCategory = nextCategory(category);
const emojisInNextCategory = allVisibleEmojis(nextVisibleCategory);
if (emojisInNextCategory[indexInRow]) {
return emojisInNextCategory[indexInRow];
}
return emojisInNextCategory.at(0) ?? null;
}