emoji-picker-react 4.0.0-rc.8 → 4.0.0

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.
Files changed (117) hide show
  1. package/README.md +176 -43
  2. package/dist/DomUtils/classNames.d.ts +9 -1
  3. package/dist/DomUtils/elementPositionInRow.d.ts +10 -0
  4. package/dist/DomUtils/focusElement.d.ts +5 -0
  5. package/dist/DomUtils/getActiveElement.d.ts +2 -0
  6. package/dist/DomUtils/isDarkTheme.d.ts +1 -0
  7. package/dist/DomUtils/keyboardNavigation.d.ts +8 -0
  8. package/dist/DomUtils/scrollTo.d.ts +3 -1
  9. package/dist/DomUtils/selectors.d.ts +24 -11
  10. package/dist/EmojiPickerReact.d.ts +6 -0
  11. package/dist/components/ErrorBoundary.d.ts +15 -0
  12. package/dist/components/body/{RecentlyUsed.d.ts → Suggested.d.ts} +1 -1
  13. package/dist/components/context/ElementRefContext.d.ts +10 -13
  14. package/dist/components/context/PickerContext.d.ts +3 -3
  15. package/dist/components/emoji/Emoji.d.ts +11 -7
  16. package/dist/components/emoji/ExportedEmoji.d.ts +8 -0
  17. package/dist/components/footer/Preview.d.ts +1 -1
  18. package/dist/config/categoryConfig.d.ts +7 -3
  19. package/dist/config/config.d.ts +18 -15
  20. package/dist/config/useConfig.d.ts +11 -4
  21. package/dist/data/skinToneVariations.d.ts +2 -0
  22. package/dist/dataUtils/emojiSelectors.d.ts +3 -2
  23. package/dist/dataUtils/suggested.d.ts +11 -0
  24. package/dist/emoji-picker-react.cjs.development.css +9 -9
  25. package/dist/emoji-picker-react.cjs.development.js +3774 -3070
  26. package/dist/emoji-picker-react.cjs.development.js.map +1 -1
  27. package/dist/emoji-picker-react.cjs.production.min.js +1 -1
  28. package/dist/emoji-picker-react.cjs.production.min.js.map +1 -1
  29. package/dist/emoji-picker-react.esm.js +3784 -3080
  30. package/dist/emoji-picker-react.esm.js.map +1 -1
  31. package/dist/hooks/preloadEmoji.d.ts +1 -1
  32. package/dist/hooks/useActiveCategoryScrollDetection.d.ts +1 -2
  33. package/dist/hooks/useCloseAllOpenToggles.d.ts +2 -4
  34. package/dist/hooks/useDebouncedState.d.ts +1 -1
  35. package/dist/hooks/useDisallowMouseMove.d.ts +4 -0
  36. package/dist/hooks/useEmojiPreviewEvents.d.ts +3 -0
  37. package/dist/hooks/useFilter.d.ts +3 -3
  38. package/dist/hooks/useFocus.d.ts +3 -0
  39. package/dist/hooks/useInitialLoad.d.ts +1 -1
  40. package/dist/hooks/useIsEmojiHidden.d.ts +7 -1
  41. package/dist/hooks/useKeyboardNavigation.d.ts +1 -0
  42. package/dist/hooks/useMouseDownHandlers.d.ts +3 -2
  43. package/dist/hooks/useOnFocus.d.ts +1 -0
  44. package/dist/hooks/useScrollCategoryIntoView.d.ts +1 -0
  45. package/dist/hooks/useSetVariationPicker.d.ts +2 -0
  46. package/dist/index.d.ts +2 -2
  47. package/dist/types/exposedTypes.d.ts +5 -1
  48. package/package.json +13 -5
  49. package/src/DomUtils/classNames.ts +12 -1
  50. package/src/DomUtils/elementPositionInRow.ts +167 -0
  51. package/src/DomUtils/focusElement.ts +35 -0
  52. package/src/DomUtils/getActiveElement.ts +5 -0
  53. package/src/DomUtils/isDarkTheme.ts +7 -0
  54. package/src/DomUtils/keyboardNavigation.ts +161 -0
  55. package/src/DomUtils/scrollTo.ts +22 -1
  56. package/src/DomUtils/selectors.ts +200 -41
  57. package/src/EmojiPickerReact.css +99 -48
  58. package/src/EmojiPickerReact.tsx +26 -0
  59. package/src/components/ErrorBoundary.tsx +28 -0
  60. package/src/components/body/Body.tsx +3 -2
  61. package/src/components/body/EmojiCategory.css +14 -28
  62. package/src/components/body/EmojiCategory.tsx +5 -4
  63. package/src/components/body/EmojiList.tsx +13 -13
  64. package/src/components/body/EmojiVariationPicker.css +13 -5
  65. package/src/components/body/EmojiVariationPicker.tsx +25 -17
  66. package/src/components/body/Suggested.tsx +54 -0
  67. package/src/components/context/ElementRefContext.tsx +54 -19
  68. package/src/components/context/PickerContext.tsx +36 -33
  69. package/src/components/emoji/Emoji.css +42 -12
  70. package/src/components/emoji/Emoji.tsx +73 -34
  71. package/src/components/emoji/ExportedEmoji.tsx +30 -0
  72. package/src/components/footer/Preview.css +1 -1
  73. package/src/components/footer/Preview.tsx +16 -23
  74. package/src/components/header/Search.css +7 -7
  75. package/src/components/header/Search.tsx +31 -7
  76. package/src/components/header/SkinTonePicker.css +15 -8
  77. package/src/components/header/SkinTonePicker.tsx +23 -5
  78. package/src/components/main/PickerMain.css +0 -2
  79. package/src/components/main/PickerMain.tsx +14 -1
  80. package/src/components/navigation/CategoryNavigation.css +41 -10
  81. package/src/components/navigation/CategoryNavigation.tsx +22 -10
  82. package/src/components/navigation/svg/CategoryNav.svg +343 -379
  83. package/src/config/categoryConfig.ts +40 -14
  84. package/src/config/config.ts +59 -13
  85. package/src/config/useConfig.ts +35 -7
  86. package/src/data/emojis.json +1 -1
  87. package/src/data/skinToneVariations.ts +19 -0
  88. package/src/dataUtils/emojiSelectors.ts +25 -5
  89. package/src/dataUtils/{recentlyUsed.ts → suggested.ts} +20 -18
  90. package/src/hooks/preloadEmoji.ts +13 -2
  91. package/src/hooks/useActiveCategoryScrollDetection.ts +10 -8
  92. package/src/hooks/useCloseAllOpenToggles.ts +15 -4
  93. package/src/hooks/useDebouncedState.ts +10 -7
  94. package/src/hooks/useDisallowMouseMove.ts +47 -0
  95. package/src/hooks/useEmojiPreviewEvents.ts +103 -0
  96. package/src/hooks/useFilter.ts +46 -47
  97. package/src/hooks/useFocus.ts +40 -0
  98. package/src/hooks/useInitialLoad.ts +1 -0
  99. package/src/hooks/useIsEmojiHidden.ts +16 -3
  100. package/src/hooks/useKeyboardNavigation.ts +438 -0
  101. package/src/hooks/useMouseDownHandlers.ts +97 -89
  102. package/src/hooks/useOnFocus.ts +46 -0
  103. package/src/hooks/useOnScroll.ts +2 -2
  104. package/src/hooks/useScrollCategoryIntoView.ts +28 -0
  105. package/src/hooks/useSetVariationPicker.ts +28 -0
  106. package/src/index.tsx +9 -17
  107. package/src/types/exposedTypes.ts +6 -1
  108. package/dist/DomUtils/emojiElementRef.d.ts +0 -10
  109. package/dist/DomUtils/scrollCategoryIntoView.d.ts +0 -1
  110. package/dist/dataUtils/recentlyUsed.d.ts +0 -11
  111. package/dist/hooks/useEmojiElements.d.ts +0 -1
  112. package/dist/hooks/useEmojiMouseEnter.d.ts +0 -3
  113. package/src/DomUtils/emojiElementRef.ts +0 -42
  114. package/src/DomUtils/scrollCategoryIntoView.ts +0 -18
  115. package/src/components/body/RecentlyUsed.tsx +0 -50
  116. package/src/hooks/useEmojiElements.ts +0 -7
  117. package/src/hooks/useEmojiMouseEnter.ts +0 -59
