emoji-picker-react 4.22.3 → 5.0.1

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 (409) hide show
  1. package/README.md +331 -49
  2. package/dist/DomUtils/categoryNameFromDom.d.ts +1 -1
  3. package/dist/DomUtils/classNames.d.ts +23 -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 +15 -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/skinToneVariations.js +2 -2
  183. package/dist/data/skinToneVariations.ts +8 -7
  184. package/dist/data-core/pickerData.d.ts +12 -0
  185. package/dist/data-core/prepare.d.ts +23 -0
  186. package/dist/data-core/search.d.ts +13 -0
  187. package/dist/data-core/types.d.ts +23 -0
  188. package/dist/data.d.mts +72 -0
  189. package/dist/data.d.ts +72 -0
  190. package/dist/dataUtils/DataTypes.d.ts +23 -19
  191. package/dist/dataUtils/emojiSelectors.d.ts +15 -16
  192. package/dist/dataUtils/emojiUtils.d.ts +1 -12
  193. package/dist/dataUtils/nativeEmojiSequenceSupport.d.ts +10 -0
  194. package/dist/dataUtils/nativeEmojiSupport.d.ts +40 -0
  195. package/dist/dataUtils/parseNativeEmoji.d.ts +1 -1
  196. package/dist/dataUtils/suggested.d.ts +11 -11
  197. package/dist/dataUtils/suggestedEmojis.d.ts +7 -0
  198. package/dist/esm/chunks/chunk-7ZGTUZFB.mjs +8025 -0
  199. package/dist/esm/chunks/chunk-ACXAQEKG.mjs +149 -0
  200. package/dist/esm/chunks/chunk-F2KS7DCA.mjs +6735 -0
  201. package/dist/esm/chunks/chunk-L5W4PFRJ.mjs +14 -0
  202. package/dist/esm/chunks/chunk-LO4TPPLH.mjs +33 -0
  203. package/dist/esm/chunks/emojis-6L3GRBAP.mjs +7 -0
  204. package/dist/esm/data.mjs +11 -0
  205. package/dist/esm/index.mjs +279 -0
  206. package/dist/esm/primitives/index.mjs +8 -0
  207. package/dist/hooks/preloadEmoji.d.ts +5 -9
  208. package/dist/hooks/useActiveCategoryScrollDetection.d.ts +11 -11
  209. package/dist/hooks/useCategoryHeight.d.ts +7 -5
  210. package/dist/hooks/useCloseAllOpenToggles.d.ts +2 -2
  211. package/dist/hooks/useDataIdentityStabilityWarning.d.ts +13 -0
  212. package/dist/hooks/useDebouncedState.d.ts +1 -1
  213. package/dist/hooks/useDisallowMouseMove.d.ts +4 -4
  214. package/dist/hooks/useDisallowedEmojis.d.ts +2 -3
  215. package/dist/hooks/useEmojiPreviewEvents.d.ts +3 -3
  216. package/dist/hooks/useEmojiVirtualization.d.ts +22 -17
  217. package/dist/hooks/useFilter.d.ts +10 -16
  218. package/dist/hooks/useFocus.d.ts +3 -3
  219. package/dist/hooks/useInitialLoad.d.ts +2 -2
  220. package/dist/hooks/useIsEmojiHidden.d.ts +8 -8
  221. package/dist/hooks/useIsMounted.d.ts +1 -1
  222. package/dist/hooks/useIsSearchMode.d.ts +1 -1
  223. package/dist/hooks/useIsomorphicLayoutEffect.d.ts +2 -2
  224. package/dist/hooks/useKeyboardNavigation.d.ts +3 -1
  225. package/dist/hooks/useMouseDownHandlers.d.ts +8 -4
  226. package/dist/hooks/useOnFocus.d.ts +1 -1
  227. package/dist/hooks/useOnScroll.d.ts +2 -2
  228. package/dist/hooks/useReactionsFocus.d.ts +1 -0
  229. package/dist/hooks/useRegisterRegion.d.ts +24 -0
  230. package/dist/hooks/useResolvedEmojiData.d.ts +37 -0
  231. package/dist/hooks/useScrollCategoryIntoView.d.ts +1 -1
  232. package/dist/hooks/useSearchController.d.ts +17 -0
  233. package/dist/hooks/useSearchResults.d.ts +8 -0
  234. package/dist/hooks/useSetVariationPicker.d.ts +2 -2
  235. package/dist/hooks/useShouldHideCustomEmojis.d.ts +1 -1
  236. package/dist/hooks/useShouldShowSkinTonePicker.d.ts +4 -4
  237. package/dist/index.d.mts +376 -0
  238. package/dist/index.d.ts +376 -9
  239. package/dist/index.js +2 -8
  240. package/dist/primitives/CategoryNav.d.ts +4 -0
  241. package/dist/primitives/Empty.d.ts +6 -0
  242. package/dist/primitives/List.d.ts +4 -0
  243. package/dist/primitives/LoadError.d.ts +8 -0
  244. package/dist/primitives/Loading.d.ts +4 -0
  245. package/dist/primitives/Panel.d.ts +3 -0
  246. package/dist/primitives/Preview.d.ts +3 -0
  247. package/dist/primitives/Reactions.d.ts +2 -0
  248. package/dist/primitives/Root.d.ts +26 -0
  249. package/dist/primitives/Search.d.ts +5 -0
  250. package/dist/primitives/SearchInput.d.ts +3 -0
  251. package/dist/primitives/SkinTone.d.ts +4 -0
  252. package/dist/primitives/Viewport.d.ts +19 -0
  253. package/dist/primitives/appearance.d.ts +3 -0
  254. package/dist/primitives/components.d.ts +27 -0
  255. package/dist/primitives/hooks.d.ts +48 -0
  256. package/dist/primitives/index.d.mts +561 -0
  257. package/dist/primitives/index.d.ts +561 -0
  258. package/dist/primitives/index.js +2 -0
  259. package/dist/primitives/nativeProps.d.ts +15 -0
  260. package/dist/primitives/scope.d.ts +13 -0
  261. package/dist/primitives/structuralStyles.d.ts +16 -0
  262. package/dist/primitives/tokens.d.ts +18 -0
  263. package/dist/primitives/types.d.ts +85 -0
  264. package/dist/state/activeEmojiStore.d.ts +7 -0
  265. package/dist/state/navigationRegistry.d.ts +61 -0
  266. package/dist/state/regionTraversal.d.ts +20 -0
  267. package/dist/typeRefinements/typeRefinements.d.ts +6 -6
  268. package/dist/types/exposedTypes.d.ts +16 -6
  269. package/dist/types/exposedTypes.js +5 -0
  270. package/dist/virtualization/virtualizationHelpers.d.ts +22 -18
  271. package/llms-full.txt +3683 -0
  272. package/llms.txt +66 -480
  273. package/package.json +180 -66
  274. package/src/DomUtils/categoryNameFromDom.ts +1 -1
  275. package/src/DomUtils/classNames.ts +1 -4
  276. package/src/DomUtils/elementPositionInRow.ts +28 -6
  277. package/src/DomUtils/eventBelongsToPicker.ts +17 -0
  278. package/src/DomUtils/getActiveElement.ts +7 -1
  279. package/src/DomUtils/isJsdom.ts +12 -0
  280. package/src/DomUtils/isRtl.ts +5 -0
  281. package/src/DomUtils/keyboardNavigation.ts +164 -144
  282. package/src/DomUtils/scrollTo.ts +20 -18
  283. package/src/DomUtils/selectors.ts +30 -127
  284. package/src/EmojiPickerReact.tsx +155 -48
  285. package/src/Stylesheet/DedupedStyle.tsx +80 -0
  286. package/src/Stylesheet/stylesheet.tsx +128 -59
  287. package/src/components/Layout/Flex.tsx +12 -11
  288. package/src/components/Reactions/BtnPlus.tsx +61 -51
  289. package/src/components/Reactions/Reactions.tsx +113 -55
  290. package/src/components/atoms/Button.tsx +18 -15
  291. package/src/components/body/EmojiCategory.tsx +77 -40
  292. package/src/components/body/EmojiList.tsx +107 -44
  293. package/src/components/body/EmojiVariationPicker.tsx +113 -66
  294. package/src/components/body/MeasureEmoji.tsx +9 -2
  295. package/src/components/body/listComponents.tsx +79 -0
  296. package/src/components/context/ElementRefContext.tsx +46 -35
  297. package/src/components/context/PickerConfigContext.tsx +55 -40
  298. package/src/components/context/PickerContext.tsx +567 -92
  299. package/src/components/context/PickerDataContext.tsx +114 -99
  300. package/src/components/emoji/BaseEmojiProps.ts +5 -4
  301. package/src/components/emoji/ClickableEmojiButton.tsx +148 -63
  302. package/src/components/emoji/Emoji.tsx +24 -2
  303. package/src/components/emoji/EmojiImg.tsx +13 -12
  304. package/src/components/emoji/ExportedEmoji.tsx +5 -4
  305. package/src/components/emoji/NativeEmoji.tsx +19 -16
  306. package/src/components/emoji/ViewOnlyEmoji.tsx +55 -30
  307. package/src/components/emoji/emojiStyles.ts +12 -11
  308. package/src/components/footer/Preview.tsx +86 -91
  309. package/src/components/header/Header.tsx +21 -6
  310. package/src/components/header/Search/BtnClearSearch.tsx +69 -55
  311. package/src/components/header/Search/IcnSearch.tsx +21 -21
  312. package/src/components/header/Search/Search.tsx +79 -137
  313. package/src/components/header/SkinTonePicker/BtnSkinToneVariation.tsx +99 -62
  314. package/src/components/header/SkinTonePicker/SkinTonePicker.tsx +84 -46
  315. package/src/components/icons/svgIcons.ts +17 -0
  316. package/src/components/main/PickerMainBehaviors.tsx +163 -0
  317. package/src/components/main/defaultAppearance.tsx +189 -0
  318. package/src/components/main/labelHeight.ts +5 -0
  319. package/src/components/navigation/CategoryButton.tsx +83 -57
  320. package/src/components/navigation/CategoryNavIcon.tsx +73 -194
  321. package/src/components/navigation/CategoryNavigation.tsx +118 -46
  322. package/src/config/categoryConfig.ts +7 -3
  323. package/src/config/cdnUrls.ts +11 -21
  324. package/src/config/compareConfig.ts +128 -30
  325. package/src/config/config.ts +228 -24
  326. package/src/config/defaultPickerConfiguration.ts +7 -0
  327. package/src/config/mutableConfig.ts +44 -20
  328. package/src/config/useConfig.ts +130 -37
  329. package/src/data/defaultEmojiData.ts +49 -0
  330. package/src/data/registerDefaultEmojiData.ts +8 -0
  331. package/src/data/skinToneVariations.ts +8 -7
  332. package/src/data-core/pickerData.ts +171 -0
  333. package/src/data-core/prepare.ts +163 -0
  334. package/src/data-core/search.ts +122 -0
  335. package/src/data-core/types.ts +36 -0
  336. package/src/data.ts +10 -0
  337. package/src/dataUtils/DataTypes.ts +4 -0
  338. package/src/dataUtils/emojiSelectors.ts +59 -63
  339. package/src/dataUtils/emojiUtils.ts +17 -74
  340. package/src/dataUtils/nativeEmojiSequenceSupport.ts +116 -0
  341. package/src/dataUtils/nativeEmojiSupport.ts +160 -0
  342. package/src/dataUtils/parseNativeEmoji.ts +3 -4
  343. package/src/dataUtils/suggested.ts +8 -4
  344. package/src/dataUtils/suggestedEmojis.ts +92 -0
  345. package/src/env.d.ts +4 -0
  346. package/src/hooks/preloadEmoji.ts +2 -47
  347. package/src/hooks/useActiveCategoryScrollDetection.ts +28 -16
  348. package/src/hooks/useCategoryHeight.ts +116 -10
  349. package/src/hooks/useDataIdentityStabilityWarning.ts +86 -0
  350. package/src/hooks/useDebouncedState.ts +13 -1
  351. package/src/hooks/useDisallowedEmojis.ts +31 -42
  352. package/src/hooks/useEmojiPreviewEvents.ts +43 -10
  353. package/src/hooks/useEmojiVirtualization.tsx +112 -52
  354. package/src/hooks/useFilter.ts +92 -132
  355. package/src/hooks/useFocus.ts +14 -24
  356. package/src/hooks/useIsEmojiHidden.ts +22 -14
  357. package/src/hooks/useKeyboardNavigation.ts +412 -432
  358. package/src/hooks/useMouseDownHandlers.ts +91 -16
  359. package/src/hooks/useOnFocus.ts +12 -3
  360. package/src/hooks/useOnScroll.ts +18 -2
  361. package/src/hooks/useReactionsFocus.ts +118 -0
  362. package/src/hooks/useRegisterRegion.ts +92 -0
  363. package/src/hooks/useResolvedEmojiData.ts +200 -0
  364. package/src/hooks/useScrollCategoryIntoView.ts +74 -12
  365. package/src/hooks/useSearchController.ts +369 -0
  366. package/src/hooks/useSearchResults.ts +51 -0
  367. package/src/hooks/useSetVariationPicker.ts +4 -1
  368. package/src/index.tsx +67 -5
  369. package/src/primitives/CategoryNav.tsx +40 -0
  370. package/src/primitives/Empty.tsx +68 -0
  371. package/src/primitives/List.tsx +61 -0
  372. package/src/primitives/LoadError.tsx +38 -0
  373. package/src/primitives/Loading.tsx +60 -0
  374. package/src/primitives/Panel.tsx +47 -0
  375. package/src/primitives/Preview.tsx +38 -0
  376. package/src/primitives/Reactions.tsx +2 -0
  377. package/src/primitives/Root.tsx +398 -0
  378. package/src/primitives/Search.tsx +41 -0
  379. package/src/primitives/SearchInput.tsx +110 -0
  380. package/src/primitives/SkinTone.tsx +76 -0
  381. package/src/primitives/Viewport.tsx +204 -0
  382. package/src/primitives/appearance.tsx +7 -0
  383. package/src/primitives/components.tsx +30 -0
  384. package/src/primitives/hooks.ts +165 -0
  385. package/src/primitives/index.ts +94 -0
  386. package/src/primitives/nativeProps.ts +90 -0
  387. package/src/primitives/scope.tsx +109 -0
  388. package/src/primitives/structuralStyles.tsx +136 -0
  389. package/src/primitives/tokens.ts +150 -0
  390. package/src/primitives/types.ts +192 -0
  391. package/src/state/activeEmojiStore.ts +39 -0
  392. package/src/state/navigationRegistry.ts +209 -0
  393. package/src/state/regionTraversal.ts +116 -0
  394. package/src/svg.d.ts +2 -2
  395. package/src/types/exposedTypes.ts +28 -1
  396. package/src/virtualization/virtualizationHelpers.ts +29 -8
  397. package/dist/components/body/Body.d.ts +0 -2
  398. package/dist/components/main/PickerMain.d.ts +0 -7
  399. package/dist/dataUtils/alphaNumericEmojiIndex.d.ts +0 -5
  400. package/dist/emoji-picker-react.cjs.development.js +0 -31413
  401. package/dist/emoji-picker-react.cjs.development.js.map +0 -1
  402. package/dist/emoji-picker-react.cjs.production.min.js +0 -2
  403. package/dist/emoji-picker-react.cjs.production.min.js.map +0 -1
  404. package/dist/emoji-picker-react.esm.js +0 -31411
  405. package/dist/emoji-picker-react.esm.js.map +0 -1
  406. package/dist/hooks/useHideEmojisByUniocode.d.ts +0 -1
  407. package/src/components/body/Body.tsx +0 -43
  408. package/src/components/main/PickerMain.tsx +0 -249
  409. package/src/dataUtils/alphaNumericEmojiIndex.ts +0 -21
