emoji-picker-react 4.22.2 → 5.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 (405) hide show
  1. package/README.md +335 -49
  2. package/dist/DomUtils/categoryNameFromDom.d.ts +1 -1
  3. package/dist/DomUtils/classNames.d.ts +24 -23
  4. package/dist/DomUtils/detectEmojyPartiallyBelowFold.d.ts +2 -2
  5. package/dist/DomUtils/elementPositionInRow.d.ts +5 -5
  6. package/dist/DomUtils/eventBelongsToPicker.d.ts +2 -0
  7. package/dist/DomUtils/focusElement.d.ts +5 -5
  8. package/dist/DomUtils/getActiveElement.d.ts +2 -2
  9. package/dist/DomUtils/isJsdom.d.ts +1 -0
  10. package/dist/DomUtils/isRtl.d.ts +1 -0
  11. package/dist/DomUtils/keyboardNavigation.d.ts +7 -8
  12. package/dist/DomUtils/scrollTo.d.ts +4 -5
  13. package/dist/DomUtils/selectors.d.ts +31 -35
  14. package/dist/EmojiPickerReact.d.ts +5 -5
  15. package/dist/Stylesheet/DedupedStyle.d.ts +13 -0
  16. package/dist/Stylesheet/stylesheet.d.ts +40 -37
  17. package/dist/components/ErrorBoundary.d.ts +15 -15
  18. package/dist/components/Layout/Absolute.d.ts +8 -8
  19. package/dist/components/Layout/Flex.d.ts +13 -13
  20. package/dist/components/Layout/Relative.d.ts +8 -8
  21. package/dist/components/Layout/Space.d.ts +7 -7
  22. package/dist/components/Reactions/BtnPlus.d.ts +2 -2
  23. package/dist/components/Reactions/DEFAULT_REACTIONS.d.ts +1 -1
  24. package/dist/components/Reactions/Reactions.d.ts +3 -2
  25. package/dist/components/atoms/Button.d.ts +11 -6
  26. package/dist/components/body/EmojiCategory.d.ts +13 -12
  27. package/dist/components/body/EmojiList.d.ts +7 -4
  28. package/dist/components/body/EmojiVariationPicker.d.ts +2 -2
  29. package/dist/components/body/MeasureEmoji.d.ts +2 -2
  30. package/dist/components/body/listComponents.d.ts +47 -0
  31. package/dist/components/context/ElementRefContext.d.ts +18 -16
  32. package/dist/components/context/PickerConfigContext.d.ts +14 -9
  33. package/dist/components/context/PickerContext.d.ts +57 -25
  34. package/dist/components/context/PickerDataContext.d.ts +23 -17
  35. package/dist/components/emoji/BaseEmojiProps.d.ts +14 -13
  36. package/dist/components/emoji/ClickableEmojiButton.d.ts +23 -15
  37. package/dist/components/emoji/Emoji.d.ts +18 -15
  38. package/dist/components/emoji/EmojiImg.d.ts +11 -11
  39. package/dist/components/emoji/ExportedEmoji.d.ts +11 -11
  40. package/dist/components/emoji/NativeEmoji.d.ts +6 -6
  41. package/dist/components/emoji/ViewOnlyEmoji.d.ts +3 -3
  42. package/dist/components/emoji/emojiStyles.d.ts +6 -1
  43. package/dist/components/footer/Preview.d.ts +7 -7
  44. package/dist/components/header/Header.d.ts +2 -2
  45. package/dist/components/header/Search/BtnClearSearch.d.ts +2 -2
  46. package/dist/components/header/Search/IcnSearch.d.ts +2 -2
  47. package/dist/components/header/Search/Search.d.ts +11 -3
  48. package/dist/components/header/SkinTonePicker/BtnSkinToneVariation.d.ts +12 -12
  49. package/dist/components/header/SkinTonePicker/SkinTonePicker.d.ts +11 -11
  50. package/dist/components/icons/svgIcons.d.ts +4 -0
  51. package/dist/components/main/PickerMainBehaviors.d.ts +4 -0
  52. package/dist/components/main/defaultAppearance.d.ts +12 -0
  53. package/dist/components/main/labelHeight.d.ts +1 -0
  54. package/dist/components/navigation/CategoryButton.d.ts +12 -12
  55. package/dist/components/navigation/CategoryNavIcon.d.ts +4 -4
  56. package/dist/components/navigation/CategoryNavigation.d.ts +19 -2
  57. package/dist/config/categoryConfig.d.ts +34 -34
  58. package/dist/config/cdnUrls.d.ts +2 -2
  59. package/dist/config/compareConfig.d.ts +10 -2
  60. package/dist/config/config.d.ts +166 -64
  61. package/dist/config/customEmojiConfig.d.ts +14 -14
  62. package/dist/config/defaultPickerConfiguration.d.ts +10 -0
  63. package/dist/config/mutableConfig.d.ts +32 -10
  64. package/dist/config/useConfig.d.ts +48 -36
  65. package/dist/data/defaultEmojiData.d.ts +9 -0
  66. package/dist/data/defaultEmojiData.js +40 -0
  67. package/dist/data/emojis-bn.d.mts +39 -0
  68. package/dist/data/emojis-bn.d.ts +38 -2
  69. package/dist/data/emojis-bn.js +25 -2
  70. package/dist/data/emojis-bn.mjs +2 -0
  71. package/dist/data/emojis-da.d.mts +39 -0
  72. package/dist/data/emojis-da.d.ts +38 -2
  73. package/dist/data/emojis-da.js +25 -2
  74. package/dist/data/emojis-da.mjs +2 -0
  75. package/dist/data/emojis-de.d.mts +39 -0
  76. package/dist/data/emojis-de.d.ts +38 -2
  77. package/dist/data/emojis-de.js +25 -2
  78. package/dist/data/emojis-de.mjs +2 -0
  79. package/dist/data/emojis-en-gb.d.mts +39 -0
  80. package/dist/data/emojis-en-gb.d.ts +38 -2
  81. package/dist/data/emojis-en-gb.js +25 -2
  82. package/dist/data/emojis-en-gb.mjs +2 -0
  83. package/dist/data/emojis-en.d.mts +39 -0
  84. package/dist/data/emojis-en.d.ts +38 -2
  85. package/dist/data/emojis-en.js +25 -2
  86. package/dist/data/emojis-en.mjs +2 -0
  87. package/dist/data/emojis-es-mx.d.mts +39 -0
  88. package/dist/data/emojis-es-mx.d.ts +38 -2
  89. package/dist/data/emojis-es-mx.js +25 -2
  90. package/dist/data/emojis-es-mx.mjs +2 -0
  91. package/dist/data/emojis-es.d.mts +39 -0
  92. package/dist/data/emojis-es.d.ts +38 -2
  93. package/dist/data/emojis-es.js +25 -2
  94. package/dist/data/emojis-es.mjs +2 -0
  95. package/dist/data/emojis-et.d.mts +39 -0
  96. package/dist/data/emojis-et.d.ts +38 -2
  97. package/dist/data/emojis-et.js +25 -2
  98. package/dist/data/emojis-et.mjs +2 -0
  99. package/dist/data/emojis-fi.d.mts +39 -0
  100. package/dist/data/emojis-fi.d.ts +38 -2
  101. package/dist/data/emojis-fi.js +25 -2
  102. package/dist/data/emojis-fi.mjs +2 -0
  103. package/dist/data/emojis-fr.d.mts +39 -0
  104. package/dist/data/emojis-fr.d.ts +38 -2
  105. package/dist/data/emojis-fr.js +25 -2
  106. package/dist/data/emojis-fr.mjs +2 -0
  107. package/dist/data/emojis-hi.d.mts +39 -0
  108. package/dist/data/emojis-hi.d.ts +38 -2
  109. package/dist/data/emojis-hi.js +25 -2
  110. package/dist/data/emojis-hi.mjs +2 -0
  111. package/dist/data/emojis-hu.d.mts +39 -0
  112. package/dist/data/emojis-hu.d.ts +38 -2
  113. package/dist/data/emojis-hu.js +25 -2
  114. package/dist/data/emojis-hu.mjs +2 -0
  115. package/dist/data/emojis-it.d.mts +39 -0
  116. package/dist/data/emojis-it.d.ts +38 -2
  117. package/dist/data/emojis-it.js +25 -2
  118. package/dist/data/emojis-it.mjs +2 -0
  119. package/dist/data/emojis-ja.d.mts +39 -0
  120. package/dist/data/emojis-ja.d.ts +38 -2
  121. package/dist/data/emojis-ja.js +25 -2
  122. package/dist/data/emojis-ja.mjs +2 -0
  123. package/dist/data/emojis-ko.d.mts +39 -0
  124. package/dist/data/emojis-ko.d.ts +38 -2
  125. package/dist/data/emojis-ko.js +25 -2
  126. package/dist/data/emojis-ko.mjs +2 -0
  127. package/dist/data/emojis-lt.d.mts +39 -0
  128. package/dist/data/emojis-lt.d.ts +38 -2
  129. package/dist/data/emojis-lt.js +25 -2
  130. package/dist/data/emojis-lt.mjs +2 -0
  131. package/dist/data/emojis-ms.d.mts +39 -0
  132. package/dist/data/emojis-ms.d.ts +38 -2
  133. package/dist/data/emojis-ms.js +25 -2
  134. package/dist/data/emojis-ms.mjs +2 -0
  135. package/dist/data/emojis-nb.d.mts +39 -0
  136. package/dist/data/emojis-nb.d.ts +38 -2
  137. package/dist/data/emojis-nb.js +25 -2
  138. package/dist/data/emojis-nb.mjs +2 -0
  139. package/dist/data/emojis-nl.d.mts +39 -0
  140. package/dist/data/emojis-nl.d.ts +38 -2
  141. package/dist/data/emojis-nl.js +25 -2
  142. package/dist/data/emojis-nl.mjs +2 -0
  143. package/dist/data/emojis-pl.d.mts +39 -0
  144. package/dist/data/emojis-pl.d.ts +38 -2
  145. package/dist/data/emojis-pl.js +25 -2
  146. package/dist/data/emojis-pl.mjs +2 -0
  147. package/dist/data/emojis-pt.d.mts +39 -0
  148. package/dist/data/emojis-pt.d.ts +38 -2
  149. package/dist/data/emojis-pt.js +25 -2
  150. package/dist/data/emojis-pt.mjs +2 -0
  151. package/dist/data/emojis-ru.d.mts +39 -0
  152. package/dist/data/emojis-ru.d.ts +38 -2
  153. package/dist/data/emojis-ru.js +25 -2
  154. package/dist/data/emojis-ru.mjs +2 -0
  155. package/dist/data/emojis-sv.d.mts +39 -0
  156. package/dist/data/emojis-sv.d.ts +38 -2
  157. package/dist/data/emojis-sv.js +25 -2
  158. package/dist/data/emojis-sv.mjs +2 -0
  159. package/dist/data/emojis-th.d.mts +39 -0
  160. package/dist/data/emojis-th.d.ts +38 -2
  161. package/dist/data/emojis-th.js +25 -2
  162. package/dist/data/emojis-th.mjs +2 -0
  163. package/dist/data/emojis-uk.d.mts +39 -0
  164. package/dist/data/emojis-uk.d.ts +38 -2
  165. package/dist/data/emojis-uk.js +25 -2
  166. package/dist/data/emojis-uk.mjs +2 -0
  167. package/dist/data/emojis-vi.d.mts +39 -0
  168. package/dist/data/emojis-vi.d.ts +38 -2
  169. package/dist/data/emojis-vi.js +25 -2
  170. package/dist/data/emojis-vi.mjs +2 -0
  171. package/dist/data/emojis-zh-hant.d.mts +39 -0
  172. package/dist/data/emojis-zh-hant.d.ts +38 -2
  173. package/dist/data/emojis-zh-hant.js +25 -2
  174. package/dist/data/emojis-zh-hant.mjs +2 -0
  175. package/dist/data/emojis-zh.d.mts +39 -0
  176. package/dist/data/emojis-zh.d.ts +38 -2
  177. package/dist/data/emojis-zh.js +25 -2
  178. package/dist/data/emojis-zh.mjs +2 -0
  179. package/dist/data/index.js +1 -0
  180. package/dist/data/registerDefaultEmojiData.d.ts +1 -0
  181. package/dist/data/registerDefaultEmojiData.js +3 -0
  182. package/dist/data-core/pickerData.d.ts +12 -0
  183. package/dist/data-core/prepare.d.ts +23 -0
  184. package/dist/data-core/search.d.ts +13 -0
  185. package/dist/data-core/types.d.ts +23 -0
  186. package/dist/data.d.mts +72 -0
  187. package/dist/data.d.ts +72 -0
  188. package/dist/dataUtils/DataTypes.d.ts +23 -19
  189. package/dist/dataUtils/emojiSelectors.d.ts +15 -16
  190. package/dist/dataUtils/emojiUtils.d.ts +1 -12
  191. package/dist/dataUtils/nativeEmojiSequenceSupport.d.ts +10 -0
  192. package/dist/dataUtils/nativeEmojiSupport.d.ts +40 -0
  193. package/dist/dataUtils/parseNativeEmoji.d.ts +1 -1
  194. package/dist/dataUtils/suggested.d.ts +11 -11
  195. package/dist/dataUtils/suggestedEmojis.d.ts +7 -0
  196. package/dist/esm/chunks/chunk-46BUDQLE.mjs +228 -0
  197. package/dist/esm/chunks/chunk-7ZGTUZFB.mjs +16 -0
  198. package/dist/esm/chunks/chunk-FQIJ4HBW.mjs +25 -0
  199. package/dist/esm/chunks/chunk-KQPOIRSC.mjs +9305 -0
  200. package/dist/esm/chunks/chunk-LO4TPPLH.mjs +43 -0
  201. package/dist/esm/chunks/emojis-6L3GRBAP.mjs +9 -0
  202. package/dist/esm/data.mjs +18 -0
  203. package/dist/esm/index.mjs +389 -0
  204. package/dist/esm/primitives/index.mjs +67 -0
  205. package/dist/hooks/preloadEmoji.d.ts +9 -9
  206. package/dist/hooks/useActiveCategoryScrollDetection.d.ts +11 -11
  207. package/dist/hooks/useCategoryHeight.d.ts +6 -5
  208. package/dist/hooks/useCloseAllOpenToggles.d.ts +2 -2
  209. package/dist/hooks/useDataIdentityStabilityWarning.d.ts +13 -0
  210. package/dist/hooks/useDebouncedState.d.ts +1 -1
  211. package/dist/hooks/useDisallowMouseMove.d.ts +4 -4
  212. package/dist/hooks/useDisallowedEmojis.d.ts +2 -3
  213. package/dist/hooks/useEmojiPreviewEvents.d.ts +3 -3
  214. package/dist/hooks/useEmojiVirtualization.d.ts +21 -17
  215. package/dist/hooks/useFilter.d.ts +10 -16
  216. package/dist/hooks/useFocus.d.ts +3 -3
  217. package/dist/hooks/useInitialLoad.d.ts +2 -2
  218. package/dist/hooks/useIsEmojiHidden.d.ts +8 -8
  219. package/dist/hooks/useIsMounted.d.ts +1 -1
  220. package/dist/hooks/useIsSearchMode.d.ts +1 -1
  221. package/dist/hooks/useIsomorphicLayoutEffect.d.ts +2 -2
  222. package/dist/hooks/useKeyboardNavigation.d.ts +3 -1
  223. package/dist/hooks/useMouseDownHandlers.d.ts +8 -4
  224. package/dist/hooks/useOnFocus.d.ts +1 -1
  225. package/dist/hooks/useOnScroll.d.ts +2 -2
  226. package/dist/hooks/useReactionsFocus.d.ts +1 -0
  227. package/dist/hooks/useRegisterRegion.d.ts +24 -0
  228. package/dist/hooks/useResolvedEmojiData.d.ts +37 -0
  229. package/dist/hooks/useScrollCategoryIntoView.d.ts +1 -1
  230. package/dist/hooks/useSearchController.d.ts +17 -0
  231. package/dist/hooks/useSearchResults.d.ts +8 -0
  232. package/dist/hooks/useSetVariationPicker.d.ts +2 -2
  233. package/dist/hooks/useShouldHideCustomEmojis.d.ts +1 -1
  234. package/dist/hooks/useShouldShowSkinTonePicker.d.ts +4 -4
  235. package/dist/index.d.mts +376 -0
  236. package/dist/index.d.ts +376 -9
  237. package/dist/index.js +2 -8
  238. package/dist/primitives/CategoryNav.d.ts +4 -0
  239. package/dist/primitives/Empty.d.ts +6 -0
  240. package/dist/primitives/List.d.ts +4 -0
  241. package/dist/primitives/LoadError.d.ts +8 -0
  242. package/dist/primitives/Loading.d.ts +4 -0
  243. package/dist/primitives/Panel.d.ts +3 -0
  244. package/dist/primitives/Preview.d.ts +3 -0
  245. package/dist/primitives/Reactions.d.ts +2 -0
  246. package/dist/primitives/Root.d.ts +26 -0
  247. package/dist/primitives/Search.d.ts +5 -0
  248. package/dist/primitives/SearchInput.d.ts +3 -0
  249. package/dist/primitives/SkinTone.d.ts +4 -0
  250. package/dist/primitives/Viewport.d.ts +19 -0
  251. package/dist/primitives/appearance.d.ts +3 -0
  252. package/dist/primitives/components.d.ts +27 -0
  253. package/dist/primitives/hooks.d.ts +48 -0
  254. package/dist/primitives/index.d.mts +561 -0
  255. package/dist/primitives/index.d.ts +561 -0
  256. package/dist/primitives/index.js +2 -0
  257. package/dist/primitives/nativeProps.d.ts +15 -0
  258. package/dist/primitives/scope.d.ts +13 -0
  259. package/dist/primitives/structuralStyles.d.ts +16 -0
  260. package/dist/primitives/tokens.d.ts +18 -0
  261. package/dist/primitives/types.d.ts +85 -0
  262. package/dist/state/activeEmojiStore.d.ts +7 -0
  263. package/dist/state/navigationRegistry.d.ts +61 -0
  264. package/dist/state/regionTraversal.d.ts +20 -0
  265. package/dist/typeRefinements/typeRefinements.d.ts +6 -6
  266. package/dist/types/exposedTypes.d.ts +16 -6
  267. package/dist/types/exposedTypes.js +5 -0
  268. package/dist/virtualization/virtualizationHelpers.d.ts +20 -18
  269. package/llms-full.txt +3680 -0
  270. package/llms.txt +66 -476
  271. package/package.json +204 -66
  272. package/src/DomUtils/categoryNameFromDom.ts +1 -1
  273. package/src/DomUtils/classNames.ts +1 -0
  274. package/src/DomUtils/elementPositionInRow.ts +26 -4
  275. package/src/DomUtils/eventBelongsToPicker.ts +17 -0
  276. package/src/DomUtils/getActiveElement.ts +7 -1
  277. package/src/DomUtils/isJsdom.ts +12 -0
  278. package/src/DomUtils/isRtl.ts +5 -0
  279. package/src/DomUtils/keyboardNavigation.ts +105 -149
  280. package/src/DomUtils/scrollTo.ts +18 -16
  281. package/src/DomUtils/selectors.ts +20 -111
  282. package/src/EmojiPickerReact.tsx +155 -48
  283. package/src/Stylesheet/DedupedStyle.tsx +80 -0
  284. package/src/Stylesheet/stylesheet.tsx +128 -59
  285. package/src/components/Layout/Flex.tsx +12 -11
  286. package/src/components/Reactions/BtnPlus.tsx +61 -51
  287. package/src/components/Reactions/Reactions.tsx +113 -55
  288. package/src/components/atoms/Button.tsx +18 -15
  289. package/src/components/body/EmojiCategory.tsx +77 -40
  290. package/src/components/body/EmojiList.tsx +55 -23
  291. package/src/components/body/EmojiVariationPicker.tsx +113 -66
  292. package/src/components/body/MeasureEmoji.tsx +9 -2
  293. package/src/components/body/listComponents.tsx +79 -0
  294. package/src/components/context/ElementRefContext.tsx +46 -35
  295. package/src/components/context/PickerConfigContext.tsx +55 -40
  296. package/src/components/context/PickerContext.tsx +567 -92
  297. package/src/components/context/PickerDataContext.tsx +110 -99
  298. package/src/components/emoji/BaseEmojiProps.ts +5 -4
  299. package/src/components/emoji/ClickableEmojiButton.tsx +148 -63
  300. package/src/components/emoji/Emoji.tsx +24 -2
  301. package/src/components/emoji/EmojiImg.tsx +13 -12
  302. package/src/components/emoji/ExportedEmoji.tsx +5 -4
  303. package/src/components/emoji/NativeEmoji.tsx +19 -16
  304. package/src/components/emoji/ViewOnlyEmoji.tsx +55 -30
  305. package/src/components/emoji/emojiStyles.ts +12 -11
  306. package/src/components/footer/Preview.tsx +86 -91
  307. package/src/components/header/Header.tsx +21 -6
  308. package/src/components/header/Search/BtnClearSearch.tsx +69 -55
  309. package/src/components/header/Search/IcnSearch.tsx +21 -21
  310. package/src/components/header/Search/Search.tsx +77 -137
  311. package/src/components/header/SkinTonePicker/BtnSkinToneVariation.tsx +99 -62
  312. package/src/components/header/SkinTonePicker/SkinTonePicker.tsx +84 -46
  313. package/src/components/icons/svgIcons.ts +17 -0
  314. package/src/components/main/PickerMainBehaviors.tsx +163 -0
  315. package/src/components/main/defaultAppearance.tsx +188 -0
  316. package/src/components/main/labelHeight.ts +5 -0
  317. package/src/components/navigation/CategoryButton.tsx +83 -57
  318. package/src/components/navigation/CategoryNavIcon.tsx +73 -194
  319. package/src/components/navigation/CategoryNavigation.tsx +118 -46
  320. package/src/config/categoryConfig.ts +7 -3
  321. package/src/config/cdnUrls.ts +11 -21
  322. package/src/config/compareConfig.ts +128 -30
  323. package/src/config/config.ts +228 -24
  324. package/src/config/defaultPickerConfiguration.ts +7 -0
  325. package/src/config/mutableConfig.ts +44 -20
  326. package/src/config/useConfig.ts +130 -37
  327. package/src/data/defaultEmojiData.ts +49 -0
  328. package/src/data/registerDefaultEmojiData.ts +8 -0
  329. package/src/data-core/pickerData.ts +173 -0
  330. package/src/data-core/prepare.ts +178 -0
  331. package/src/data-core/search.ts +122 -0
  332. package/src/data-core/types.ts +36 -0
  333. package/src/data.ts +10 -0
  334. package/src/dataUtils/DataTypes.ts +4 -0
  335. package/src/dataUtils/emojiSelectors.ts +58 -63
  336. package/src/dataUtils/emojiUtils.ts +17 -74
  337. package/src/dataUtils/nativeEmojiSequenceSupport.ts +116 -0
  338. package/src/dataUtils/nativeEmojiSupport.ts +160 -0
  339. package/src/dataUtils/suggested.ts +8 -4
  340. package/src/dataUtils/suggestedEmojis.ts +92 -0
  341. package/src/env.d.ts +4 -0
  342. package/src/hooks/preloadEmoji.ts +3 -3
  343. package/src/hooks/useActiveCategoryScrollDetection.ts +10 -1
  344. package/src/hooks/useCategoryHeight.ts +82 -8
  345. package/src/hooks/useDataIdentityStabilityWarning.ts +86 -0
  346. package/src/hooks/useDebouncedState.ts +13 -1
  347. package/src/hooks/useDisallowedEmojis.ts +26 -42
  348. package/src/hooks/useEmojiPreviewEvents.ts +43 -10
  349. package/src/hooks/useEmojiVirtualization.tsx +38 -4
  350. package/src/hooks/useFilter.ts +85 -127
  351. package/src/hooks/useFocus.ts +14 -24
  352. package/src/hooks/useIsEmojiHidden.ts +10 -6
  353. package/src/hooks/useKeyboardNavigation.ts +322 -429
  354. package/src/hooks/useMouseDownHandlers.ts +91 -16
  355. package/src/hooks/useOnFocus.ts +12 -3
  356. package/src/hooks/useOnScroll.ts +18 -2
  357. package/src/hooks/useReactionsFocus.ts +118 -0
  358. package/src/hooks/useRegisterRegion.ts +92 -0
  359. package/src/hooks/useResolvedEmojiData.ts +200 -0
  360. package/src/hooks/useScrollCategoryIntoView.ts +74 -12
  361. package/src/hooks/useSearchController.ts +369 -0
  362. package/src/hooks/useSearchResults.ts +51 -0
  363. package/src/hooks/useSetVariationPicker.ts +4 -1
  364. package/src/index.tsx +67 -5
  365. package/src/primitives/CategoryNav.tsx +40 -0
  366. package/src/primitives/Empty.tsx +68 -0
  367. package/src/primitives/List.tsx +61 -0
  368. package/src/primitives/LoadError.tsx +38 -0
  369. package/src/primitives/Loading.tsx +60 -0
  370. package/src/primitives/Panel.tsx +47 -0
  371. package/src/primitives/Preview.tsx +38 -0
  372. package/src/primitives/Reactions.tsx +2 -0
  373. package/src/primitives/Root.tsx +398 -0
  374. package/src/primitives/Search.tsx +41 -0
  375. package/src/primitives/SearchInput.tsx +110 -0
  376. package/src/primitives/SkinTone.tsx +76 -0
  377. package/src/primitives/Viewport.tsx +204 -0
  378. package/src/primitives/appearance.tsx +7 -0
  379. package/src/primitives/components.tsx +30 -0
  380. package/src/primitives/hooks.ts +165 -0
  381. package/src/primitives/index.ts +94 -0
  382. package/src/primitives/nativeProps.ts +90 -0
  383. package/src/primitives/scope.tsx +109 -0
  384. package/src/primitives/structuralStyles.tsx +133 -0
  385. package/src/primitives/tokens.ts +149 -0
  386. package/src/primitives/types.ts +192 -0
  387. package/src/state/activeEmojiStore.ts +39 -0
  388. package/src/state/navigationRegistry.ts +209 -0
  389. package/src/state/regionTraversal.ts +116 -0
  390. package/src/svg.d.ts +2 -2
  391. package/src/types/exposedTypes.ts +28 -1
  392. package/src/virtualization/virtualizationHelpers.ts +20 -6
  393. package/dist/components/body/Body.d.ts +0 -2
  394. package/dist/components/main/PickerMain.d.ts +0 -7
  395. package/dist/dataUtils/alphaNumericEmojiIndex.d.ts +0 -5
  396. package/dist/emoji-picker-react.cjs.development.js +0 -31413
  397. package/dist/emoji-picker-react.cjs.development.js.map +0 -1
  398. package/dist/emoji-picker-react.cjs.production.min.js +0 -2
  399. package/dist/emoji-picker-react.cjs.production.min.js.map +0 -1
  400. package/dist/emoji-picker-react.esm.js +0 -31411
  401. package/dist/emoji-picker-react.esm.js.map +0 -1
  402. package/dist/hooks/useHideEmojisByUniocode.d.ts +0 -1
  403. package/src/components/body/Body.tsx +0 -43
  404. package/src/components/main/PickerMain.tsx +0 -249
  405. package/src/dataUtils/alphaNumericEmojiIndex.ts +0 -21
