import * as React from 'react'; import { useState } from 'react'; import { cx } from 'shipstyles'; import { stylesheet } from '../../Stylesheet/stylesheet'; import { categoryFromCategoryConfig, categoryIdFromCategoryConfig, customGroupFromCategoryConfig, } from '../../config/categoryConfig'; import { useCategoriesConfig, useCategoryIconsConfig, } from '../../config/useConfig'; import { useActiveCategoryScrollDetection } from '../../hooks/useActiveCategoryScrollDetection'; import useIsSearchMode from '../../hooks/useIsSearchMode'; import { useScrollCategoryIntoView } from '../../hooks/useScrollCategoryIntoView'; import { useShouldHideCustomEmojis } from '../../hooks/useShouldHideCustomEmojis'; import { isCustomCategory } from '../../typeRefinements/typeRefinements'; import { Categories } from '../../types/exposedTypes'; import { useCategoryNavigationRef } from '../context/ElementRefContext'; import { useVisibleCategoriesState } from '../context/PickerContext'; import { usePickerDataContext } from '../context/PickerDataContext'; import { CategoryButton } from './CategoryButton'; export function CategoryNavigation() { const [activeCategory, setActiveCategory] = useState(null); const [, setVisibleCategories] = useVisibleCategoriesState(); const scrollCategoryIntoView = useScrollCategoryIntoView(); const isSearchMode = useIsSearchMode(); const categoriesConfig = useCategoriesConfig(); const categoryIcons = useCategoryIconsConfig(); const CategoryNavigationRef = useCategoryNavigationRef(); const hideCustomCategory = useShouldHideCustomEmojis(); const { customGroups, emojiData } = usePickerDataContext(); // The observer re-subscribes when the merged categories reference // changes (sections added/removed); the reference is stable otherwise. useActiveCategoryScrollDetection({ setActiveCategory, setVisibleCategories, categories: categoriesConfig, }); const visibleCategories = categoriesConfig.filter(categoryConfig => { if (isCustomCategory(categoryConfig) && hideCustomCategory) { return false; } // Tabs navigating to an empty (hidden) section are dead weight. const group = customGroupFromCategoryConfig(categoryConfig); if (group) { return (customGroups[group]?.length ?? 0) > 0; } if (isCustomCategory(categoryConfig)) { return (emojiData.emojis?.[Categories.CUSTOM]?.length ?? 0) > 0; } return true; }); // A single tab navigates nowhere — hide the bar to reclaim its space. // https://github.com/ealush/emoji-picker-react/issues/396 if (visibleCategories.length <= 1) { return null; } return (
{visibleCategories.map(categoryConfig => { const category = categoryFromCategoryConfig(categoryConfig); const categoryId = categoryIdFromCategoryConfig(categoryConfig); const isActiveCategory = categoryId === activeCategory; const allowNavigation = !isSearchMode && !isActiveCategory; return ( { scrollCategoryIntoView(categoryId); setTimeout(() => { setActiveCategory(categoryId); }, 10); }} /> ); })}
); } const styles = stylesheet.create({ nav: { '.': 'epr-category-nav', display: 'flex', flexDirection: 'row', justifyContent: 'space-around', padding: 'var(--epr-header-padding)', }, '.epr-search-active': { nav: { opacity: '0.3', cursor: 'default', pointerEvents: 'none', }, }, '.epr-main:has(input:not(:placeholder-shown))': { nav: { opacity: '0.3', cursor: 'default', pointerEvents: 'none', }, }, });