@@ -0,0 +1,86 @@
1
+ /* global process: readonly */
2
+ import * as React from 'react';
3
+
4
+ import type { CustomEmoji } from '../config/customEmojiConfig';
5
+ import { defaultEmojiDataOrEmpty } from '../data/defaultEmojiData';
6
+ import type { EmojiData } from '../types/exposedTypes';
7
+
8
+ interface IdentityTracker {
9
+ prev: unknown;
10
+ consecutiveChanges: number;
11
+ warned: boolean;
12
+ }
13
+
14
+ function trackIdentity(
15
+ tracker: IdentityTracker,
16
+ value: unknown,
17
+ isDefault: boolean,
18
+ message: string,
19
+ ): void {
20
+ if (isDefault) {
21
+ tracker.prev = value;
22
+ tracker.consecutiveChanges = 0;
23
+ return;
24
+ }
25
+ if (tracker.prev !== undefined && tracker.prev !== value) {
26
+ tracker.consecutiveChanges += 1;
27
+ } else if (tracker.prev === value) {
28
+ tracker.consecutiveChanges = 0;
29
+ }
30
+ tracker.prev = value;
31
+ if (tracker.consecutiveChanges >= 3 && !tracker.warned) {
32
+ tracker.warned = true;
33
+ // eslint-disable-next-line no-console
34
+ console.warn(message);
35
+ }
36
+ }
37
+
38
+ function freshTracker(): IdentityTracker {
39
+ return { prev: undefined, consecutiveChanges: 0, warned: false };
40
+ }
41
+
42
+ /**
43
+ * Dev-only diagnostic.
44
+ *
45
+ * Identity caching only helps when callers keep data props referentially
46
+ * stable. If a non-default `emojiData` (or `customEmojis`) changes identity
47
+ * on three consecutive committed renders, warn once that repeated identity
48
+ * churn defeats prepared-data caching. Behavior stays correct; this is
49
+ * diagnostic only. A single/occasional replacement (e.g. locale change)
50
+ * must not warn. No deep-compare or clone is performed.
51
+ */
52
+ export const useDataIdentityStabilityWarning: (
53
+ emojiData: EmojiData | undefined,
54
+ customEmojis: CustomEmoji[] | undefined,
55
+ ) => void =
56
+ // Development only: production bundles drop the trackers entirely.
57
+ process.env.NODE_ENV === 'production'
58
+ ? () => undefined
59
+ : useIdentityChurnWarning;
60
+
61
+ function useIdentityChurnWarning(
62
+ emojiData: EmojiData | undefined,
63
+ customEmojis: CustomEmoji[] | undefined,
64
+ ): void {
65
+ const emojiState = React.useRef<IdentityTracker>(freshTracker());
66
+ const customState = React.useRef<IdentityTracker>(freshTracker());
67
+
68
+ React.useEffect(() => {
69
+ trackIdentity(
70
+ emojiState.current,
71
+ emojiData,
72
+ !emojiData || emojiData === defaultEmojiDataOrEmpty(),
73
+ '[emoji-picker-react] `emojiData` changed identity on three consecutive renders. ' +
74
+ 'Memoize/reuse the dataset object so the prepared-data cache can be shared. ' +
75
+ 'Behavior remains correct; caching is only defeated by identity churn.',
76
+ );
77
+ trackIdentity(
78
+ customState.current,
79
+ customEmojis,
80
+ !customEmojis || customEmojis.length === 0,
81
+ '[emoji-picker-react] `customEmojis` changed identity on three consecutive renders. ' +
82
+ 'Memoize/reuse the array so custom-emoji derivation can be cached. ' +
83
+ 'Behavior remains correct; caching is only defeated by identity churn.',
84
+ );
85
+ });
86
+ }
@@ -1,4 +1,4 @@
1
- import { useRef, useState } from 'react';
1
+ import { useEffect, useRef, useState } from 'react';
2
2
 