@@ -2,6 +2,7 @@ import * as React from 'react';
2
2
  import { useEffect } from 'react';
3
3
 
4
4
  import { detectEmojyPartiallyBelowFold } from '../DomUtils/detectEmojyPartiallyBelowFold';
5
+ import { eventBelongsToPicker } from '../DomUtils/eventBelongsToPicker';
5
6
  import { focusElement } from '../DomUtils/focusElement';
6
7
  import {
7
8
  allUnifiedFromEmojiElement,
@@ -11,16 +12,19 @@ import {
11
12
  useBodyRef,
12
13
  usePickerMainRef,
13
14
  } from '../components/context/ElementRefContext';
14
- import { PreviewEmoji } from '../components/footer/Preview';
15
+ import { ActiveEmojiState } from '../components/context/PickerContext';
15
16
 
16
17
  import {
17
18
  useAllowMouseMove,
18
19
  useIsMouseDisallowed,
19
20
  } from './useDisallowMouseMove';
20
21
 
22
+ // Tracks the hovered/focused emoji. `focusOnHover` additionally moves
23
+ // focus to the hovered emoji so arrow keys continue from it (enabled
24
+ // together with the preview, as in v4).
21
25
  export function useEmojiPreviewEvents(
22
- allow: boolean,
23
- setPreviewEmoji: React.Dispatch<React.SetStateAction<PreviewEmoji>>,
26
+ focusOnHover: boolean,
27
+ setPreviewEmoji: React.Dispatch<React.SetStateAction<ActiveEmojiState>>,
24
28
  ) {
25
29
  const BodyRef = useBodyRef();
26
30
  const PickerMainRef = usePickerMainRef();
@@ -28,9 +32,6 @@ export function useEmojiPreviewEvents(
28
32
  const allowMouseMove = useAllowMouseMove();
29
33
 
30
34
  useEffect(() => {
31
- if (!allow) {
32
- return;
33
- }
34
35
  const bodyRef = BodyRef.current;
35
36
 
36
37
  bodyRef?.addEventListener('keydown', onEscape, {
@@ -39,6 +40,14 @@ export function useEmojiPreviewEvents(
39
40
 
40
41
  bodyRef?.addEventListener('mouseover', onMouseOver, true);
41
42
 
43
+ // Mouseover precedes mousemove on a newly hovered cell. Resume hover
44
+ // during capture, before the body's movement listener clears the
45
+ // keyboard guard, so the first real pointer movement is not lost.
46
+ bodyRef?.addEventListener('mousemove', onMouseMove, {
47
+ capture: true,
48
+ passive: true,
49
+ });
50
+
42
51
  bodyRef?.addEventListener('focus', onEnter, true);
43
52
 
44
53
  bodyRef?.addEventListener('mouseout', onLeave, {
@@ -47,6 +56,7 @@ export function useEmojiPreviewEvents(
47
56
  bodyRef?.addEventListener('blur', onLeave, true);
48
57
 
49
58
  function onEnter(e: FocusEvent) {
59
+ if (!eventBelongsToPicker(e, bodyRef)) return;
50
60
  const button = buttonFromTarget(e.target as HTMLElement);
51
61
 
52
62
  if (!button) {
@@ -65,6 +75,7 @@ export function useEmojiPreviewEvents(
65
75
  });
66
76
  }
67
77
  function onLeave(e?: FocusEvent | MouseEvent) {
78
+ if (e && !eventBelongsToPicker(e, bodyRef)) return;
68
79
  if (e) {
69
80
  const relatedTarget = e.relatedTarget as HTMLElement;
70
81
 
@@ -76,13 +87,22 @@ export function useEmojiPreviewEvents(
76
87
  setPreviewEmoji(null);
77
88
  }
78
89
  function onEscape(e: KeyboardEvent) {
90
+ if (!eventBelongsToPicker(e, bodyRef)) return;
79
91
  if (e.key === 'Escape') {
80
92
  setPreviewEmoji(null);
81
93
  }
82
94
  }
83
95
 
96
+ function onMouseMove(e: MouseEvent) {
97
+ if (!eventBelongsToPicker(e, bodyRef) || !isMouseDisallowed()) {
98
+ return;
99
+ }
100
+ allowMouseMove();
101
+ onMouseOver(e);
102
+ }
103
+
84
104
  function onMouseOver(e: MouseEvent) {
85
- if (isMouseDisallowed()) {
105
+ if (!eventBelongsToPicker(e, bodyRef) || isMouseDisallowed()) {
86
106
  return;
87
107
  }
88
108
 
@@ -112,6 +132,7 @@ export function useEmojiPreviewEvents(
112
132
  // the browser scroll them into view, yanking the scroll position
113
133
  // while the user is browsing (see also PR #509).
114
134
  if (
135
+ focusOnHover &&
115
136
  !isExternalElementFocused() &&
116
137
  !isPartiallyBelowFold(button, bodyRef)
117
138
  ) {
@@ -130,7 +151,11 @@ export function useEmojiPreviewEvents(
130
151
  function isExternalElementFocused(): boolean {
131
152
  const active = document.activeElement as HTMLElement | null;
132
153
 
133
- if (!active || active === document.body || active === document.documentElement) {
154
+ if (
155
+ !active ||
156
+ active === document.body ||
157
+ active === document.documentElement
158
+ ) {
134
159
  return false;
135
160
  }
136
161
 
@@ -138,11 +163,19 @@ export function useEmojiPreviewEvents(
138
163
  }
139
164
 
140
165
  return () => {
141
- bodyRef?.removeEventListener('mouseover', onMouseOver);
166
+ bodyRef?.removeEventListener('mouseover', onMouseOver, true);
167
+ bodyRef?.removeEventListener('mousemove', onMouseMove, true);
142
168
  bodyRef?.removeEventListener('mouseout', onLeave);
143
169
  bodyRef?.removeEventListener('focus', onEnter, true);
144
170
  bodyRef?.removeEventListener('blur', onLeave, true);
145
171
  bodyRef?.removeEventListener('keydown', onEscape);
146
172
  };
147
- }, [BodyRef, PickerMainRef, allow, setPreviewEmoji, isMouseDisallowed, allowMouseMove]);
173
+ }, [
174
+ BodyRef,
175
+ PickerMainRef,
176
+ focusOnHover,
177
+ setPreviewEmoji,
178
+ isMouseDisallowed,
179
+ allowMouseMove,
180
+ ]);
148
181
  }
@@ -2,7 +2,10 @@ import { ReactNode, useEffect } from 'react';
2
2
  import * as React from 'react';
3
3
 
4
4
  import { useBodyRef } from '../components/context/ElementRefContext';
5
- import { useActiveSkinToneState } from '../components/context/PickerContext';
5
+ import {
6
+ useActiveSkinToneState,
7
+ useRequestNativeProbe,
8
+ } from '../components/context/PickerContext';
6
9
  import { ClickableEmoji } from '../components/emoji/Emoji';
7
10
  import {
8
11
  useEmojiStyleConfig,
@@ -28,12 +31,15 @@ export function useEmojiVirtualization({
28
31
  onHeightReady,
29
32
  scrollTop,
30
33
  isCategoryVisible,
34
+ positioned,
31
35
  }: {
32
36
  categoryEmojis: DataEmojis;
33
37
  topOffset: number;
34
38
  onHeightReady: (height: number) => void;
35
39
  scrollTop: number;
36
40
  isCategoryVisible: boolean;
41
+ /** Whether topOffset is final; unmeasured sections above shift it. */
42
+ positioned: boolean;
37
43
  }) {
38
44
  const isEmojiHidden = useIsEmojiHidden();
39
45
  const lazyLoadEmojis = useLazyLoadEmojisConfig();
@@ -43,14 +49,30 @@ export function useEmojiVirtualization({
43
49
  const getEmojiUrl = useGetEmojiUrlConfig();
44
50
  const showVariations = !useSkinTonesDisabledConfig();
45
51
  const BodyRef = useBodyRef();
52
+ const requestNativeProbe = useRequestNativeProbe();
53
+ // Native glyphs checked exactly: the cells this pass renders (visible
54
+ // rows plus virtualization's margin rows), nothing else.
55
+ const probe: string[] = [];
46
56
 
47
57
  let virtualizedCounter = 0;
48
58
 
59
+ // A section shows each rendered emoji once. Recents can hold the same
60
+ // emoji under two tones (e.g. neutral and medium); the active tone
61
+ // renders both as one identity, which must not be listed or keyed twice.
62
+ const rendered = new Set<string>();
49
63
  const emojisToPush = categoryEmojis.filter((emoji) => {
50
64
  const isDisallowed = isEmojiDisallowed(emoji);
51
- const { failedToLoad, filteredOut, hidden } = isEmojiHidden(emoji);
65
+ const { hidden } = isEmojiHidden(emoji);
52
66
 
53
- return !failedToLoad && !filteredOut && !hidden && !isDisallowed;
67
+ if (hidden || isDisallowed) {
68
+ return false;
69
+ }
70
+ const unified = emojiUnified(emoji, activeSkinTone);
71
+ if (rendered.has(unified)) {
72
+ return false;
73
+ }
74
+ rendered.add(unified);
75
+ return true;
54
76
  });
55
77
 
56
78
  const dimensions = useCategoryHeight(emojisToPush.length);
@@ -61,7 +83,7 @@ export function useEmojiVirtualization({
61
83
  }
62
84
  }, [dimensions, onHeightReady, emojisToPush.length]);
63
85
 
64
- const isVirtualized = (style: { top: number; left: number } | undefined) =>
86
+ const isVirtualized = (style: { top: number } | undefined) =>
65
87
  dimensions &&
66
88
  BodyRef.current &&
67
89
  shouldVirtualize({
@@ -96,6 +118,10 @@ export function useEmojiVirtualization({
96
118
  return accumulator;
97
119
  }
98
120
 
121
+ // Wait for layout: an unmeasured pass renders a whole category, and
122
+ // while sections above are unmeasured this one renders off-screen
123
+ // cells as if it were at the top.
124
+ if (dimensions && positioned) probe.push(unified);
99
125
  accumulator.push(
100
126
  <ClickableEmoji
101
127
  showVariations={showVariations}
@@ -109,14 +135,22 @@ export function useEmojiVirtualization({
109
135
  ...style,
110
136
  position: 'absolute',
111
137
  }}
138
+ index={index}
139
+ // Grid semantics (issue #508): each emoji is a cell of its
140
+ // category row; native button activation is kept.
141
+ role="gridcell"
112
142
  />,
113
143
  );
114
144
  return accumulator;
115
145
  }, [] as ReactNode[]);
116
146
 
147
+ // Every commit: requests for already-checked glyphs are free.
148
+ useEffect(() => requestNativeProbe(probe));
149
+
117
150
  return {
118
151
  virtualizedCounter,
119
152
  emojis,
120
153
  dimensions,
154
+ emojiCount: emojisToPush.length,
121
155
  };
122
156
  }
@@ -1,143 +1,116 @@
1
+ import * as React from 'react';
2
+
1
3
  import { scrollTo } from '../DomUtils/scrollTo';
2
- import {
3
- usePickerMainRef,
4
- useSearchInputRef,
5
- } from '../components/context/ElementRefContext';
4
+ import { usePickerMainRef } from '../components/context/ElementRefContext';
6
5
  import {
7
6
  FilterState,
7
+ useFilterQueryOrderRef,
8
8
  useFilterRef,
9
+ useNavigationRegistry,
9
10
  useSearchTermState,
10
11
  } from '../components/context/PickerContext';
11
- import { useSearchResultsConfig } from '../config/useConfig';
12
+ import { usePickerDataContext } from '../components/context/PickerDataContext';
13
+ import { normalizeQuery } from '../data-core/prepare';
12
14
  import { DataEmoji } from '../dataUtils/DataTypes';
13
15
  import { emojiNames } from '../dataUtils/emojiUtils';
14
16
 
15
- import { useFocusSearchInput } from './useFocus';
17
+ /**
18
+ * Bound on cached per-query filter dicts per Root. Dicts are cheaply
19
+ * rebuilt from the shared memoized core, so a long typing session keeps
20
+ * only a recent window instead of growing without bound.
21
+ */
22
+ const MAX_FILTER_QUERIES = 50;
16
23
 
17
24
  function useSetFilterRef() {
18
25
  const filterRef = useFilterRef();
19
-
20
- return function setFilter(
21
- setter: FilterState | ((current: FilterState) => FilterState),
22
- ): void {
23
- if (typeof setter === 'function') {
24
- return setFilter(setter(filterRef.current));
26
+ const orderRef = useFilterQueryOrderRef();
27
+
28
+ return function setFilter(nextValue: string, dict: FilterDict): void {
29
+ // Copy-on-write into a fresh object: the ref never aliases shared
30
+ // snapshot state, and spread defines own properties (no prototype
31
+ // mutation for adversarial queries such as "__proto__").
32
+ const next: FilterState = { ...filterRef.current, [nextValue]: dict };
33
+ const order = orderRef.current;
34
+ if (!order.includes(nextValue)) {
35
+ order.push(nextValue);
25
36
  }
26
-
27
- filterRef.current = setter;
28
- };
29
- }
30
-
31
- export function useClearSearch() {
32
- const applySearch = useApplySearch();
33
- const SearchInputRef = useSearchInputRef();
34
- const focusSearchInput = useFocusSearchInput();
35
-
36
- return function clearSearch() {
37
- if (SearchInputRef.current) {
38
- SearchInputRef.current.value = '';
39
- }
40
-
41
- applySearch('');
42
- focusSearchInput();
43
- };
44
- }
45
-
46
- export function useAppendSearch() {
47
- const SearchInputRef = useSearchInputRef();
48
- const applySearch = useApplySearch();
49
-
50
- return function appendSearch(str: string) {
51
- if (SearchInputRef.current) {
52
- SearchInputRef.current.value = `${SearchInputRef.current.value}${str}`;
53
- applySearch(getNormalizedSearchTerm(SearchInputRef.current.value));
54
- } else {
55
- applySearch(getNormalizedSearchTerm(str));
37
+ while (order.length > MAX_FILTER_QUERIES) {
38
+ const oldest = order.shift() as string;
39
+ delete next[oldest];
56
40
  }
41
+ filterRef.current = next;
57
42
  };
58
43
  }
59
44
 
60
45
  export function useFilter() {
61
- const SearchInputRef = useSearchInputRef();
62
46
  const filterRef = useFilterRef();
63
47
  const setFilterRef = useSetFilterRef();
64
48
  const applySearch = useApplySearch();
65
-
66
- const [searchTerm] = useSearchTermState();
67
- const statusSearchResults = getStatusSearchResults(
68
- filterRef.current,
69
- searchTerm,
70
- );
71
-
72
- return {
73
- onChange,
74
- searchTerm,
75
- SearchInputRef,
76
- statusSearchResults,
77
- };
49
+ const { queryFilterDict } = usePickerDataContext();
50
+ // Managed SearchInput owns announcements; this hook only applies filters.
51
+ return { onChange };
78
52
 
79
53
  function onChange(inputValue: string) {
80
54
  const filter = filterRef.current;
81
55
 
82
- const nextValue = inputValue.toLowerCase();
56
+ // Normalized derived query: the visible/callback value
57
+ // stays raw, filtering folds case and trims surrounding whitespace.
58
+ // Matching itself runs in the single shared prepared core.
59
+ const nextValue = normalizeQuery(inputValue);
83
60
 
84
- if (filter?.[nextValue] || nextValue.length <= 1) {
61
+ if (!nextValue) {
85
62
  return applySearch(nextValue);
86
63
  }
87
-
88
- const longestMatch = findLongestMatch(nextValue, filter);
89
-
90
- if (!longestMatch) {
91
- // Can we even get here?
92
- // If so, we need to search among all emojis
64
+ if (hasOwnQuery(filter, nextValue)) {
93
65
  return applySearch(nextValue);
94
66
  }
95
67
 
96
- setFilterRef((current) =>
97
- Object.assign(current, {
98
- [nextValue]: filterEmojiObjectByKeyword(longestMatch, nextValue),
99
- }),
100
- );
68
+ setFilterRef(nextValue, queryFilterDict(nextValue));
101
69
  applySearch(nextValue);
102
70
  }
103
71
  }
104
72
 
73
+ function hasOwnQuery(filter: FilterState, query: string): boolean {
74
+ return Object.prototype.hasOwnProperty.call(filter, query);
75
+ }
76
+
105
77
  function useApplySearch() {
106
78
  const [, setSearchTerm] = useSearchTermState();
107
79
  const PickerMainRef = usePickerMainRef();
80
+ const registry = useNavigationRegistry();
81
+ // A search frame pending at unmount must not schedule post-unmount
82
+ // debounce work (the debounced state also clears its own timer, so
83
+ // both stages of the pipeline die with the Root).
84
+ const mountedRef = React.useRef(true);
85
+ React.useEffect(() => {
86
+ mountedRef.current = true;
87
+ return () => {
88
+ mountedRef.current = false;
89
+ };
90
+ }, []);
108
91
 
109
92
  return function applySearch(searchTerm: string) {
93
+ // A new accepted filter obsoletes pending grid materialize/focus work.
94
+ registry.invalidate();
95
+ // The scroll back to the top lands after the debounce; navigation in
96
+ // the meantime (e.g. a category tab clicked right after clearing the
97
+ // search) supersedes it instead of being yanked back to the top.
98
+ const token = registry.currentGeneration();
110
99
  requestAnimationFrame(() => {
100
+ if (!mountedRef.current) {
101
+ return;
102
+ }
111
103
  setSearchTerm(searchTerm ? searchTerm?.toLowerCase() : searchTerm).then(
112
104
  () => {
113
- scrollTo(PickerMainRef.current, 0);
105
+ if (registry.isCurrent(token)) {
106
+ scrollTo(PickerMainRef.current, 0, () => registry.isCurrent(token));
107
+ }
114
108
  },
115
109
  );
116
110
  });
117
111
  };
118
112
  }
119
113
 
120
- export function filterEmojiObjectByKeyword(
121
- emojis: FilterDict,
122
- keyword: string,
123
- ): FilterDict {
124
- const filtered: FilterDict = {};
125
-
126
- for (const unified in emojis) {
127
- const emoji = emojis[unified];
128
-
129
- if (hasMatch(emoji, keyword)) {
130
- filtered[unified] = emoji;
131
- }
132
- }
133
-
134
- return filtered;
135
- }
136
-
137
- function hasMatch(emoji: DataEmoji, keyword: string): boolean {
138
- return emojiNames(emoji).some((name) => name.includes(keyword));
139
- }
140
-
141
114
  export function useIsEmojiFiltered(): (unified: string) => boolean {
142
115
  const { current: filter } = useFilterRef();
143
116
  const [searchTerm] = useSearchTermState();
@@ -159,45 +132,30 @@ export function isEmojiFilteredBySearchTerm(
159
132
 
160
133
  export type FilterDict = Record<string, DataEmoji>;
161
134
 
135
+ export function filterEmojiObjectByKeyword(
136
+ emojis: FilterDict,
137
+ keyword: string,
138
+ ): FilterDict {
139
+ const filtered: FilterDict = {};
140
+ Object.keys(emojis).forEach((unified) => {
141
+ const emoji = emojis[unified];
142
+ if (
143
+ emojiNames(emoji).some((name) => name.toLowerCase().includes(keyword))
144
+ ) {
145
+ filtered[unified] = emoji;
146
+ }
147
+ });
148
+ return filtered;
149
+ }
150
+
162
151
  export function findLongestMatch(
163
152
  keyword: string,
164
153
  dict: Record<string, FilterDict> | null,
165
154
  ): FilterDict | null {
166
- if (!dict) {
167
- return null;
168
- }
169
-
170
- if (dict[keyword]) {
171
- return dict[keyword];
172
- }
173
-
174
- const longestMatchingKey = Object.keys(dict)
175
- .sort((a, b) => b.length - a.length)
155
+ if (!dict) return null;
156
+ if (dict[keyword]) return dict[keyword];
157
+ const longest = Object.keys(dict)
158
+ .sort((left, right) => right.length - left.length)
176
159
  .find((key) => keyword.includes(key));
177
-
178
- if (longestMatchingKey) {
179
- return dict[longestMatchingKey];
180
- }
181
-
182
- return null;
183
- }
184
-
185
- export function getNormalizedSearchTerm(str: string): string {
186
- if (!str || typeof str !== 'string') {
187
- return '';
188
- }
189
-
190
- return str.trim().toLowerCase();
191
- }
192
-
193
- function getStatusSearchResults(
194
- filterState: FilterState,
195
- searchTerm: string,
196
- ): string {
197
- if (!filterState?.[searchTerm]) return '';
198
-
199
- const searchResultsCount =
200
- Object.entries(filterState?.[searchTerm])?.length || 0;
201
- // eslint-disable-next-line react-hooks/rules-of-hooks
202
- return useSearchResultsConfig(searchResultsCount);
160
+ return longest ? dict[longest] : null;
203
161
  }
@@ -1,40 +1,30 @@
1
1
  import { useCallback } from 'react';
2
2
 
3
- import { focusElement, focusFirstElementChild } from '../DomUtils/focusElement';
3
+ import { focusElement } from '../DomUtils/focusElement';
4
+ import { NullableElement } from '../DomUtils/selectors';
4
5
  import {
6
+ ElementRef,
5
7
  useCategoryNavigationRef,
6
8
  useSearchInputRef,
7
9
  useSkinTonePickerRef,
8
10
  } from '../components/context/ElementRefContext';
9
11
 
10
- export function useFocusSearchInput() {
11
- const SearchInputRef = useSearchInputRef();
12
-
12
+ function useFocusRef(ref: ElementRef, child = false) {
13
13
  return useCallback(() => {
14
- focusElement(SearchInputRef.current);
15
- }, [SearchInputRef]);
14
+ focusElement(
15
+ (child ? ref.current?.firstElementChild : ref.current) as NullableElement,
16
+ );
17
+ }, [ref, child]);
16
18
  }
17
19
 
18
- export function useFocusSkinTonePicker() {
19
- const SkinTonePickerRef = useSkinTonePickerRef();
20
-
21
- return useCallback(() => {
22
- if (!SkinTonePickerRef.current) {
23
- return;
24
- }
20
+ export function useFocusSearchInput() {
21
+ return useFocusRef(useSearchInputRef());
22
+ }
25
23
 
26
- focusFirstElementChild(SkinTonePickerRef.current);
27
- }, [SkinTonePickerRef]);
24
+ export function useFocusSkinTonePicker() {
25
+ return useFocusRef(useSkinTonePickerRef(), true);
28
26
  }
29
27
 
30
28
  export function useFocusCategoryNavigation() {
31
- const CategoryNavigationRef = useCategoryNavigationRef();
32
-
33
- return useCallback(() => {
34
- if (!CategoryNavigationRef.current) {
35
- return;
36
- }
37
-
38
- focusFirstElementChild(CategoryNavigationRef.current);
39
- }, [CategoryNavigationRef]);
29
+ return useFocusRef(useCategoryNavigationRef(), true);
40
30
  }
@@ -1,18 +1,22 @@
1
- import { useEmojisThatFailedToLoadState } from '../components/context/PickerContext';
1
+ import {
2
+ useActiveSkinToneState,
3
+ useEmojisThatFailedToLoadState,
4
+ } from '../components/context/PickerContext';
2
5
  import { DataEmoji } from '../dataUtils/DataTypes';
3
- import { emojiUnified } from '../dataUtils/emojiUtils';
6
+ import { emojiUnified, emojiCanonicalUnified } from '../dataUtils/emojiUtils';
4
7
 
5
8
  import { useIsEmojiFiltered } from './useFilter';
6
9
 
7
10
  export function useIsEmojiHidden(): (emoji: DataEmoji) => IsHiddenReturn {
11
+ const [activeSkinTone] = useActiveSkinToneState();
8
12
  const [emojisThatFailedToLoad] = useEmojisThatFailedToLoadState();
9
13
  const isEmojiFiltered = useIsEmojiFiltered();
10
14
 
11
15
  return (emoji: DataEmoji): IsHiddenReturn => {
12
- const unified = emojiUnified(emoji);
13
-
14
- const failedToLoad = emojisThatFailedToLoad.has(unified);
15
- const filteredOut = isEmojiFiltered(unified);
16
+ const failedToLoad = emojisThatFailedToLoad.has(
17
+ emojiUnified(emoji, activeSkinTone),
18
+ );
19
+ const filteredOut = isEmojiFiltered(emojiCanonicalUnified(emoji));
16
20
 
17
21
  return {
18
22
  failedToLoad,