emoji-picker-react 4.22.3 → 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 +331 -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 -480
  271. package/package.json +180 -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
@@ -1,185 +1,660 @@
1
1
  import * as React from 'react';
2
2
  import { useState } from 'react';
3
3
 
4
+ import { isJsdom } from '../../DomUtils/isJsdom';
4
5
  import {
6
+ useEmojiStyleConfig,
7
+ useGetEmojiUrlConfig,
8
+ useCustomEmojisConfig,
5
9
  useDefaultSkinToneConfig,
10
+ useSkinToneConfig,
11
+ useDefaultSearchValueConfig,
6
12
  useReactionsOpenConfig,
13
+ useSearchValueConfig,
7
14
  } from '../../config/useConfig';
8
15
  import { DataEmoji } from '../../dataUtils/DataTypes';
16
+ import {
17
+ addedIn,
18
+ emojiUnified,
19
+ emojiVariations,
20
+ } from '../../dataUtils/emojiUtils';
21
+ import { NATIVE_PROBE_BATCH } from '../../dataUtils/nativeEmojiSequenceSupport';
22
+ import {
23
+ NativeEmojiProbe,
24
+ NativeEmojiSupport,
25
+ detectNativeEmojiSupport,
26
+ isNativeEmojiSupported,
27
+ nativeSupportSnapshot,
28
+ } from '../../dataUtils/nativeEmojiSupport';
9
29
  import { useDebouncedState } from '../../hooks/useDebouncedState';
10
- import { useDisallowedEmojis } from '../../hooks/useDisallowedEmojis';
11
30
  import { FilterDict } from '../../hooks/useFilter';
12
31
  import { useMarkInitialLoad } from '../../hooks/useInitialLoad';
13
- import { SkinTones } from '../../types/exposedTypes';
32
+ import { useIsomorphicLayoutEffect } from '../../hooks/useIsomorphicLayoutEffect';
33
+ import { createActiveEmojiStore } from '../../state/activeEmojiStore';
34
+ import { NavigationRegistry } from '../../state/navigationRegistry';
35
+ import { isCustomEmoji } from '../../typeRefinements/typeRefinements';
36
+ import { EmojiStyle, SkinTones } from '../../types/exposedTypes';
14
37
 
38
+ import { usePickerMainRef } from './ElementRefContext';
15
39
  import { usePickerDataContext } from './PickerDataContext';
16
40
 
