emoji-picker-react 4.14.0 → 4.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@ import { GetEmojiUrl } from '../components/emoji/BaseEmojiProps';
2
2
  import { DataEmoji } from '../dataUtils/DataTypes';
3
3
  import { EmojiStyle } from '../types/exposedTypes';
4
4
  import { Dimensions } from '../virtualization/virtualizationHelpers';
5
- export declare function preloaEmojidIfNeeded(emoji: undefined | DataEmoji, emojiStyles: EmojiStyle, scrollTop: number, clientHeight: number, topOffset: number, style: {
5
+ export declare function preloadEmojiIfNeeded(emoji: undefined | DataEmoji, emojiStyles: EmojiStyle, scrollTop: number, clientHeight: number, topOffset: number, style: {
6
6
  top: number;
7
7
  } | undefined, dimensions: Dimensions, getEmojiUrl: GetEmojiUrl): void;
8
8
  export declare function preloadEmoji(getEmojiUrl: GetEmojiUrl, emoji: undefined | DataEmoji, emojiStyle: EmojiStyle): void;
@@ -1 +1,4 @@
1
- export declare function useActiveCategoryScrollDetection(setActiveCategory: (category: string) => void): void;
1
+ export declare function useActiveCategoryScrollDetection({ setActiveCategory, setVisibleCategories }: {
2
+ setActiveCategory: (category: string) => void;
3
+ setVisibleCategories: (categories: string[]) => void;
4
+ }): void;
@@ -1,10 +1,11 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { DataEmojis } from '../dataUtils/DataTypes';
3
- export declare function useEmojiVirtualization({ categoryEmojis, topOffset, onHeightReady, scrollTop }: {
3
+ export declare function useEmojiVirtualization({ categoryEmojis, topOffset, onHeightReady, scrollTop, isCategoryVisible }: {
4
4
  categoryEmojis: DataEmojis;
5
5
  topOffset: number;
6
6
  onHeightReady: (height: number) => void;
7
7
  scrollTop: number;
8
+ isCategoryVisible: boolean;
8
9
  }): {
9
10
  virtualizedCounter: number;
10
11
  emojis: ReactNode[];
@@ -0,0 +1 @@
1
+ export declare function useIsMounted(): boolean;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.14.0",
2
+ "version": "4.14.2",
3
3
  "license": "MIT",
4
4
  "main": "dist/index.js",
5
5
  "homepage": "https://ealush.com/emoji-picker-react",
@@ -13,6 +13,7 @@ import { DataEmojis } from '../../dataUtils/DataTypes';
13
13
  import { useGetEmojisByCategory } from '../../dataUtils/emojiSelectors';
14
14
  import { useEmojiVirtualization } from '../../hooks/useEmojiVirtualization';
15
15
  import { useEmojiListRef } from '../context/ElementRefContext';
16
+ import { useVisibleCategoriesState } from '../context/PickerContext';
16
17
 
17
18
  import { EmojiCategory } from './EmojiCategory';
18
19
 
@@ -46,7 +47,10 @@ export function EmojiList({ scrollTop }: { scrollTop: number }) {
46
47
  topOffset={currentOffset}
47
48
  onHeightReady={height => {
48
49
  if (categoryHeights[category] !== height) {
49
- setCategoryHeights(prev => ({ ...prev, [category]: height }));
50
+ setCategoryHeights(prev => ({
51
+ ...prev,
52
+ [category]: height
53
+ }));
50
54
  }
51
55
  }}
52
56
  scrollTop={scrollTop}
@@ -71,11 +75,14 @@ function RenderCategory({
71
75
  onHeightReady: (height: number) => void;
72
76
  scrollTop: number;
73
77
  }) {
78
+ const [visibleCategories] = useVisibleCategoriesState();
79
+
74
80
  const { virtualizedCounter, emojis, dimensions } = useEmojiVirtualization({
75
81
  categoryEmojis,
76
82
  topOffset,
77
83
  onHeightReady,
78
- scrollTop
84
+ scrollTop,
85
+ isCategoryVisible: visibleCategories.includes(categoryConfig.category)
79
86
  });
80
87
 
81
88
  return (
@@ -35,6 +35,7 @@ export function PickerContextProvider({ children }: Props) {
35
35
  const emojiVariationPickerState = useState<DataEmoji | null>(null);
36
36
  const reactionsModeState = useState(reactionsDefaultOpen);
37
37
  const [isPastInitialLoad, setIsPastInitialLoad] = useState(false);
38
+ const visibleCategoriesState = useState<string[]>([]);
38
39
 
39
40
  useMarkInitialLoad(setIsPastInitialLoad);
40
41
 
@@ -53,7 +54,8 @@ export function PickerContextProvider({ children }: Props) {
53
54
  searchTerm,
54
55
  skinToneFanOpenState,
55
56
  suggestedUpdateState,
56
- reactionsModeState
57
+ reactionsModeState,
58
+ visibleCategoriesState
57
59
  }}
58
60
  >
59
61
  {children}
@@ -77,6 +79,7 @@ const PickerContext = React.createContext<{
77
79
  disallowMouseRef: React.MutableRefObject<boolean>;
78
80
  disallowedEmojisRef: React.MutableRefObject<Record<string, boolean>>;
79
81
  reactionsModeState: ReactState<boolean>;
82
+ visibleCategoriesState: ReactState<Array<string>>;
80
83
  }>({
81
84
  activeCategoryState: [null, () => {}],
82
85
  activeSkinTone: [SkinTones.NEUTRAL, () => {}],
@@ -90,7 +93,8 @@ const PickerContext = React.createContext<{
90
93
  searchTerm: ['', () => new Promise<string>(() => undefined)],
91
94
  skinToneFanOpenState: [false, () => {}],
92
95
  suggestedUpdateState: [Date.now(), () => {}],
93
- reactionsModeState: [false, () => {}]
96
+ reactionsModeState: [false, () => {}],
97
+ visibleCategoriesState: [[], () => []]
94
98
  });
95
99
 
96
100
  type Props = Readonly<{
@@ -155,6 +159,11 @@ export function useDisallowedEmojisRef() {
155
159
  return disallowedEmojisRef;
156
160
  }
157
161
 
162
+ export function useVisibleCategoriesState() {
163
+ const { visibleCategoriesState } = React.useContext(PickerContext);
164
+ return visibleCategoriesState;
165
+ }
166
+
158
167
  export function useUpdateSuggested(): [number, () => void] {
159
168
  const { suggestedUpdateState } = React.useContext(PickerContext);
160
169
 
@@ -11,13 +11,15 @@ import { useScrollCategoryIntoView } from '../../hooks/useScrollCategoryIntoView
11
11
  import { useShouldHideCustomEmojis } from '../../hooks/useShouldHideCustomEmojis';
12
12
  import { isCustomCategory } from '../../typeRefinements/typeRefinements';
13
13
  import { useCategoryNavigationRef } from '../context/ElementRefContext';
14
+ import { useVisibleCategoriesState } from '../context/PickerContext';
14
15
 
15
16
  import { CategoryButton } from './CategoryButton';
16
17
 
17
18
  export function CategoryNavigation() {
18
19
  const [activeCategory, setActiveCategory] = useState<string | null>(null);
20
+ const [, setVisibleCategories] = useVisibleCategoriesState();
19
21
  const scrollCategoryIntoView = useScrollCategoryIntoView();
20
- useActiveCategoryScrollDetection(setActiveCategory);
22
+ useActiveCategoryScrollDetection({ setActiveCategory, setVisibleCategories });
21
23
  const isSearchMode = useIsSearchMode();
22
24
 
23
25
  const categoriesConfig = useCategoriesConfig();
@@ -5,7 +5,7 @@ import { EmojiStyle } from '../types/exposedTypes';
5
5
  import { Dimensions } from '../virtualization/virtualizationHelpers';
6
6
 
7
7
  // eslint-disable-next-line max-params
8
- export function preloaEmojidIfNeeded(
8
+ export function preloadEmojiIfNeeded(
9
9
  emoji: undefined | DataEmoji,
10
10
  emojiStyles: EmojiStyle,
11
11
  scrollTop: number,
@@ -4,13 +4,17 @@ import { categoryNameFromDom } from '../DomUtils/categoryNameFromDom';
4
4
  import { asSelectors, ClassNames } from '../DomUtils/classNames';
5
5
  import { useBodyRef } from '../components/context/ElementRefContext';
6
6
 
7
- export function useActiveCategoryScrollDetection(
8
- setActiveCategory: (category: string) => void
9
- ) {
7
+ export function useActiveCategoryScrollDetection({
8
+ setActiveCategory,
9
+ setVisibleCategories
10
+ }: {
11
+ setActiveCategory: (category: string) => void;
12
+ setVisibleCategories: (categories: string[]) => void;
13
+ }) {
10
14
  const BodyRef = useBodyRef();
11
15
 
12
16
  useEffect(() => {
13
- const visibleCategories = new Map();
17
+ const visibleCategories = new Map<string, number>();
14
18
  const bodyRef = BodyRef.current;
15
19
  const observer = new IntersectionObserver(
16
20
  entries => {
@@ -20,10 +24,18 @@ export function useActiveCategoryScrollDetection(
20
24
 
21
25
  for (const entry of entries) {
22
26
  const id = categoryNameFromDom(entry.target);
27
+
28
+ if (!id) {
29
+ continue;
30
+ }
31
+
23
32
  visibleCategories.set(id, entry.intersectionRatio);
24
33
  }
25
34
 
26
35
  const ratios = Array.from(visibleCategories);
36
+ setVisibleCategories(
37
+ ratios.filter(([_, ratio]) => ratio > 0).map(([id]) => id)
38
+ );
27
39
  const lastCategory = ratios[ratios.length - 1];
28
40
 
29
41
  if (lastCategory[1] == 1) {
@@ -44,5 +56,5 @@ export function useActiveCategoryScrollDetection(
44
56
  bodyRef?.querySelectorAll(asSelectors(ClassNames.category)).forEach(el => {
45
57
  observer.observe(el);
46
58
  });
47
- }, [BodyRef, setActiveCategory]);
59
+ }, [BodyRef, setActiveCategory, setVisibleCategories]);
48
60
  }
@@ -5,7 +5,10 @@ import {
5
5
  useEmojiListRef,
6
6
  usePickerMainRef
7
7
  } from '../components/context/ElementRefContext';
8
- import { useReactionsModeState } from '../components/context/PickerContext';
8
+ import {
9
+ useReactionsModeState,
10
+ useVisibleCategoriesState
11
+ } from '../components/context/PickerContext';
9
12
 
10
13
  const EMOJI_SIZE_DEFAULT = 32;
11
14
 
@@ -22,6 +25,7 @@ export function useCategoryHeight(
22
25
  const [isReactionsMode] = useReactionsModeState();
23
26
  const PickerMainRef = usePickerMainRef();
24
27
  const emojiSizeRef = React.useRef<number | undefined>();
28
+ const [visibleCategories] = useVisibleCategoriesState();
25
29
  const [dimensions, setDimensions] = React.useState<{
26
30
  categoryHeight: number;
27
31
  emojisPerRow: number;
@@ -54,7 +58,12 @@ export function useCategoryHeight(
54
58
  // Recompute on data-count changes and when reactions mode toggles
55
59
  React.useEffect(() => {
56
60
  computeAndSetDimensions();
57
- }, [emojiCount, isReactionsMode, computeAndSetDimensions]);
61
+ }, [
62
+ emojiCount,
63
+ isReactionsMode,
64
+ computeAndSetDimensions,
65
+ visibleCategories.length
66
+ ]);
58
67
 
59
68
  // Listen to transitionend on the picker root (where height transition occurs)
60
69
  React.useEffect(() => {
@@ -17,7 +17,7 @@ import {
17
17
  shouldVirtualize
18
18
  } from '../virtualization/virtualizationHelpers';
19
19
 
20
- import { preloaEmojidIfNeeded } from './preloadEmoji';
20
+ import { preloadEmojiIfNeeded } from './preloadEmoji';
21
21
  import { useCategoryHeight } from './useCategoryHeight';
22
22
  import { useIsEmojiDisallowed } from './useDisallowedEmojis';
23
23
  import { useIsEmojiHidden } from './useIsEmojiHidden';
@@ -26,12 +26,14 @@ export function useEmojiVirtualization({
26
26
  categoryEmojis,
27
27
  topOffset,
28
28
  onHeightReady,
29
- scrollTop
29
+ scrollTop,
30
+ isCategoryVisible
30
31
  }: {
31
32
  categoryEmojis: DataEmojis;
32
33
  topOffset: number;
33
34
  onHeightReady: (height: number) => void;
34
35
  scrollTop: number;
36
+ isCategoryVisible: boolean;
35
37
  }) {
36
38
  const isEmojiHidden = useIsEmojiHidden();
37
39
  const lazyLoadEmojis = useLazyLoadEmojisConfig();
@@ -74,7 +76,7 @@ export function useEmojiVirtualization({
74
76
  })
75
77
  ) {
76
78
  virtualizedCounter++;
77
- preloaEmojidIfNeeded(
79
+ preloadEmojiIfNeeded(
78
80
  emoji,
79
81
  emojiStyle,
80
82
  scrollTop,
@@ -86,6 +88,12 @@ export function useEmojiVirtualization({
86
88
  );
87
89
  return accumulator;
88
90
  }
91
+
92
+ if (!isCategoryVisible) {
93
+ virtualizedCounter++;
94
+ return accumulator;
95
+ }
96
+
89
97
  accumulator.push(
90
98
  <ClickableEmoji
91
99
  showVariations={showVariations}
@@ -0,0 +1,11 @@
1
+ import * as React from 'react';
2
+
3
+ export function useIsMounted() {
4
+ const [isMounted, setIsMounted] = React.useState(false);
5
+
6
+ React.useEffect(() => {
7
+ setIsMounted(true);
8
+ }, []);
9
+
10
+ return isMounted;
11
+ }
@@ -1 +0,0 @@
1
- export declare function useIsEverMounted(): boolean;
@@ -1,14 +0,0 @@
1
- import * as React from 'react';
2
-
3
- let isEverMounted = false;
4
-
5
- export function useIsEverMounted() {
6
- const [isMounted, setIsMounted] = React.useState(isEverMounted);
7
-
8
- React.useEffect(() => {
9
- setIsMounted(true);
10
- isEverMounted = true;
11
- }, []);
12
-
13
- return isMounted || isEverMounted;
14
- }