@@ -7,7 +7,8 @@ import {
7
7
  } from '../../config/categoryConfig';
8
8
  import {
9
9
  useCategoriesConfig,
10
- useEmojiStyleConfig
10
+ useEmojiStyleConfig,
11
+ useLazyLoadEmojisConfig
11
12
  } from '../../config/useConfig';
12
13
  import { emojisByCategory, emojiUnified } from '../../dataUtils/emojiSelectors';
13
14
  import { useIsEmojiHidden } from '../../hooks/useIsEmojiHidden';
@@ -15,12 +16,12 @@ import {
15
16
  useActiveSkinToneState,
16
17
  useIsPastInitialLoad
17
18
  } from '../context/PickerContext';
18
- import { Emoji } from '../emoji/Emoji';
19
+ import { ClickableEmoji } from '../emoji/Emoji';
19
20
 
20
21
  import { EmojiCategory } from './EmojiCategory';
21
- import { RecentlyUsed } from './RecentlyUsed';
22
+ import { Suggested } from './Suggested';
23
+
22
24
  import './EmojiList.css';
23
- import { getEmojiRef } from '../../DomUtils/emojiElementRef';
24
25
 
25
26
  export function EmojiList() {
26
27
  const categories = useCategoriesConfig();
@@ -30,10 +31,8 @@ export function EmojiList() {
30
31
  {categories.map((categoryConfig, index) => {
31
32
  const category = categoryFromCategoryConfig(categoryConfig);
32
33
 
33
- if (category === Categories.RECENTLY_USED) {
34
- return (
35
- <RecentlyUsed key={category} categoryConfig={categoryConfig} />
36
- );
34
+ if (category === Categories.SUGGESTED) {
35
+ return <Suggested key={category} categoryConfig={categoryConfig} />;
37
36
  }
38
37
 
39
38
  return (
@@ -59,6 +58,7 @@ function RenderCategory({
59
58
  categoryConfig: CategoryConfig;
60
59
  }) {
61
60
  const isEmojiHidden = useIsEmojiHidden();
61
+ const lazyLoadEmojis = useLazyLoadEmojisConfig();
62
62
  const emojiStyle = useEmojiStyleConfig();
63
63
  const isPastInitialLoad = useIsPastInitialLoad();
64
64
  const [activeSkinTone] = useActiveSkinToneState();
@@ -72,21 +72,21 @@ function RenderCategory({
72
72
 
73
73
  const emojis = emojisToPush.map(emoji => {
74
74
  const unified = emojiUnified(emoji, activeSkinTone);
75
- const hidden = isEmojiHidden(emoji);
76
- const emojiRef = getEmojiRef(emojiUnified(emoji));
75
+ const { failedToLoad, filteredOut, hidden } = isEmojiHidden(emoji);
77
76
 
78
77
  if (hidden) {
79
78
  hiddenCounter++;
80
79
  }
81
80
 
82
81
  return (
83
- <Emoji
82
+ <ClickableEmoji
84
83
  key={unified}
85
84
  emoji={emoji}
86
85
  unified={unified}
87
- hidden={hidden}
86
+ hidden={failedToLoad}
87
+ hiddenOnSearch={filteredOut}
88
88
  emojiStyle={emojiStyle}
89
- emojiRef={emojiRef}
89
+ lazyLoad={lazyLoadEmojis}
90
90
  />
91
91
  );
92
92
  });
@@ -1,8 +1,8 @@
1
1
  .EmojiPickerReact .epr-emoji-variation-picker {
2
2
  position: absolute;
3
3
  top: 0;
4
- right: 5px;
5
- left: 5px;
4
+ right: 15px;
5
+ left: 15px;
6
6
  padding: 5px;
7
7
  height: 0;
8
8
  box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2);
@@ -18,12 +18,20 @@
18
18
  height: var(--epr-emoji-variation-picker-height);
19
19
  z-index: var(--epr-skin-variation-picker-z-index);
20
20
  background: var(--epr-emoji-variation-picker-bg-color);
21
+ transform: scale(0.9);
22
+ transition: transform 0.1s ease-out, opacity 0.2s ease-out;
23
+ }
24
+
25
+ .EmojiPickerReact .epr-emoji-variation-picker.pointing-up {
26
+ transform-origin: center 0%;
27
+ transform: scale(0.9);
21
28
  }
22
29
 
23
30
  .EmojiPickerReact .epr-emoji-variation-picker.visible {
24
31
  opacity: 1;
25
32
  visibility: visible;
26
33
  pointer-events: all;
34
+ transform: scale(1);
27
35
  }
28
36
  .EmojiPickerReact
29
37
  .epr-emoji-variation-picker
@@ -56,10 +64,10 @@
56
64
 
57
65
  .EmojiPickerReact .epr-emoji-variation-picker .epr-emoji-pointer {
58
66
  top: 100%;
59
- transform: translateX(-7px);
67
+ transform: translateX(-18px);
60
68
  }
61
69
 
62
- .EmojiPickerReact .epr-emoji-variation-picker .epr-emoji-pointer.pointing-up {
70
+ .EmojiPickerReact .epr-emoji-variation-picker.pointing-up .epr-emoji-pointer {
63
71
  top: 0;
64
- transform: rotate(180deg) translateY(100%) translateX(7px);
72
+ transform: rotate(180deg) translateY(100%) translateX(18px);
65
73
  }
@@ -1,8 +1,10 @@
1
1
  import clsx from 'clsx';
2
2
  import * as React from 'react';
3
3
 
4
+ import { ClassNames } from '../../DomUtils/classNames';
5
+ import { focusElement } from '../../DomUtils/focusElement';
4
6
  import {
5
- buttonFromEmoji,
7
+ buttonFromTarget,
6
8
  elementHeight,
7
9
  emojiTrueOffsetTop,
8
10
  emojiTruOffsetLeft
@@ -17,10 +19,11 @@ import {
17
19
  import {
18
20
  useAnchoredEmojiRef,
19
21
  useBodyRef,
20
- useSetAnchoredEmojiRef
22
+ useSetAnchoredEmojiRef,
23
+ useVariationPickerRef
21
24
  } from '../context/ElementRefContext';
22
25
  import { useEmojiVariationPickerState } from '../context/PickerContext';
23
- import { Emoji } from '../emoji/Emoji';
26
+ import { ClickableEmoji } from '../emoji/Emoji';
24
27
  import './EmojiVariationPicker.css';
25
28
 
26
29
  enum Direction {
@@ -29,7 +32,8 @@ enum Direction {
29
32
  }
30
33
 
31
34
  export function EmojiVariationPicker() {
32
- const VariationPickerRef = React.useRef<HTMLDivElement>(null);
35
+ const AnchoredEmojiRef = useAnchoredEmojiRef();
36
+ const VariationPickerRef = useVariationPickerRef();
33
37
  const [emoji] = useEmojiVariationPickerState();
34
38
  const emojiStyle = useEmojiStyleConfig();
35
39
  const { getTop, getMenuDirection } = useVariationPickerTop(
@@ -38,10 +42,18 @@ export function EmojiVariationPicker() {
38
42
  const setAnchoredEmojiRef = useSetAnchoredEmojiRef();
39
43
  const getPointerStyle = usePointerStyle(VariationPickerRef);
40
44
 
41
- const visible = emoji && emojiHasVariations(emoji);
45
+ const button = buttonFromTarget(AnchoredEmojiRef.current);
46
+
47
+ const visible =
48
+ emoji &&
49
+ button &&
50
+ emojiHasVariations(emoji) &&
51
+ button.classList.contains(ClassNames.emojiHasVariatios);
52
+
42
53
  let top, pointerStyle;
43
54
 
44
- if (!visible) {
55
+ if (!visible && AnchoredEmojiRef.current) {
56
+ focusElement(AnchoredEmojiRef.current);
45
57
  setAnchoredEmojiRef(null);
46
58
  } else {
47
59
  top = getTop();
@@ -54,7 +66,8 @@ export function EmojiVariationPicker() {
54
66
  <div
55
67
  ref={VariationPickerRef}
56
68
  className={clsx('epr-emoji-variation-picker', {
57
- visible
69
+ visible,
70
+ 'pointing-up': getMenuDirection() === Direction.Down
58
71
  })}
59
72
  style={{ top }}
60
73
  >
@@ -63,7 +76,7 @@ export function EmojiVariationPicker() {
63
76
  .concat(emojiVariations(safeEmoji))
64
77
  .slice(0, 6)
65
78
  .map(unified => (
66
- <Emoji
79
+ <ClickableEmoji
67
80
  key={unified}
68
81
  emoji={safeEmoji}
69
82
  unified={unified}
@@ -72,12 +85,7 @@ export function EmojiVariationPicker() {
72
85
  />
73
86
  ))
74
87
  : null}
75
- <div
76
- className={clsx('epr-emoji-pointer', {
77
- 'pointing-up': getMenuDirection() === Direction.Down
78
- })}
79
- style={pointerStyle}
80
- />
88
+ <div className="epr-emoji-pointer" style={pointerStyle} />
81
89
  </div>
82
90
  );
83
91
  }
@@ -91,7 +99,7 @@ function usePointerStyle(VariationPickerRef: React.RefObject<HTMLElement>) {
91
99
  }
92
100
 
93
101
  if (AnchoredEmojiRef.current) {
94
- const button = buttonFromEmoji(AnchoredEmojiRef.current);
102
+ const button = buttonFromTarget(AnchoredEmojiRef.current);
95
103
 
96
104
  const offsetLeft = emojiTruOffsetLeft(button);
97
105
 
@@ -100,7 +108,7 @@ function usePointerStyle(VariationPickerRef: React.RefObject<HTMLElement>) {
100
108
  }
101
109
 
102
110
  // half of the button
103
- style.left = offsetLeft + button?.clientWidth / 2 - 12;
111
+ style.left = offsetLeft + button?.clientWidth / 2;
104
112
  }
105
113
 
106
114
  return style;
@@ -135,7 +143,7 @@ function useVariationPickerTop(
135
143
 
136
144
  if (AnchoredEmojiRef.current) {
137
145
  const bodyRef = BodyRef.current;
138
- const button = buttonFromEmoji(AnchoredEmojiRef.current);
146
+ const button = buttonFromTarget(AnchoredEmojiRef.current);
139
147
 
140
148
  const buttonHeight = elementHeight(button);
141
149
 
@@ -0,0 +1,54 @@
1
+ import * as React from 'react';
2
+
3
+ import { CategoryConfig } from '../../config/categoryConfig';
4
+ import {
5
+ useEmojiStyleConfig,
6
+ useSuggestedEmojisModeConfig
7
+ } from '../../config/useConfig';
8
+ import { emojiByUnified } from '../../dataUtils/emojiSelectors';
9
+ import { getsuggested } from '../../dataUtils/suggested';
10
+ import { useUpdateSuggested } from '../context/PickerContext';
11
+ import { ClickableEmoji } from '../emoji/Emoji';
12
+
13
+ import { EmojiCategory } from './EmojiCategory';
14
+
15
+ type Props = Readonly<{
16
+ categoryConfig: CategoryConfig;
17
+ }>;
18
+
19
+ export function Suggested({ categoryConfig }: Props) {
20
+ const [suggestedUpdated] = useUpdateSuggested();
21
+ const suggestedEmojisModeConfig = useSuggestedEmojisModeConfig();
22
+ const suggested = React.useMemo(
23
+ () => getsuggested(suggestedEmojisModeConfig) ?? [],
24
+ // eslint-disable-next-line react-hooks/exhaustive-deps
25
+ [suggestedUpdated, suggestedEmojisModeConfig]
26
+ );
27
+ const emojiStyle = useEmojiStyleConfig();
28
+
29
+ return (
30
+ <EmojiCategory
31
+ categoryConfig={categoryConfig}
32
+ hiddenOnSearch
33
+ hidden={suggested.length === 0}
34
+ >
35
+ {suggested.map(suggestedItem => {
36
+ const emoji = emojiByUnified(suggestedItem.original);
37
+
38
+ if (!emoji) {
39
+ return null;
40
+ }
41
+
42
+ return (
43
+ <ClickableEmoji
44
+ showVariations={false}
45
+ unified={suggestedItem.unified}
46
+ emojiStyle={emojiStyle}
47
+ emoji={emoji}
48
+ key={suggestedItem.unified}
49
+ />
50
+ );
51
+ })}
52
+ </EmojiCategory>
53
+ );
54
+ }
@@ -1,5 +1,7 @@
1
1
  import * as React from 'react';
2
2
 
3
+ import { NullableElement } from '../../DomUtils/selectors';
4
+
3
5
  export function ElementRefContextProvider({
4
6
  children
5
7
  }: {
@@ -7,55 +9,88 @@ export function ElementRefContextProvider({
7
9
  }) {
8
10
  const PickerMainRef = React.useRef<HTMLElement>(null);
9
11
  const AnchoredEmojiRef = React.useRef<HTMLElement>(null);
10
- const BodyRef = React.useRef<HTMLElement>(null);
12
+ const BodyRef = React.useRef<HTMLDivElement>(null);
13
+ const SearchInputRef = React.useRef<HTMLInputElement>(null);
14
+ const SkinTonePickerRef = React.useRef<HTMLDivElement>(null);
15
+ const CategoryNavigationRef = React.useRef<HTMLDivElement>(null);
16
+ const VariationPickerRef = React.useRef<HTMLDivElement>(null);
11
17
 
12
18
  return (
13
19
  <ElementRefContext.Provider
14
- value={{ PickerMainRef, AnchoredEmojiRef, BodyRef }}
20
+ value={{
21
+ AnchoredEmojiRef,
22
+ BodyRef,
23
+ CategoryNavigationRef,
24
+ PickerMainRef,
25
+ SearchInputRef,
26
+ SkinTonePickerRef,
27
+ VariationPickerRef
28
+ }}
15
29
  >
16
30
  {children}
17
31
  </ElementRefContext.Provider>
18
32
  );
19
33
  }
20
34
 
21
- export type ElementRef = React.MutableRefObject<HTMLElement | null>;
35
+ export type ElementRef<
36
+ E extends HTMLElement = HTMLElement
37
+ > = React.MutableRefObject<E | null>;
22
38
 
23
39
  type ElementRefs = {
24
40
  PickerMainRef: ElementRef;
25
41
  AnchoredEmojiRef: ElementRef;
26
- BodyRef: React.MutableRefObject<HTMLElement | null>;
42
+ SkinTonePickerRef: ElementRef<HTMLDivElement>;
43
+ SearchInputRef: ElementRef<HTMLInputElement>;
44
+ BodyRef: ElementRef<HTMLDivElement>;
45
+ CategoryNavigationRef: ElementRef<HTMLDivElement>;
46
+ VariationPickerRef: ElementRef<HTMLDivElement>;
27
47
  };
28
48
 
29
49
  const ElementRefContext = React.createContext<ElementRefs>({
30
- PickerMainRef: React.createRef(),
31
50
  AnchoredEmojiRef: React.createRef(),
32
- BodyRef: React.createRef()
51
+ BodyRef: React.createRef(),
52
+ CategoryNavigationRef: React.createRef(),
53
+ PickerMainRef: React.createRef(),
54
+ SearchInputRef: React.createRef(),
55
+ SkinTonePickerRef: React.createRef(),
56
+ VariationPickerRef: React.createRef()
33
57
  });
34
58
 
35
- type ElementRefKeys = keyof ElementRefs;
36
-
37
- export function useElementRef(key: ElementRefKeys) {
38
- const { [key]: elementRef } = React.useContext(ElementRefContext);
39
- return elementRef;
59
+ function useElementRef() {
60
+ return React.useContext(ElementRefContext);
40
61
  }
41
62
 
42
63
  export function usePickerMainRef() {
43
- return useElementRef('PickerMainRef');
64
+ return useElementRef()['PickerMainRef'];
44
65
  }
45
66
 
46
67
  export function useAnchoredEmojiRef() {
47
- return useElementRef('AnchoredEmojiRef');
68
+ return useElementRef()['AnchoredEmojiRef'];
48
69
  }
49
70
 
50
- export function useSetAnchoredEmojiRef(): (target: null | HTMLElement) => void {
71
+ export function useSetAnchoredEmojiRef(): (target: NullableElement) => void {
51
72
  const AnchoredEmojiRef = useAnchoredEmojiRef();
52
- return (target: null | HTMLElement) => {
73
+ return (target: NullableElement) => {
53
74
  AnchoredEmojiRef.current = target;
54
75
  };
55
76
  }
56
77
 
57
- export function useBodyRef(): React.MutableRefObject<HTMLDivElement | null> {
58
- return useElementRef(
59
- 'BodyRef'
60
- ) as React.MutableRefObject<HTMLDivElement | null>;
78
+ export function useBodyRef() {
79
+ return useElementRef()['BodyRef'];
80
+ }
81
+
82
+ export function useSearchInputRef() {
83
+ return useElementRef()['SearchInputRef'];
84
+ }
85
+
86
+ export function useSkinTonePickerRef() {
87
+ return useElementRef()['SkinTonePickerRef'];
88
+ }
89
+
90
+ export function useCategoryNavigationRef() {
91
+ return useElementRef()['CategoryNavigationRef'];
92
+ }
93
+
94
+ export function useVariationPickerRef() {
95
+ return useElementRef()['VariationPickerRef'];
61
96
  }
@@ -1,14 +1,13 @@
1
1
  import * as React from 'react';
2
2
  import { useState } from 'react';
3
+
3
4
  import { useDefaultSkinToneConfig } from '../../config/useConfig';
4
- import { alphaNumericEmojiIndex } from '../../dataUtils/alphaNumericEmojiIndex';
5
5
  import { DataEmoji } from '../../dataUtils/DataTypes';
6
- import { scrollCategoryIntoView } from '../../DomUtils/scrollCategoryIntoView';
6
+ import { alphaNumericEmojiIndex } from '../../dataUtils/alphaNumericEmojiIndex';
7
7
  import { useDebouncedState } from '../../hooks/useDebouncedState';
8
8
  import { FilterDict } from '../../hooks/useFilter';
9
9
  import { useMarkInitialLoad } from '../../hooks/useInitialLoad';
10
10
  import { SkinTones } from '../../types/exposedTypes';
11
- import { usePickerMainRef } from './ElementRefContext';
12
11
 
13
12
  export function PickerContextProvider({ children }: Props) {
14
13
  const defaultSkinTone = useDefaultSkinToneConfig();
@@ -16,7 +15,9 @@ export function PickerContextProvider({ children }: Props) {
16
15
  // Initialize the filter with the inititial dictionary
17
16
  const filterRef = React.useRef<FilterState>(alphaNumericEmojiIndex);
18
17
  const disallowClickRef = React.useRef<boolean>(false);
18
+ const disallowMouseRef = React.useRef<boolean>(false);
19
19
 
20
+ const suggestedUpdateState = useDebouncedState(Date.now(), 200);
20
21
  const searchTerm = useDebouncedState('', 100);
21
22
  const skinToneFanOpenState = useState<boolean>(false);
22
23
  const activeSkinTone = useState<SkinTones>(defaultSkinTone);
@@ -30,15 +31,17 @@ export function PickerContextProvider({ children }: Props) {
30
31
  return (
31
32
  <PickerContext.Provider
32
33
  value={{
33
- filterRef,
34
- searchTerm,
35
34
  activeCategoryState,
36
35
  activeSkinTone,
36
+ disallowClickRef,
37
+ disallowMouseRef,
38
+ emojiVariationPickerState,
37
39
  emojisThatFailedToLoadState,
40
+ filterRef,
38
41
  isPastInitialLoad,
39
- emojiVariationPickerState,
42
+ searchTerm,
40
43
  skinToneFanOpenState,
41
- disallowClickRef
44
+ suggestedUpdateState
42
45
  }}
43
46
  >
44
47
  {children}
@@ -49,7 +52,8 @@ export function PickerContextProvider({ children }: Props) {
49
52
  type ReactState<T> = [T, React.Dispatch<React.SetStateAction<T>>];
50
53
 
51
54
  const PickerContext = React.createContext<{
52
- searchTerm: [string, (term: string) => void];
55
+ searchTerm: [string, (term: string) => Promise<string>];
56
+ suggestedUpdateState: [number, (term: number) => void];
53
57
  activeCategoryState: ReactState<ActiveCategoryState>;
54
58
  activeSkinTone: ReactState<SkinTones>;
55
59
  emojisThatFailedToLoadState: ReactState<Set<string>>;
@@ -58,16 +62,19 @@ const PickerContext = React.createContext<{
58
62
  skinToneFanOpenState: ReactState<boolean>;
59
63
  filterRef: React.MutableRefObject<FilterState>;
60
64
  disallowClickRef: React.MutableRefObject<boolean>;
65
+ disallowMouseRef: React.MutableRefObject<boolean>;
61
66
  }>({
62
- searchTerm: ['', () => {}],
63
67
  activeCategoryState: [null, () => {}],
64
68
  activeSkinTone: [SkinTones.NEUTRAL, () => {}],
69
+ disallowClickRef: { current: false },
70
+ disallowMouseRef: { current: false },
71
+ emojiVariationPickerState: [null, () => {}],
65
72
  emojisThatFailedToLoadState: [new Set(), () => {}],
73
+ filterRef: { current: {} },
66
74
  isPastInitialLoad: true,
67
- emojiVariationPickerState: [null, () => {}],
75
+ searchTerm: ['', () => new Promise<string>(() => undefined)],
68
76
  skinToneFanOpenState: [false, () => {}],
69
- filterRef: { current: {} },
70
- disallowClickRef: { current: false }
77
+ suggestedUpdateState: [Date.now(), () => {}]
71
78
  });
72
79
 
73
80
  type Props = Readonly<{
@@ -84,32 +91,16 @@ export function useDisallowClickRef() {
84
91
  return disallowClickRef;
85
92
  }
86
93
 
94
+ export function useDisallowMouseRef() {
95
+ const { disallowMouseRef } = React.useContext(PickerContext);
96
+ return disallowMouseRef;
97
+ }
98
+
87
99
  export function useSearchTermState() {
88
100
  const { searchTerm } = React.useContext(PickerContext);
89
101
  return searchTerm;
90
102
  }
91
103
 
92
- export function useActiveCategoryState(): [
93
- ActiveCategoryState,
94
- (nextActive: string) => void,
95
- (nextActive: string) => void
96
- ] {
97
- const { activeCategoryState } = React.useContext(PickerContext);
98
- const PickerMainRef = usePickerMainRef();
99
-
100
- const [activeCategory, setActiveCategory] = activeCategoryState;
101
- return [activeCategory, setCategory, setActiveCategoryAndScroll];
102
-
103
- function setCategory(category: string) {
104
- setActiveCategory(category);
105
- }
106
-
107
- function setActiveCategoryAndScroll(category: string) {
108
- setCategory(category);
109
- scrollCategoryIntoView(PickerMainRef.current, category);
110
- }
111
- }
112
-
113
104
  export function useActiveSkinToneState(): [
114
105
  SkinTones,
115
106
  (skinTone: SkinTones) => void
@@ -138,6 +129,18 @@ export function useSkinToneFanOpenState() {
138
129
  return skinToneFanOpenState;
139
130
  }
140
131
 
132
+ export function useUpdateSuggested(): [number, () => void] {
133
+ const { suggestedUpdateState } = React.useContext(PickerContext);
134
+
135
+ const [suggestedUpdated, setsuggestedUpdate] = suggestedUpdateState;
136
+ return [
137
+ suggestedUpdated,
138
+ function updateSuggested() {
139
+ setsuggestedUpdate(Date.now());
140
+ }
141
+ ];
142
+ }
143
+
141
144
  export type FilterState = Record<string, FilterDict>;
142
145
 
143
146
  type ActiveCategoryState = null | string;
@@ -1,6 +1,15 @@
1
1
  .EmojiPickerReact button.epr-emoji {
2
2
  position: relative;
3
- padding: var(--epr-emoji-padding);
3
+ width: var(--epr-emoji-fullsize);
4
+ height: var(--epr-emoji-fullsize);
5
+ box-sizing: border-box;
6
+ display: flex;
7
+ align-items: center;
8
+ justify-content: center;
9
+ max-width: var(--epr-emoji-fullsize);
10
+ max-height: var(--epr-emoji-fullsize);
11
+ border-radius: 8px;
12
+ overflow: hidden;
4
13
  }
5
14
 
6
15
  .EmojiPickerReact button.epr-emoji.epr-emoji-has-variations:after {
@@ -8,8 +17,8 @@
8
17
  display: block;
9
18
  width: 0;
10
19
  height: 0;
11
- right: -2px;
12
- bottom: 0px;
20
+ right: 0px;
21
+ bottom: 1px;
13
22
  position: absolute;
14
23
  border-left: 4px solid transparent;
15
24
  border-right: 4px solid transparent;
@@ -18,27 +27,48 @@
18
27
  z-index: var(--epr-emoji-variations-indictator-z-index);
19
28
  }
20
29
 
30
+ .EmojiPickerReact button.epr-emoji .epr-emoji-img {
31
+ max-width: var(--epr-emoji-fullsize);
32
+ max-height: var(--epr-emoji-fullsize);
33
+ padding: var(--epr-emoji-padding);
34
+ }
35
+
36
+ .EmojiPickerReact button.epr-emoji .epr-emoji-native {
37
+ padding: var(--epr-emoji-padding);
38
+ }
39
+
21
40
  .EmojiPickerReact button.epr-emoji > * {
22
- height: var(--epr-emoji-size);
23
- width: var(--epr-emoji-size);
24
- font-size: var(--epr-emoji-size);
41
+ align-self: center;
42
+ justify-self: center;
43
+ display: block;
25
44
  }
26
45
 
27
46
  .EmojiPickerReact button.epr-emoji.epr-emoji-has-variations:hover:after {
28
47
  border-bottom: 4px solid var(--epr-emoji-variation-indicator-color-hover);
29
48
  }
30
49
 
31
- .EmojiPickerReact button.epr-emoji > .epr-emoji-img {
50
+ .__EmojiPicker__.epr-emoji-img {
32
51
  font-size: 0;
33
52
  }
34
53
 
35
- .EmojiPickerReact button.epr-emoji > .epr-emoji-native {
36
- font-family: 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Emoji', Times,
37
- Symbola, Aegyptus, Code2000, Code2001, Code2002, Musica, serif, LastResort;
54
+ .__EmojiPicker__.epr-emoji-native {
55
+ font-family: 'Segoe UI Emoji', 'Segoe UI Symbol', 'Segoe UI',
56
+ 'Apple Color Emoji', 'Twemoji Mozilla', 'Noto Color Emoji', 'Android Emoji',
57
+ Times, Symbola, Aegyptus, Code2000, Code2001, Code2002, Musica, serif,
58
+ LastResort;
59
+ position: relative;
38
60
  line-height: 100%;
61
+ font-size: var(--epr-emoji-size);
62
+ text-align: center;
63
+ align-self: center;
64
+ justify-self: center;
65
+ letter-spacing: 0;
39
66
  }
40
67
 
41
- .EmojiPickerReact button.epr-emoji:hover {
42
- border-radius: 8px;
68
+ .EmojiPickerReact button.epr-emoji:hover > * {
43
69
  background-color: var(--epr-emoji-hover-color);
44
70
  }
71
+
72
+ .EmojiPickerReact button.epr-emoji:focus > * {
73
+ background-color: var(--epr-focus-bg-color);
74
+ }