17
- export function PickerContextProvider({ children }: Props) {
18
- const disallowedEmojis = useDisallowedEmojis();
19
- const defaultSkinTone = useDefaultSkinToneConfig();
20
- const reactionsDefaultOpen = useReactionsOpenConfig();
21
- const { searchIndex } = usePickerDataContext();
41
+ // One Root-scoped controller, narrowly sliced.
42
+ //
43
+ // The v4 provider exposed a single omnibus context whose value identity
44
+ // changed for unrelated state, so every subscriber rerendered on every
45
+ // update. This module still owns the same state (same hook names, same
46
+ // defaults, same behavior) but splits it into one stable services context
47
+ // plus narrow per-domain contexts. Each slice value is memoized on its own
48
+ // state only, so a reactions toggle never rerenders search subscribers,
49
+ // scroll/geometry work never rerenders search/category/preview/reactions
50
+ // subscribers, and each Root instance is isolated by construction.
51
+ //
52
+ // Active hover/focus subscriptions are keyed by cell so only the previous
53
+ // and next active cells rerender, while custom previews subscribe to the shared
54
+ // slice could provide.
55
+ //
56
+ // Only React-16.8-compatible runtime APIs are used here; the React floor
57
+ // scan (npm run check:react-floor) guards against newer hook APIs.
22
58
 
23
- // Initialize the filter with the inititial dictionary
24
- const filterRef = React.useRef<FilterState>(searchIndex);
25
-
26
- React.useEffect(() => {
27
- filterRef.current = searchIndex;
28
- }, [searchIndex]);
29
- const disallowClickRef = React.useRef<boolean>(false);
30
- const disallowMouseRef = React.useRef<boolean>(false);
31
- const disallowedEmojisRef =
32
- React.useRef<Record<string, boolean>>(disallowedEmojis);
59
+ type ReactState<T> = [T, React.Dispatch<React.SetStateAction<T>>];
33
60
 
34
- const suggestedUpdateState = useDebouncedState(Date.now(), 200);
35
- const searchTerm = useDebouncedState('', 100);
36
- const skinToneFanOpenState = useState<boolean>(false);
37
- const activeSkinTone = useState<SkinTones>(defaultSkinTone);
38
- const activeCategoryState = useState<ActiveCategoryState>(null);
39
- const emojisThatFailedToLoadState = useState<Set<string>>(new Set());
40
- const emojiVariationPickerState = useState<DataEmoji | null>(null);
41
- const reactionsModeState = useState(reactionsDefaultOpen);
42
- const [isPastInitialLoad, setIsPastInitialLoad] = useState(false);
43
- const visibleCategoriesState = useState<string[]>([]);
44
- const emojiSizeState = useState<number | null>(null);
61
+ // Memoize a state tuple on its state value alone. Setters from useState are
62
+ // stable; debounced setters close over stable setState + timer ref, so any
63
+ // captured instance behaves identically.
64
+ function useSliceValue<T>(tuple: ReactState<T>): ReactState<T> {
65
+ const [state, setState] = tuple;
66
+ // setState from useState is stable; the value intentionally re-memoizes on
67
+ // state changes only.
68
+ // eslint-disable-next-line react-hooks/exhaustive-deps
69
+ return React.useMemo(() => [state, setState] as ReactState<T>, [state]);
70
+ }
45
71
 
46
- useMarkInitialLoad(setIsPastInitialLoad);
72
+ function useDebouncedSliceValue<T>(
73
+ tuple: [T, (value: T) => Promise<T>],
74
+ ): [T, (value: T) => Promise<T>] {
75
+ const [state, setState] = tuple;
76
+ // The debounced setter is functionally stable (stable setState + timer
77
+ // ref), so capturing it alongside the state value is safe.
47
78
 
48
- return (
49
- <PickerContext.Provider
50
- value={{
51
- activeCategoryState,
52
- activeSkinTone,
53
- disallowClickRef,
54
- disallowMouseRef,
55
- disallowedEmojisRef,
56
- emojiVariationPickerState,
57
- emojisThatFailedToLoadState,
58
- filterRef,
59
- isPastInitialLoad,
60
- searchTerm,
61
- skinToneFanOpenState,
62
- suggestedUpdateState,
63
- reactionsModeState,
64
- visibleCategoriesState,
65
- emojiSizeState,
66
- }}
67
- >
68
- {children}
69
- </PickerContext.Provider>
79
+ return React.useMemo(
80
+ () => [state, setState] as [T, (value: T) => Promise<T>],
81
+ // eslint-disable-next-line react-hooks/exhaustive-deps
82
+ [state],
70
83
  );
71
84
  }
72
85
 
73
- type ReactState<T> = [T, React.Dispatch<React.SetStateAction<T>>];
86
+ export interface PickerServices {
87
+ activeEmojiStore: ReturnType<typeof createActiveEmojiStore>;
88
+ filterRef: React.MutableRefObject<FilterState>;
89
+ filterQueryOrderRef: React.MutableRefObject<string[]>;
90
+ disallowClickRef: React.MutableRefObject<boolean>;
91
+ disallowMouseRef: React.MutableRefObject<boolean>;
92
+ navigationRegistry: NavigationRegistry;
93
+ requestNativeProbe: RequestNativeProbe;
94
+ }
95
+
96
+ const PickerServicesContext =
97
+ /* @__PURE__ */ React.createContext<PickerServices>({
98
+ activeEmojiStore: createActiveEmojiStore(),
99
+ filterRef: { current: {} },
100
+ filterQueryOrderRef: { current: [] },
101
+ disallowClickRef: { current: false },
102
+ disallowMouseRef: { current: false },
103
+ navigationRegistry: new NavigationRegistry(),
104
+ requestNativeProbe: () => undefined,
105
+ });
74
106
 
75
- const PickerContext = React.createContext<{
107
+ const SearchSliceContext = /* @__PURE__ */ React.createContext<{
76
108
  searchTerm: [string, (term: string) => Promise<string>];
77
109
  suggestedUpdateState: [number, (term: number) => void];
78
- activeCategoryState: ReactState<ActiveCategoryState>;
110
+ }>({
111
+ searchTerm: ['', () => new Promise<string>(() => undefined)],
112
+ suggestedUpdateState: [Date.now(), () => {}],
113
+ });
114
+
115
+ // Raw input/display state for the search transition.
116
+ // Kept separate from the debounced accepted query above so keystrokes only
117
+ // rerender input subscribers, while commits flow through the query slice.
118
+ const SearchInputSliceContext = /* @__PURE__ */ React.createContext<{
119
+ displayValue: ReactState<string>;
120
+ committedValue: ReactState<string>;
121
+ composing: ReactState<boolean>;
122
+ }>({
123
+ displayValue: ['', () => {}],
124
+ committedValue: ['', () => {}],
125
+ composing: [false, () => {}],
126
+ });
127
+
128
+ const ReactionsSliceContext = /* @__PURE__ */ React.createContext<
129
+ ReactState<boolean>
130
+ >([false, () => {}]);
131
+
132
+ // Hovered/focused emoji (unified + original unified), written by the
133
+ // Viewport's pointer/focus listeners and read by Preview and the public
134
+ // useActiveEmoji hook. Its own slice: hover rerenders only its readers.
135
+ export type ActiveEmojiState = null | {
136
+ unified: string;
137
+ originalUnified: string;
138
+ };
139
+
140
+ const VariationSliceContext = /* @__PURE__ */ React.createContext<
141
+ ReactState<DataEmoji | null>
142
+ >([null, () => {}]);
143
+
144
+ const SkinToneSliceContext = /* @__PURE__ */ React.createContext<{
79
145
  activeSkinTone: ReactState<SkinTones>;
80
- emojisThatFailedToLoadState: ReactState<Set<string>>;
81
- isPastInitialLoad: boolean;
82
- emojiVariationPickerState: ReactState<DataEmoji | null>;
83
146
  skinToneFanOpenState: ReactState<boolean>;
84
- filterRef: React.MutableRefObject<FilterState>;
85
- disallowClickRef: React.MutableRefObject<boolean>;
86
- disallowMouseRef: React.MutableRefObject<boolean>;
87
- disallowedEmojisRef: React.MutableRefObject<Record<string, boolean>>;
88
- reactionsModeState: ReactState<boolean>;
147
+ }>({
148
+ activeSkinTone: [SkinTones.NEUTRAL, () => {}],
149
+ skinToneFanOpenState: [false, () => {}],
150
+ });
151
+
152
+ const ViewportSliceContext = /* @__PURE__ */ React.createContext<{
153
+ activeCategoryState: ReactState<ActiveCategoryState>;
89
154
  visibleCategoriesState: ReactState<Array<string>>;
90
155
  emojiSizeState: ReactState<number | null>;
91
156
  }>({
92
157
  activeCategoryState: [null, () => {}],
93
- activeSkinTone: [SkinTones.NEUTRAL, () => {}],
94
- disallowClickRef: { current: false },
95
- disallowMouseRef: { current: false },
96
- disallowedEmojisRef: { current: {} },
97
- emojiVariationPickerState: [null, () => {}],
98
- emojisThatFailedToLoadState: [new Set(), () => {}],
99
- filterRef: { current: {} },
100
- isPastInitialLoad: true,
101
- searchTerm: ['', () => new Promise<string>(() => undefined)],
102
- skinToneFanOpenState: [false, () => {}],
103
- suggestedUpdateState: [Date.now(), () => {}],
104
- reactionsModeState: [false, () => {}],
105
158
  visibleCategoriesState: [[], () => []],
106
159
  emojiSizeState: [null, () => {}],
107
160
  });
108
161
 
162
+ const LoadSliceContext = /* @__PURE__ */ React.createContext<{
163
+ emojisThatFailedToLoadState: [Set<string>, (unified: string) => void];
164
+ isPastInitialLoad: boolean;
165
+ }>({
166
+ emojisThatFailedToLoadState: [new Set(), () => {}],
167
+ isPastInitialLoad: true,
168
+ });
169
+
170
+ // Platform emoji support: a version prediction before paint, refined with
171
+ // exact per-sequence results in the background, and refreshed on font
172
+ // changes. `null` means "no filtering": before mount (SSR and the
173
+ // hydration render), or for image emoji styles. An explicit emojiVersion
174
+ // is an additional cap, not an opt-out.
175
+ const NativeSupportContext =
176
+ /* @__PURE__ */ React.createContext<NativeEmojiSupport | null>(null);
177
+
178
+ type RequestNativeProbe = (unifieds: string[]) => void;
179
+
180
+ /**
181
+ * Asks for exact checks of identities about to be shown: grid cells in
182
+ * and near the viewport, their skin tone variants, and reaction choices.
183
+ */
184
+ export function useRequestNativeProbe(): RequestNativeProbe {
185
+ return React.useContext(PickerServicesContext).requestNativeProbe;
186
+ }
187
+
188
+ function useNativeEmojiSupportState(): [
189
+ NativeEmojiSupport | null,
190
+ RequestNativeProbe,
191
+ ] {
192
+ const emojiStyle = useEmojiStyleConfig();
193
+ const { allEmojis } = usePickerDataContext();
194
+ const PickerMainRef = usePickerMainRef();
195
+ const shouldDetect = emojiStyle === EmojiStyle.NATIVE;
196
+ const [snapshot, setSnapshot] = useState<
197
+ [support: NativeEmojiSupport, data: DataEmoji[]] | null
198
+ >(null);
199
+ // Every identity shown in this Root's lifetime, re-checked when the
200
+ // font changes. Requests can arrive before detection starts.
201
+ const requestedRef = React.useRef(new Set<string>());
202
+ const requestRef = React.useRef<RequestNativeProbe>((unifieds) => {
203
+ unifieds.forEach((unified) => requestedRef.current.add(unified));
204
+ });
205
+ const request = React.useCallback<RequestNativeProbe>(
206
+ (unifieds) => requestRef.current(unifieds),
207
+ [],
208
+ );
209
+
210
+ useIsomorphicLayoutEffect(() => {
211
+ if (!shouldDetect || isJsdom()) {
212
+ setSnapshot(null);
213
+ return;
214
+ }
215
+ // Probe with the font the picker actually renders native emojis in,
216
+ // so a consumer font (e.g. a country-flag polyfill set through
217
+ // --epr-emoji-font-family) is what gets measured.
218
+ const root = PickerMainRef.current;
219
+ const view = root?.ownerDocument.defaultView;
220
+ if (!root || !view) return;
221
+ const requested = requestedRef.current;
222
+ const inventory: [unified: string, version: number][] = [];
223
+ // The first emoji of each version in dataset order is its sample.
224
+ const samples = new Map<number, string>();
225
+ allEmojis.forEach((emoji) => {
226
+ if (isCustomEmoji(emoji)) return;
227
+ const unified = emojiUnified(emoji);
228
+ const version = addedIn(emoji);
229
+ if (!samples.has(version)) samples.set(version, unified);
230
+ [unified, ...emojiVariations(emoji)].forEach((id) =>
231
+ inventory.push([id, version]),
232
+ );
233
+ });
234
+ let previousFont: string | null = null;
235
+ let timer: ReturnType<typeof setTimeout> | undefined;
236
+ let published: NativeEmojiSupport | null = null;
237
+ let detected: NativeEmojiProbe | undefined;
238
+ const pending = new Set<string>();
239
+ // A new snapshot rerenders the whole grid, so publish only when a
240
+ // verdict changes.
241
+ const publish = (next: NativeEmojiSupport) => {
242
+ const current = published;
243
+ if (
244
+ current &&
245
+ inventory.every(
246
+ ([unified, version]) =>
247
+ isNativeEmojiSupported(next, unified, version) ===
248
+ isNativeEmojiSupported(current, unified, version),
249
+ )
250
+ )
251
+ return;
252
+ published = next;
253
+ setSnapshot([next, allEmojis]);
254
+ };
255
+ // One canvas batch per task, so a fast scroll never blocks input.
256
+ const flush = () => {
257
+ timer = undefined;
258
+ if (!detected) return;
259
+ const batch = Array.from(pending).slice(0, NATIVE_PROBE_BATCH);
260
+ batch.forEach((unified) => pending.delete(unified));
261
+ detected.prime(batch);
262
+ // Publish once the backlog is checked, so a font refresh never
263
+ // flashes unchecked glyphs back to their prediction.
264
+ if (pending.size) timer = setTimeout(flush, 0);
265
+ else publish(nativeSupportSnapshot(detected, samples));
266
+ };
267
+ requestRef.current = (unifieds) => {
268
+ unifieds.forEach((unified) => {
269
+ requested.add(unified);
270
+ if (detected && !detected.has(unified)) pending.add(unified);
271
+ });
272
+ if (pending.size && timer === undefined) timer = setTimeout(flush, 0);
273
+ };
274
+ const probe = (refresh = false) => {
275
+ const customFont = view
276
+ .getComputedStyle(root)
277
+ .getPropertyValue('--epr-emoji-font-family')
278
+ .trim();
279
+ // Attribute changes often leave the computed font unchanged.
280
+ if (!refresh && customFont === previousFont) return;
281
+ previousFont = customFont;
282
+ clearTimeout(timer);
283
+ timer = undefined;
284
+ pending.clear();
285
+ const next = detectNativeEmojiSupport(
286
+ customFont || undefined,
287
+ refresh,
288
+ root.ownerDocument,
289
+ );
290
+ // An inconclusive probe filters nothing, just like the initial null.
291
+ // Avoid another synchronous full-grid render for this no-op result.
292
+ if (!('snapshot' in next)) {
293
+ detected = undefined;
294
+ published = null;
295
+ setSnapshot(null);
296
+ return;
297
+ }
298
+ detected = next;
299
+ // Publish before paint: cached exact results plus the prediction. A
300
+ // refresh after webfonts load keeps the current results on screen
301
+ // until the identities shown so far are checked again.
302
+ if (!refresh) publish(nativeSupportSnapshot(next, samples));
303
+ requestRef.current(Array.from(requested));
304
+ if (!pending.size) publish(nativeSupportSnapshot(next, samples));
305
+ };
306
+ probe();
307
+ // Any root or ancestor attribute can select another emoji font via CSS.
308
+ const observer = new view.MutationObserver(() => probe());
309
+ for (
310
+ let element: HTMLElement | null = root;
311
+ element;
312
+ element = element.parentElement
313
+ ) {
314
+ observer.observe(element, {
315
+ attributes: true,
316
+ });
317
+ }
318
+ const fonts = root.ownerDocument.fonts;
319
+ const refresh = () => probe(true);
320
+ fonts?.addEventListener('loadingdone', refresh);
321
+ return () => {
322
+ clearTimeout(timer);
323
+ detected = undefined;
324
+ observer.disconnect();
325
+ fonts?.removeEventListener('loadingdone', refresh);
326
+ };
327
+ }, [shouldDetect, PickerMainRef, allEmojis]);
328
+
329
+ // A snapshot belongs to the dataset it was computed for; a new dataset
330
+ // waits for its own layout-effect publication.
331
+ return [
332
+ shouldDetect && snapshot?.[1] === allEmojis ? snapshot[0] : null,
333
+ request,
334
+ ];
335
+ }
336
+
337
+ export function useNativeEmojiSupport(): NativeEmojiSupport | null {
338
+ return React.useContext(NativeSupportContext);
339
+ }
340
+
341
+ export function PickerContextProvider({ children }: Props) {
342
+ const [nativeSupport, requestNativeProbe] = useNativeEmojiSupportState();
343
+ const defaultSkinTone = useDefaultSkinToneConfig();
344
+ const reactionsDefaultOpen = useReactionsOpenConfig();
345
+ const defaultSearchValue = useDefaultSearchValueConfig();
346
+
347
+ // Per-Root query-result cache only. It never aliases shared snapshot
348
+ // state: query dicts are computed through the shared prepared core and
349
+ // stored here, bounded, so unmounting leaves nothing behind.
350
+ const filterRef = React.useRef<FilterState>({});
351
+ const filterQueryOrderRef = React.useRef<string[]>([]);
352
+ const { queryFilterDict } = usePickerDataContext();
353
+ const previousQueryBuilder = React.useRef(queryFilterDict);
354
+ if (previousQueryBuilder.current !== queryFilterDict) {
355
+ // Data can arrive after a user starts searching. Cached empty results
356
+ // must be rebuilt before descendants render the new dataset.
357
+ previousQueryBuilder.current = queryFilterDict;
358
+ filterRef.current = Object.fromEntries(
359
+ Object.keys(filterRef.current).map((query) => [
360
+ query,
361
+ queryFilterDict(query),
362
+ ]),
363
+ );
364
+ }
365
+ const disallowClickRef = React.useRef<boolean>(false);
366
+ const disallowMouseRef = React.useRef<boolean>(false);
367
+ const activeStoreRef = React.useRef<ReturnType<
368
+ typeof createActiveEmojiStore
369
+ > | null>(null);
370
+ if (!activeStoreRef.current)
371
+ activeStoreRef.current = createActiveEmojiStore();
372
+
373
+ const registryRef = React.useRef<NavigationRegistry | null>(null);
374
+ if (registryRef.current === null) {
375
+ registryRef.current = new NavigationRegistry();
376
+ }
377
+ React.useEffect(() => {
378
+ const registry = registryRef.current as NavigationRegistry;
379
+ // The registry object persists across remounts (ref-held), so revive
380
+ // it on every mount: without this, the unmount cleanup below leaves
381
+ // all generation-guarded navigation permanently dead after any
382
+ // remount (including the StrictMode double-mount).
383
+ registry.revive();
384
+ return () => {
385
+ // Root unmount invalidates pending materialize/scroll/focus work.
386
+ registry.dispose();
387
+ };
388
+ }, []);
389
+
390
+ const servicesValue = React.useMemo<PickerServices>(
391
+ () => ({
392
+ activeEmojiStore: activeStoreRef.current as ReturnType<
393
+ typeof createActiveEmojiStore
394
+ >,
395
+ filterRef,
396
+ filterQueryOrderRef,
397
+ disallowClickRef,
398
+ disallowMouseRef,
399
+ navigationRegistry: registryRef.current as NavigationRegistry,
400
+ requestNativeProbe,
401
+ }),
402
+ // All members are stable refs or callbacks; the identity never changes.
403
+ // eslint-disable-next-line react-hooks/exhaustive-deps
404
+ [],
405
+ );
406
+
407
+ const searchTerm = useDebouncedSliceValue(useDebouncedState('', 100));
408
+ const suggestedUpdateState = useDebouncedSliceValue(
409
+ useDebouncedState(Date.now(), 200),
410
+ );
411
+ const searchValue = React.useMemo(
412
+ () => ({ searchTerm, suggestedUpdateState }),
413
+ // eslint-disable-next-line react-hooks/exhaustive-deps
414
+ [searchTerm[0], suggestedUpdateState[0]],
415
+ );
416
+
417
+ // Accepted/display state. A controlled `searchValue` is the visible
418
+ // source of truth from the first paint (including SSR, where effects
419
+ // never run); `defaultSearchValue` is read once per mounted lifetime
420
+ // via the state initializer.
421
+ const searchValueConfig = useSearchValueConfig();
422
+ const initialAccepted = searchValueConfig ?? defaultSearchValue ?? '';
423
+ const displayState = useState<string>(initialAccepted);
424
+ const committedState = useState<string>(initialAccepted);
425
+ const composingState = useState<boolean>(false);
426
+ const searchInputValue = React.useMemo(
427
+ () => ({
428
+ displayValue: displayState as ReactState<string>,
429
+ committedValue: committedState as ReactState<string>,
430
+ composing: composingState as ReactState<boolean>,
431
+ }),
432
+ // eslint-disable-next-line react-hooks/exhaustive-deps
433
+ [displayState[0], committedState[0], composingState[0]],
434
+ );
435
+
436
+ const reactionsModeState = useState(reactionsDefaultOpen);
437
+ const reactionsValue = useSliceValue(reactionsModeState);
438
+
439
+ const emojiVariationPickerState = useState<DataEmoji | null>(null);
440
+ const variationValue = useSliceValue(emojiVariationPickerState);
441
+
442
+ // Controlled `skinTone` is the visible source of truth; the setter still
443
+ // updates local state (harmless while controlled) so switching back to
444
+ // uncontrolled resumes from the last selection. Selection always reports
445
+ // through onSkinToneChange, which the skin tone picker calls itself.
446
+ const controlledSkinTone = useSkinToneConfig();
447
+ const [localSkinTone, setLocalSkinTone] =
448
+ useState<SkinTones>(defaultSkinTone);
449
+ const effectiveSkinTone = controlledSkinTone ?? localSkinTone;
450
+ const skinToneFanOpenState = useState<boolean>(false);
451
+ const skinToneValue = React.useMemo(
452
+ () => ({
453
+ activeSkinTone: [
454
+ effectiveSkinTone,
455
+ setLocalSkinTone,
456
+ ] as ReactState<SkinTones>,
457
+ skinToneFanOpenState: skinToneFanOpenState as ReactState<boolean>,
458
+ }),
459
+ // eslint-disable-next-line react-hooks/exhaustive-deps
460
+ [effectiveSkinTone, skinToneFanOpenState[0]],
461
+ );
462
+
463
+ const activeCategoryState = useState<ActiveCategoryState>(null);
464
+ const emojiStyle = useEmojiStyleConfig();
465
+ const resolver = useGetEmojiUrlConfig();
466
+ const customEmojis = useCustomEmojisConfig();
467
+ // Each asset source owns its failure cache. Errors from older sources can
468
+ // only update their old cache; they never suppress the current assets.
469
+ const failedIds = React.useMemo(
470
+ () => new Set<string>(),
471
+ // The cache lifetime follows asset inputs, not the current failures.
472
+ // eslint-disable-next-line react-hooks/exhaustive-deps
473
+ [emojiStyle, resolver, customEmojis],
474
+ );
475
+ const [failureVersion, setFailureVersion] = useState({});
476
+ const setFailed = React.useCallback(
477
+ (unified: string) => {
478
+ failedIds.add(unified);
479
+ setFailureVersion({});
480
+ },
481
+ [failedIds],
482
+ );
483
+ const emojisThatFailedToLoadState: [Set<string>, (unified: string) => void] =
484
+ [failedIds, setFailed];
485
+ const visibleCategoriesState = useState<string[]>([]);
486
+ const emojiSizeState = useState<number | null>(null);
487
+ const viewportValue = React.useMemo(
488
+ () => ({
489
+ activeCategoryState:
490
+ activeCategoryState as ReactState<ActiveCategoryState>,
491
+ visibleCategoriesState: visibleCategoriesState as ReactState<
492
+ Array<string>
493
+ >,
494
+ emojiSizeState: emojiSizeState as ReactState<number | null>,
495
+ }),
496
+ // eslint-disable-next-line react-hooks/exhaustive-deps
497
+ [activeCategoryState[0], visibleCategoriesState[0], emojiSizeState[0]],
498
+ );
499
+
500
+ const [isPastInitialLoad, setIsPastInitialLoad] = useState(false);
501
+ const loadValue = React.useMemo(
502
+ () => ({
503
+ emojisThatFailedToLoadState,
504
+ isPastInitialLoad,
505
+ }),
506
+ // eslint-disable-next-line react-hooks/exhaustive-deps
507
+ [failedIds, failureVersion, setFailed, isPastInitialLoad],
508
+ );
509
+
510
+ useMarkInitialLoad(setIsPastInitialLoad);
511
+
512
+ return (
513
+ <PickerServicesContext.Provider value={servicesValue}>
514
+ <NativeSupportContext.Provider value={nativeSupport}>
515
+ <SearchSliceContext.Provider value={searchValue}>
516
+ <SearchInputSliceContext.Provider value={searchInputValue}>
517
+ <ReactionsSliceContext.Provider value={reactionsValue}>
518
+ <VariationSliceContext.Provider value={variationValue}>
519
+ <SkinToneSliceContext.Provider value={skinToneValue}>
520
+ <ViewportSliceContext.Provider value={viewportValue}>
521
+ <LoadSliceContext.Provider value={loadValue}>
522
+ {children}
523
+ </LoadSliceContext.Provider>
524
+ </ViewportSliceContext.Provider>
525
+ </SkinToneSliceContext.Provider>
526
+ </VariationSliceContext.Provider>
527
+ </ReactionsSliceContext.Provider>
528
+ </SearchInputSliceContext.Provider>
529
+ </SearchSliceContext.Provider>
530
+ </NativeSupportContext.Provider>
531
+ </PickerServicesContext.Provider>
532
+ );
533
+ }
534
+
109
535
  type Props = Readonly<{
110
536
  children: React.ReactNode;
111
537
  }>;
112
538
 
113
539
  export function useFilterRef() {
114
- const { filterRef } = React.useContext(PickerContext);
540
+ const { filterRef } = React.useContext(PickerServicesContext);
115
541
  return filterRef;
116
542
  }
117
543
 
544
+ export function useFilterQueryOrderRef() {
545
+ const { filterQueryOrderRef } = React.useContext(PickerServicesContext);
546
+ return filterQueryOrderRef;
547
+ }
548
+
118
549
  export function useDisallowClickRef() {
119
- const { disallowClickRef } = React.useContext(PickerContext);
550
+ const { disallowClickRef } = React.useContext(PickerServicesContext);
120
551
  return disallowClickRef;
121
552
  }
122
553
 
123
554
  export function useDisallowMouseRef() {
124
- const { disallowMouseRef } = React.useContext(PickerContext);
555
+ const { disallowMouseRef } = React.useContext(PickerServicesContext);
125
556
  return disallowMouseRef;
126
557
  }
127
558
 
559
+ export function useNavigationRegistry(): NavigationRegistry {
560
+ const { navigationRegistry } = React.useContext(PickerServicesContext);
561
+ return navigationRegistry;
562
+ }
563
+
564
+ export function useActiveEmojiState(): ReactState<ActiveEmojiState> {
565
+ const { activeEmojiStore: store } = React.useContext(PickerServicesContext);
566
+ const [value, setValue] = React.useState(store.get);
567
+ React.useEffect(() => {
568
+ const read = () => setValue(store.get());
569
+ const unsubscribe = store.subscribe(read);
570
+ read();
571
+ return unsubscribe;
572
+ }, [store]);
573
+ return [value, store.set];
574
+ }
575
+
576
+ export function useIsActiveEmoji(unified?: string): boolean {
577
+ const { activeEmojiStore: store } = React.useContext(PickerServicesContext);
578
+ const read = React.useCallback(
579
+ () => !!unified && store.get()?.unified === unified,
580
+ [store, unified],
581
+ );
582
+ const [active, setActive] = React.useState(read);
583
+ React.useEffect(() => {
584
+ const update = () => setActive(read());
585
+ const unsubscribe = store.subscribe(update, unified);
586
+ update();
587
+ return unsubscribe;
588
+ }, [store, unified, read]);
589
+ // The identity can change during virtualization before effects run.
590
+ return active && read();
591
+ }
592
+
128
593
  export function useReactionsModeState() {
129
- const { reactionsModeState } = React.useContext(PickerContext);
594
+ const reactionsModeState = React.useContext(ReactionsSliceContext);
130
595
  return reactionsModeState;
131
596
  }
132
597
 
133
598
  export function useSearchTermState() {
134
- const { searchTerm } = React.useContext(PickerContext);
599
+ const { searchTerm } = React.useContext(SearchSliceContext);
135
600
  return searchTerm;
136
601
  }
137
602
 
603
+ export function useSearchDisplayState() {
604
+ const { displayValue } = React.useContext(SearchInputSliceContext);
605
+ return displayValue;
606
+ }
607
+
608
+ export function useSearchCommittedState() {
609
+ const { committedValue } = React.useContext(SearchInputSliceContext);
610
+ return committedValue;
611
+ }
612
+
613
+ export function useSearchComposingState() {
614
+ const { composing } = React.useContext(SearchInputSliceContext);
615
+ return composing;
616
+ }
617
+
138
618
  export function useActiveSkinToneState(): [
139
619
  SkinTones,
140
620
  (skinTone: SkinTones) => void,
141
621
  ] {
142
- const { activeSkinTone } = React.useContext(PickerContext);
622
+ const { activeSkinTone } = React.useContext(SkinToneSliceContext);
143
623
  return activeSkinTone;
144
624
  }
145
625
 
146
626
  export function useEmojisThatFailedToLoadState() {
147
- const { emojisThatFailedToLoadState } = React.useContext(PickerContext);
627
+ const { emojisThatFailedToLoadState } = React.useContext(LoadSliceContext);
148
628
  return emojisThatFailedToLoadState;
149
629
  }
150
630
 
151
631
  export function useIsPastInitialLoad(): boolean {
152
- const { isPastInitialLoad } = React.useContext(PickerContext);
632
+ const { isPastInitialLoad } = React.useContext(LoadSliceContext);
153
633
  return isPastInitialLoad;
154
634
  }
155
635
 
156
636
  export function useEmojiVariationPickerState() {
157
- const { emojiVariationPickerState } = React.useContext(PickerContext);
637
+ const emojiVariationPickerState = React.useContext(VariationSliceContext);
158
638
  return emojiVariationPickerState;
159
639
  }
160
640
 
161
641
  export function useSkinToneFanOpenState() {
162
- const { skinToneFanOpenState } = React.useContext(PickerContext);
642
+ const { skinToneFanOpenState } = React.useContext(SkinToneSliceContext);
163
643
  return skinToneFanOpenState;
164
644
  }
165
645
 
166
- export function useDisallowedEmojisRef() {
167
- const { disallowedEmojisRef } = React.useContext(PickerContext);
168
- return disallowedEmojisRef;
169
- }
170
-
171
646
  export function useVisibleCategoriesState() {
172
- const { visibleCategoriesState } = React.useContext(PickerContext);
647
+ const { visibleCategoriesState } = React.useContext(ViewportSliceContext);
173
648
  return visibleCategoriesState;
174
649
  }
175
650
 
176
651
  export function useEmojiSizeState() {
177
- const { emojiSizeState } = React.useContext(PickerContext);
652
+ const { emojiSizeState } = React.useContext(ViewportSliceContext);
178
653
  return emojiSizeState;
179
654
  }
180
655
 
181
656
  export function useUpdateSuggested(): [number, () => void] {
182
- const { suggestedUpdateState } = React.useContext(PickerContext);
657
+ const { suggestedUpdateState } = React.useContext(SearchSliceContext);
183
658
 
184
659
  const [suggestedUpdated, setsuggestedUpdate] = suggestedUpdateState;
185
660
  return [