3
3
  export function useDebouncedState<T>(
4
4
  initialValue: T,
@@ -7,6 +7,18 @@ export function useDebouncedState<T>(
7
7
  const [state, setState] = useState<T>(initialValue);
8
8
  const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
9
9
 
10
+ // A pending trailing edge must never fire after unmount (React warns
11
+ // about the post-unmount setState and the work is wasted).
12
+ useEffect(
13
+ () => () => {
14
+ if (timer.current) {
15
+ clearTimeout(timer.current);
16
+ timer.current = null;
17
+ }
18
+ },
19
+ [],
20
+ );
21
+
10
22
  function debouncedSetState(value: T) {
11
23
  return new Promise<T>((resolve) => {
12
24
  if (timer.current) {
@@ -1,52 +1,41 @@
1
- import { useRef, useMemo } from 'react';
1
+ import { useCallback } from 'react';
2
2
 
3
- import { usePickerDataContext } from '../components/context/PickerDataContext';
4
- import { useEmojiVersionConfig } from '../config/useConfig';
3
+ import {
4
+ useActiveSkinToneState,
5
+ useNativeEmojiSupport,
6
+ } from '../components/context/PickerContext';
7
+ import { useEmojiVersionConfig, useUnicodeToHide } from '../config/useConfig';
5
8
  import { DataEmoji } from '../dataUtils/DataTypes';
6
9
  import {
7
10
  addedIn,
8
11
  emojiUnified,
9
12
  unifiedWithoutSkinTone,
10
13
  } from '../dataUtils/emojiUtils';
11
-
12
- import { useIsUnicodeHidden } from './useHideEmojisByUniocode';
13
-
14
- export function useDisallowedEmojis() {
15
- const DisallowedEmojisRef = useRef<Record<string, boolean>>({});
16
- const emojiVersionConfig = useEmojiVersionConfig();
17
- const { allEmojis } = usePickerDataContext();
18
-
19
- return useMemo(() => {
20
- const emojiVersion = parseFloat(`${emojiVersionConfig}`);
21
-
22
- if (!emojiVersionConfig || Number.isNaN(emojiVersion)) {
23
- return DisallowedEmojisRef.current;
24
- }
25
-
26
- return allEmojis.reduce((disallowedEmojis, emoji) => {
27
- if (addedInNewerVersion(emoji, emojiVersion)) {
28
- disallowedEmojis[emojiUnified(emoji)] = true;
29
- }
30
-
31
- return disallowedEmojis;
32
- }, DisallowedEmojisRef.current);
33
- }, [emojiVersionConfig, allEmojis]);
34
- }
14
+ import { isNativeEmojiSupported } from '../dataUtils/nativeEmojiSupport';
15
+ import { isCustomEmoji } from '../typeRefinements/typeRefinements';
35
16
 
36
17
  export function useIsEmojiDisallowed() {
37
- const disallowedEmojis = useDisallowedEmojis();
38
- const isUnicodeHidden = useIsUnicodeHidden();
39
-
40
- return function isEmojiDisallowed(emoji: DataEmoji) {
41
- const unified = unifiedWithoutSkinTone(emojiUnified(emoji));
42
-
43
- return Boolean(disallowedEmojis[unified] || isUnicodeHidden(unified));
44
- };
45
- }
46
-
47
- function addedInNewerVersion(
48
- emoji: DataEmoji,
49
- supportedLevel: number,
50
- ): boolean {
51
- return addedIn(emoji) > supportedLevel;
18
+ const configured = parseFloat(useEmojiVersionConfig() || '');
19
+ const nativeSupport = useNativeEmojiSupport();
20
+ // A configured cap restricts native support; it never overrides it.
21
+ const cap = Number.isNaN(configured) ? Infinity : configured;
22
+ const unicodeToHide = useUnicodeToHide();
23
+ const [activeSkinTone] = useActiveSkinToneState();
24
+
25
+ return useCallback(
26
+ function isEmojiDisallowed(
27
+ emoji: DataEmoji,
28
+ renderedUnified = emojiUnified(emoji, activeSkinTone),
29
+ ) {
30
+ if (unicodeToHide.has(unifiedWithoutSkinTone(emojiUnified(emoji))))
31
+ return true;
32
+ if (isCustomEmoji(emoji)) return false;
33
+ const version = addedIn(emoji);
34
+ return (
35
+ version > cap ||
36
+ !isNativeEmojiSupported(nativeSupport, renderedUnified, version)
37
+ );
38
+ },
39
+ [activeSkinTone, unicodeToHide, cap, nativeSupport],
40
+ );
52
41
  }
@@ -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,
@@ -10,17 +13,20 @@ import {
10
13
  useLazyLoadEmojisConfig,
11
14
  useSkinTonesDisabledConfig,
12
15
  } from '../config/useConfig';
13
- import { DataEmojis } from '../dataUtils/DataTypes';
16
+ import { DataEmoji, DataEmojis } from '../dataUtils/DataTypes';
14
17
  import { emojiUnified } from '../dataUtils/emojiUtils';
18
+ import { EmojiStyle } from '../types/exposedTypes';
15
19
  import {
16
20
  getEmojiPositionStyle,
17
21
  shouldVirtualize,
22
+ isJustBelowViewport,
18
23
  } from '../virtualization/virtualizationHelpers';
19
24
 
20
- import { preloadEmojiIfNeeded } from './preloadEmoji';
25
+ import { preloadEmoji } from './preloadEmoji';
21
26
  import { useCategoryHeight } from './useCategoryHeight';
22
27
  import { useIsEmojiDisallowed } from './useDisallowedEmojis';
23
28
  import { useIsEmojiHidden } from './useIsEmojiHidden';
29
+ import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';
24
30
 
25
31
  export function useEmojiVirtualization({
26
32
  categoryEmojis,
@@ -28,12 +34,15 @@ export function useEmojiVirtualization({
28
34
  onHeightReady,
29
35
  scrollTop,
30
36
  isCategoryVisible,
37
+ positioned,
31
38
  }: {
32
39
  categoryEmojis: DataEmojis;
33
40
  topOffset: number;
34
41
  onHeightReady: (height: number) => void;
35
42
  scrollTop: number;
36
43
  isCategoryVisible: boolean;
44
+ /** Whether topOffset is final; unmeasured sections above shift it. */
45
+ positioned: boolean;
37
46
  }) {
38
47
  const isEmojiHidden = useIsEmojiHidden();
39
48
  const lazyLoadEmojis = useLazyLoadEmojisConfig();
@@ -43,25 +52,52 @@ export function useEmojiVirtualization({
43
52
  const getEmojiUrl = useGetEmojiUrlConfig();
44
53
  const showVariations = !useSkinTonesDisabledConfig();
45
54
  const BodyRef = useBodyRef();
55
+ const requestNativeProbe = useRequestNativeProbe();
56
+ // Native glyphs checked exactly: the cells this pass renders (visible
57
+ // rows plus virtualization's margin rows), nothing else.
58
+ const probe: string[] = [];
59
+ const preloads: DataEmoji[] = [];
60
+ const [committed, setCommitted] = React.useState(false);
46
61
 
47
- let virtualizedCounter = 0;
48
-
49
- const emojisToPush = categoryEmojis.filter((emoji) => {
50
- const isDisallowed = isEmojiDisallowed(emoji);
51
- const { failedToLoad, filteredOut, hidden } = isEmojiHidden(emoji);
62
+ // A section shows each rendered emoji once. Recents can hold the same
63
+ // emoji under two tones (e.g. neutral and medium); the active tone
64
+ // renders both as one identity, which must not be listed or keyed twice.
65
+ const emojisToPush = React.useMemo(() => {
66
+ const rendered = new Set<string>();
67
+ return categoryEmojis.filter((emoji) => {
68
+ const isDisallowed = isEmojiDisallowed(emoji);
69
+ const { hidden } = isEmojiHidden(emoji);
52
70
 
53
- return !failedToLoad && !filteredOut && !hidden && !isDisallowed;
54
- });
71
+ if (hidden || isDisallowed) {
72
+ return false;
73
+ }
74
+ const unified = emojiUnified(emoji, activeSkinTone);
75
+ if (rendered.has(unified)) {
76
+ return false;
77
+ }
78
+ rendered.add(unified);
79
+ return true;
80
+ });
81
+ }, [categoryEmojis, activeSkinTone, isEmojiHidden, isEmojiDisallowed]);
55
82
 
56
- const dimensions = useCategoryHeight(emojisToPush.length);
83
+ const dimensions = useCategoryHeight(
84
+ emojisToPush.length,
85
+ isCategoryVisible && positioned,
86
+ );
57
87
 
88
+ // Guarantee a render even when a hidden picker has no measurement cell.
89
+ // A passive update avoids flushing all observer effects before first paint.
58
90
  useEffect(() => {
91
+ if (!dimensions) setCommitted(true);
92
+ }, [dimensions]);
93
+
94
+ useIsomorphicLayoutEffect(() => {
59
95
  if (dimensions) {
60
96
  onHeightReady(dimensions.categoryHeight);
61
97
  }
62
98
  }, [dimensions, onHeightReady, emojisToPush.length]);
63
99
 
64
- const isVirtualized = (style: { top: number; left: number } | undefined) =>
100
+ const isVirtualized = (style: { top: number } | undefined) =>
65
101
  dimensions &&
66
102
  BodyRef.current &&
67
103
  shouldVirtualize({
@@ -72,51 +108,75 @@ export function useEmojiVirtualization({
72
108
  dimensions,
73
109
  });
74
110
 
75
- const emojis = emojisToPush.reduce((accumulator, emoji, index) => {
76
- const unified = emojiUnified(emoji, activeSkinTone);
77
- const style = getEmojiPositionStyle(dimensions, index);
78
-
79
- if (isVirtualized(style)) {
80
- virtualizedCounter++;
81
- preloadEmojiIfNeeded(
82
- emoji,
83
- emojiStyle,
84
- scrollTop,
85
- BodyRef.current?.clientHeight ?? 0,
86
- topOffset,
87
- style,
88
- dimensions,
89
- getEmojiUrl,
90
- );
91
- return accumulator;
92
- }
111
+ const needsPreload = (style: { top: number } | undefined) =>
112
+ emojiStyle !== EmojiStyle.NATIVE &&
113
+ dimensions &&
114
+ style &&
115
+ isJustBelowViewport(
116
+ topOffset + style.top,
117
+ scrollTop + (BodyRef.current?.clientHeight ?? 0),
118
+ dimensions.emojiSize,
119
+ );
93
120
 
94
- if (!isCategoryVisible) {
95
- virtualizedCounter++;
96
- return accumulator;
97
- }
121
+ const waitingForLayout =
122
+ (!committed && !dimensions) ||
123
+ ((!dimensions || !positioned) && BodyRef.current?.clientWidth);
98
124
 
99
- accumulator.push(
100
- <ClickableEmoji
101
- showVariations={showVariations}
102
- key={unified}
103
- emoji={emoji}
104
- unified={unified}
105
- emojiStyle={emojiStyle}
106
- lazyLoad={lazyLoadEmojis}
107
- getEmojiUrl={getEmojiUrl}
108
- style={{
109
- ...style,
110
- position: 'absolute',
111
- }}
112
- />,
113
- );
114
- return accumulator;
115
- }, [] as ReactNode[]);
125
+ // MeasureEmoji supplies geometry before cells are built. Layout-free
126
+ // environments can render after the first commit without measurements.
127
+ const emojis = waitingForLayout
128
+ ? []
129
+ : emojisToPush.reduce((accumulator, emoji, index) => {
130
+ const style = getEmojiPositionStyle(dimensions, index);
131
+
132
+ if (isVirtualized(style)) {
133
+ if (needsPreload(style)) preloads.push(emoji);
134
+ return accumulator;
135
+ }
136
+
137
+ if (!isCategoryVisible) {
138
+ return accumulator;
139
+ }
140
+
141
+ const unified = emojiUnified(emoji, activeSkinTone);
142
+ // Wait for layout: an unmeasured pass renders a whole category, and
143
+ // while sections above are unmeasured this one renders off-screen
144
+ // cells as if it were at the top.
145
+ if (dimensions && positioned) probe.push(unified);
146
+ accumulator.push(
147
+ <ClickableEmoji
148
+ showVariations={showVariations}
149
+ key={unified}
150
+ emoji={emoji}
151
+ unified={unified}
152
+ emojiStyle={emojiStyle}
153
+ lazyLoad={lazyLoadEmojis}
154
+ getEmojiUrl={getEmojiUrl}
155
+ style={{
156
+ ...style,
157
+ position: 'absolute',
158
+ }}
159
+ index={index}
160
+ // Grid semantics (issue #508): each emoji is a cell of its
161
+ // category row; native button activation is kept.
162
+ role="gridcell"
163
+ />,
164
+ );
165
+ return accumulator;
166
+ }, [] as ReactNode[]);
167
+
168
+ // Every commit: requests for already-checked glyphs are free.
169
+ useEffect(() => requestNativeProbe(probe));
170
+ // Image requests belong to committed work, never an abandoned render.
171
+ // The proximity and cache checks run synchronously, without per-cell timers.
172
+ useEffect(() => {
173
+ preloads.forEach((emoji) => preloadEmoji(getEmojiUrl, emoji, emojiStyle));
174
+ });
116
175
 
117
176
  return {
118
- virtualizedCounter,
177
+ virtualizedCounter: emojisToPush.length - emojis.length,
119
178
  emojis,
120
179
  dimensions,
180
+ emojiCount: emojisToPush.length,
121
181
  };
122
182
  }