emoji-picker-react 4.22.2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (405) hide show
  1. package/README.md +335 -49
  2. package/dist/DomUtils/categoryNameFromDom.d.ts +1 -1
  3. package/dist/DomUtils/classNames.d.ts +24 -23
  4. package/dist/DomUtils/detectEmojyPartiallyBelowFold.d.ts +2 -2
  5. package/dist/DomUtils/elementPositionInRow.d.ts +5 -5
  6. package/dist/DomUtils/eventBelongsToPicker.d.ts +2 -0
  7. package/dist/DomUtils/focusElement.d.ts +5 -5
  8. package/dist/DomUtils/getActiveElement.d.ts +2 -2
  9. package/dist/DomUtils/isJsdom.d.ts +1 -0
  10. package/dist/DomUtils/isRtl.d.ts +1 -0
  11. package/dist/DomUtils/keyboardNavigation.d.ts +7 -8
  12. package/dist/DomUtils/scrollTo.d.ts +4 -5
  13. package/dist/DomUtils/selectors.d.ts +31 -35
  14. package/dist/EmojiPickerReact.d.ts +5 -5
  15. package/dist/Stylesheet/DedupedStyle.d.ts +13 -0
  16. package/dist/Stylesheet/stylesheet.d.ts +40 -37
  17. package/dist/components/ErrorBoundary.d.ts +15 -15
  18. package/dist/components/Layout/Absolute.d.ts +8 -8
  19. package/dist/components/Layout/Flex.d.ts +13 -13
  20. package/dist/components/Layout/Relative.d.ts +8 -8
  21. package/dist/components/Layout/Space.d.ts +7 -7
  22. package/dist/components/Reactions/BtnPlus.d.ts +2 -2
  23. package/dist/components/Reactions/DEFAULT_REACTIONS.d.ts +1 -1
  24. package/dist/components/Reactions/Reactions.d.ts +3 -2
  25. package/dist/components/atoms/Button.d.ts +11 -6
  26. package/dist/components/body/EmojiCategory.d.ts +13 -12
  27. package/dist/components/body/EmojiList.d.ts +7 -4
  28. package/dist/components/body/EmojiVariationPicker.d.ts +2 -2
  29. package/dist/components/body/MeasureEmoji.d.ts +2 -2
  30. package/dist/components/body/listComponents.d.ts +47 -0
  31. package/dist/components/context/ElementRefContext.d.ts +18 -16
  32. package/dist/components/context/PickerConfigContext.d.ts +14 -9
  33. package/dist/components/context/PickerContext.d.ts +57 -25
  34. package/dist/components/context/PickerDataContext.d.ts +23 -17
  35. package/dist/components/emoji/BaseEmojiProps.d.ts +14 -13
  36. package/dist/components/emoji/ClickableEmojiButton.d.ts +23 -15
  37. package/dist/components/emoji/Emoji.d.ts +18 -15
  38. package/dist/components/emoji/EmojiImg.d.ts +11 -11
  39. package/dist/components/emoji/ExportedEmoji.d.ts +11 -11
  40. package/dist/components/emoji/NativeEmoji.d.ts +6 -6
  41. package/dist/components/emoji/ViewOnlyEmoji.d.ts +3 -3
  42. package/dist/components/emoji/emojiStyles.d.ts +6 -1
  43. package/dist/components/footer/Preview.d.ts +7 -7
  44. package/dist/components/header/Header.d.ts +2 -2
  45. package/dist/components/header/Search/BtnClearSearch.d.ts +2 -2
  46. package/dist/components/header/Search/IcnSearch.d.ts +2 -2
  47. package/dist/components/header/Search/Search.d.ts +11 -3
  48. package/dist/components/header/SkinTonePicker/BtnSkinToneVariation.d.ts +12 -12
  49. package/dist/components/header/SkinTonePicker/SkinTonePicker.d.ts +11 -11
  50. package/dist/components/icons/svgIcons.d.ts +4 -0
  51. package/dist/components/main/PickerMainBehaviors.d.ts +4 -0
  52. package/dist/components/main/defaultAppearance.d.ts +12 -0
  53. package/dist/components/main/labelHeight.d.ts +1 -0
  54. package/dist/components/navigation/CategoryButton.d.ts +12 -12
  55. package/dist/components/navigation/CategoryNavIcon.d.ts +4 -4
  56. package/dist/components/navigation/CategoryNavigation.d.ts +19 -2
  57. package/dist/config/categoryConfig.d.ts +34 -34
  58. package/dist/config/cdnUrls.d.ts +2 -2
  59. package/dist/config/compareConfig.d.ts +10 -2
  60. package/dist/config/config.d.ts +166 -64
  61. package/dist/config/customEmojiConfig.d.ts +14 -14
  62. package/dist/config/defaultPickerConfiguration.d.ts +10 -0
  63. package/dist/config/mutableConfig.d.ts +32 -10
  64. package/dist/config/useConfig.d.ts +48 -36
  65. package/dist/data/defaultEmojiData.d.ts +9 -0
  66. package/dist/data/defaultEmojiData.js +40 -0
  67. package/dist/data/emojis-bn.d.mts +39 -0
  68. package/dist/data/emojis-bn.d.ts +38 -2
  69. package/dist/data/emojis-bn.js +25 -2
  70. package/dist/data/emojis-bn.mjs +2 -0
  71. package/dist/data/emojis-da.d.mts +39 -0
  72. package/dist/data/emojis-da.d.ts +38 -2
  73. package/dist/data/emojis-da.js +25 -2
  74. package/dist/data/emojis-da.mjs +2 -0
  75. package/dist/data/emojis-de.d.mts +39 -0
  76. package/dist/data/emojis-de.d.ts +38 -2
  77. package/dist/data/emojis-de.js +25 -2
  78. package/dist/data/emojis-de.mjs +2 -0
  79. package/dist/data/emojis-en-gb.d.mts +39 -0
  80. package/dist/data/emojis-en-gb.d.ts +38 -2
  81. package/dist/data/emojis-en-gb.js +25 -2
  82. package/dist/data/emojis-en-gb.mjs +2 -0
  83. package/dist/data/emojis-en.d.mts +39 -0
  84. package/dist/data/emojis-en.d.ts +38 -2
  85. package/dist/data/emojis-en.js +25 -2
  86. package/dist/data/emojis-en.mjs +2 -0
  87. package/dist/data/emojis-es-mx.d.mts +39 -0
  88. package/dist/data/emojis-es-mx.d.ts +38 -2
  89. package/dist/data/emojis-es-mx.js +25 -2
  90. package/dist/data/emojis-es-mx.mjs +2 -0
  91. package/dist/data/emojis-es.d.mts +39 -0
  92. package/dist/data/emojis-es.d.ts +38 -2
  93. package/dist/data/emojis-es.js +25 -2
  94. package/dist/data/emojis-es.mjs +2 -0
  95. package/dist/data/emojis-et.d.mts +39 -0
  96. package/dist/data/emojis-et.d.ts +38 -2
  97. package/dist/data/emojis-et.js +25 -2
  98. package/dist/data/emojis-et.mjs +2 -0
  99. package/dist/data/emojis-fi.d.mts +39 -0
  100. package/dist/data/emojis-fi.d.ts +38 -2
  101. package/dist/data/emojis-fi.js +25 -2
  102. package/dist/data/emojis-fi.mjs +2 -0
  103. package/dist/data/emojis-fr.d.mts +39 -0
  104. package/dist/data/emojis-fr.d.ts +38 -2
  105. package/dist/data/emojis-fr.js +25 -2
  106. package/dist/data/emojis-fr.mjs +2 -0
  107. package/dist/data/emojis-hi.d.mts +39 -0
  108. package/dist/data/emojis-hi.d.ts +38 -2
  109. package/dist/data/emojis-hi.js +25 -2
  110. package/dist/data/emojis-hi.mjs +2 -0
  111. package/dist/data/emojis-hu.d.mts +39 -0
  112. package/dist/data/emojis-hu.d.ts +38 -2
  113. package/dist/data/emojis-hu.js +25 -2
  114. package/dist/data/emojis-hu.mjs +2 -0
  115. package/dist/data/emojis-it.d.mts +39 -0
  116. package/dist/data/emojis-it.d.ts +38 -2
  117. package/dist/data/emojis-it.js +25 -2
  118. package/dist/data/emojis-it.mjs +2 -0
  119. package/dist/data/emojis-ja.d.mts +39 -0
  120. package/dist/data/emojis-ja.d.ts +38 -2
  121. package/dist/data/emojis-ja.js +25 -2
  122. package/dist/data/emojis-ja.mjs +2 -0
  123. package/dist/data/emojis-ko.d.mts +39 -0
  124. package/dist/data/emojis-ko.d.ts +38 -2
  125. package/dist/data/emojis-ko.js +25 -2
  126. package/dist/data/emojis-ko.mjs +2 -0
  127. package/dist/data/emojis-lt.d.mts +39 -0
  128. package/dist/data/emojis-lt.d.ts +38 -2
  129. package/dist/data/emojis-lt.js +25 -2
  130. package/dist/data/emojis-lt.mjs +2 -0
  131. package/dist/data/emojis-ms.d.mts +39 -0
  132. package/dist/data/emojis-ms.d.ts +38 -2
  133. package/dist/data/emojis-ms.js +25 -2
  134. package/dist/data/emojis-ms.mjs +2 -0
  135. package/dist/data/emojis-nb.d.mts +39 -0
  136. package/dist/data/emojis-nb.d.ts +38 -2
  137. package/dist/data/emojis-nb.js +25 -2
  138. package/dist/data/emojis-nb.mjs +2 -0
  139. package/dist/data/emojis-nl.d.mts +39 -0
  140. package/dist/data/emojis-nl.d.ts +38 -2
  141. package/dist/data/emojis-nl.js +25 -2
  142. package/dist/data/emojis-nl.mjs +2 -0
  143. package/dist/data/emojis-pl.d.mts +39 -0
  144. package/dist/data/emojis-pl.d.ts +38 -2
  145. package/dist/data/emojis-pl.js +25 -2
  146. package/dist/data/emojis-pl.mjs +2 -0
  147. package/dist/data/emojis-pt.d.mts +39 -0
  148. package/dist/data/emojis-pt.d.ts +38 -2
  149. package/dist/data/emojis-pt.js +25 -2
  150. package/dist/data/emojis-pt.mjs +2 -0
  151. package/dist/data/emojis-ru.d.mts +39 -0
  152. package/dist/data/emojis-ru.d.ts +38 -2
  153. package/dist/data/emojis-ru.js +25 -2
  154. package/dist/data/emojis-ru.mjs +2 -0
  155. package/dist/data/emojis-sv.d.mts +39 -0
  156. package/dist/data/emojis-sv.d.ts +38 -2
  157. package/dist/data/emojis-sv.js +25 -2
  158. package/dist/data/emojis-sv.mjs +2 -0
  159. package/dist/data/emojis-th.d.mts +39 -0
  160. package/dist/data/emojis-th.d.ts +38 -2
  161. package/dist/data/emojis-th.js +25 -2
  162. package/dist/data/emojis-th.mjs +2 -0
  163. package/dist/data/emojis-uk.d.mts +39 -0
  164. package/dist/data/emojis-uk.d.ts +38 -2
  165. package/dist/data/emojis-uk.js +25 -2
  166. package/dist/data/emojis-uk.mjs +2 -0
  167. package/dist/data/emojis-vi.d.mts +39 -0
  168. package/dist/data/emojis-vi.d.ts +38 -2
  169. package/dist/data/emojis-vi.js +25 -2
  170. package/dist/data/emojis-vi.mjs +2 -0
  171. package/dist/data/emojis-zh-hant.d.mts +39 -0
  172. package/dist/data/emojis-zh-hant.d.ts +38 -2
  173. package/dist/data/emojis-zh-hant.js +25 -2
  174. package/dist/data/emojis-zh-hant.mjs +2 -0
  175. package/dist/data/emojis-zh.d.mts +39 -0
  176. package/dist/data/emojis-zh.d.ts +38 -2
  177. package/dist/data/emojis-zh.js +25 -2
  178. package/dist/data/emojis-zh.mjs +2 -0
  179. package/dist/data/index.js +1 -0
  180. package/dist/data/registerDefaultEmojiData.d.ts +1 -0
  181. package/dist/data/registerDefaultEmojiData.js +3 -0
  182. package/dist/data-core/pickerData.d.ts +12 -0
  183. package/dist/data-core/prepare.d.ts +23 -0
  184. package/dist/data-core/search.d.ts +13 -0
  185. package/dist/data-core/types.d.ts +23 -0
  186. package/dist/data.d.mts +72 -0
  187. package/dist/data.d.ts +72 -0
  188. package/dist/dataUtils/DataTypes.d.ts +23 -19
  189. package/dist/dataUtils/emojiSelectors.d.ts +15 -16
  190. package/dist/dataUtils/emojiUtils.d.ts +1 -12
  191. package/dist/dataUtils/nativeEmojiSequenceSupport.d.ts +10 -0
  192. package/dist/dataUtils/nativeEmojiSupport.d.ts +40 -0
  193. package/dist/dataUtils/parseNativeEmoji.d.ts +1 -1
  194. package/dist/dataUtils/suggested.d.ts +11 -11
  195. package/dist/dataUtils/suggestedEmojis.d.ts +7 -0
  196. package/dist/esm/chunks/chunk-46BUDQLE.mjs +228 -0
  197. package/dist/esm/chunks/chunk-7ZGTUZFB.mjs +16 -0
  198. package/dist/esm/chunks/chunk-FQIJ4HBW.mjs +25 -0
  199. package/dist/esm/chunks/chunk-KQPOIRSC.mjs +9305 -0
  200. package/dist/esm/chunks/chunk-LO4TPPLH.mjs +43 -0
  201. package/dist/esm/chunks/emojis-6L3GRBAP.mjs +9 -0
  202. package/dist/esm/data.mjs +18 -0
  203. package/dist/esm/index.mjs +389 -0
  204. package/dist/esm/primitives/index.mjs +67 -0
  205. package/dist/hooks/preloadEmoji.d.ts +9 -9
  206. package/dist/hooks/useActiveCategoryScrollDetection.d.ts +11 -11
  207. package/dist/hooks/useCategoryHeight.d.ts +6 -5
  208. package/dist/hooks/useCloseAllOpenToggles.d.ts +2 -2
  209. package/dist/hooks/useDataIdentityStabilityWarning.d.ts +13 -0
  210. package/dist/hooks/useDebouncedState.d.ts +1 -1
  211. package/dist/hooks/useDisallowMouseMove.d.ts +4 -4
  212. package/dist/hooks/useDisallowedEmojis.d.ts +2 -3
  213. package/dist/hooks/useEmojiPreviewEvents.d.ts +3 -3
  214. package/dist/hooks/useEmojiVirtualization.d.ts +21 -17
  215. package/dist/hooks/useFilter.d.ts +10 -16
  216. package/dist/hooks/useFocus.d.ts +3 -3
  217. package/dist/hooks/useInitialLoad.d.ts +2 -2
  218. package/dist/hooks/useIsEmojiHidden.d.ts +8 -8
  219. package/dist/hooks/useIsMounted.d.ts +1 -1
  220. package/dist/hooks/useIsSearchMode.d.ts +1 -1
  221. package/dist/hooks/useIsomorphicLayoutEffect.d.ts +2 -2
  222. package/dist/hooks/useKeyboardNavigation.d.ts +3 -1
  223. package/dist/hooks/useMouseDownHandlers.d.ts +8 -4
  224. package/dist/hooks/useOnFocus.d.ts +1 -1
  225. package/dist/hooks/useOnScroll.d.ts +2 -2
  226. package/dist/hooks/useReactionsFocus.d.ts +1 -0
  227. package/dist/hooks/useRegisterRegion.d.ts +24 -0
  228. package/dist/hooks/useResolvedEmojiData.d.ts +37 -0
  229. package/dist/hooks/useScrollCategoryIntoView.d.ts +1 -1
  230. package/dist/hooks/useSearchController.d.ts +17 -0
  231. package/dist/hooks/useSearchResults.d.ts +8 -0
  232. package/dist/hooks/useSetVariationPicker.d.ts +2 -2
  233. package/dist/hooks/useShouldHideCustomEmojis.d.ts +1 -1
  234. package/dist/hooks/useShouldShowSkinTonePicker.d.ts +4 -4
  235. package/dist/index.d.mts +376 -0
  236. package/dist/index.d.ts +376 -9
  237. package/dist/index.js +2 -8
  238. package/dist/primitives/CategoryNav.d.ts +4 -0
  239. package/dist/primitives/Empty.d.ts +6 -0
  240. package/dist/primitives/List.d.ts +4 -0
  241. package/dist/primitives/LoadError.d.ts +8 -0
  242. package/dist/primitives/Loading.d.ts +4 -0
  243. package/dist/primitives/Panel.d.ts +3 -0
  244. package/dist/primitives/Preview.d.ts +3 -0
  245. package/dist/primitives/Reactions.d.ts +2 -0
  246. package/dist/primitives/Root.d.ts +26 -0
  247. package/dist/primitives/Search.d.ts +5 -0
  248. package/dist/primitives/SearchInput.d.ts +3 -0
  249. package/dist/primitives/SkinTone.d.ts +4 -0
  250. package/dist/primitives/Viewport.d.ts +19 -0
  251. package/dist/primitives/appearance.d.ts +3 -0
  252. package/dist/primitives/components.d.ts +27 -0
  253. package/dist/primitives/hooks.d.ts +48 -0
  254. package/dist/primitives/index.d.mts +561 -0
  255. package/dist/primitives/index.d.ts +561 -0
  256. package/dist/primitives/index.js +2 -0
  257. package/dist/primitives/nativeProps.d.ts +15 -0
  258. package/dist/primitives/scope.d.ts +13 -0
  259. package/dist/primitives/structuralStyles.d.ts +16 -0
  260. package/dist/primitives/tokens.d.ts +18 -0
  261. package/dist/primitives/types.d.ts +85 -0
  262. package/dist/state/activeEmojiStore.d.ts +7 -0
  263. package/dist/state/navigationRegistry.d.ts +61 -0
  264. package/dist/state/regionTraversal.d.ts +20 -0
  265. package/dist/typeRefinements/typeRefinements.d.ts +6 -6
  266. package/dist/types/exposedTypes.d.ts +16 -6
  267. package/dist/types/exposedTypes.js +5 -0
  268. package/dist/virtualization/virtualizationHelpers.d.ts +20 -18
  269. package/llms-full.txt +3680 -0
  270. package/llms.txt +66 -476
  271. package/package.json +204 -66
  272. package/src/DomUtils/categoryNameFromDom.ts +1 -1
  273. package/src/DomUtils/classNames.ts +1 -0
  274. package/src/DomUtils/elementPositionInRow.ts +26 -4
  275. package/src/DomUtils/eventBelongsToPicker.ts +17 -0
  276. package/src/DomUtils/getActiveElement.ts +7 -1
  277. package/src/DomUtils/isJsdom.ts +12 -0
  278. package/src/DomUtils/isRtl.ts +5 -0
  279. package/src/DomUtils/keyboardNavigation.ts +105 -149
  280. package/src/DomUtils/scrollTo.ts +18 -16
  281. package/src/DomUtils/selectors.ts +20 -111
  282. package/src/EmojiPickerReact.tsx +155 -48
  283. package/src/Stylesheet/DedupedStyle.tsx +80 -0
  284. package/src/Stylesheet/stylesheet.tsx +128 -59
  285. package/src/components/Layout/Flex.tsx +12 -11
  286. package/src/components/Reactions/BtnPlus.tsx +61 -51
  287. package/src/components/Reactions/Reactions.tsx +113 -55
  288. package/src/components/atoms/Button.tsx +18 -15
  289. package/src/components/body/EmojiCategory.tsx +77 -40
  290. package/src/components/body/EmojiList.tsx +55 -23
  291. package/src/components/body/EmojiVariationPicker.tsx +113 -66
  292. package/src/components/body/MeasureEmoji.tsx +9 -2
  293. package/src/components/body/listComponents.tsx +79 -0
  294. package/src/components/context/ElementRefContext.tsx +46 -35
  295. package/src/components/context/PickerConfigContext.tsx +55 -40
  296. package/src/components/context/PickerContext.tsx +567 -92
  297. package/src/components/context/PickerDataContext.tsx +110 -99
  298. package/src/components/emoji/BaseEmojiProps.ts +5 -4
  299. package/src/components/emoji/ClickableEmojiButton.tsx +148 -63
  300. package/src/components/emoji/Emoji.tsx +24 -2
  301. package/src/components/emoji/EmojiImg.tsx +13 -12
  302. package/src/components/emoji/ExportedEmoji.tsx +5 -4
  303. package/src/components/emoji/NativeEmoji.tsx +19 -16
  304. package/src/components/emoji/ViewOnlyEmoji.tsx +55 -30
  305. package/src/components/emoji/emojiStyles.ts +12 -11
  306. package/src/components/footer/Preview.tsx +86 -91
  307. package/src/components/header/Header.tsx +21 -6
  308. package/src/components/header/Search/BtnClearSearch.tsx +69 -55
  309. package/src/components/header/Search/IcnSearch.tsx +21 -21
  310. package/src/components/header/Search/Search.tsx +77 -137
  311. package/src/components/header/SkinTonePicker/BtnSkinToneVariation.tsx +99 -62
  312. package/src/components/header/SkinTonePicker/SkinTonePicker.tsx +84 -46
  313. package/src/components/icons/svgIcons.ts +17 -0
  314. package/src/components/main/PickerMainBehaviors.tsx +163 -0
  315. package/src/components/main/defaultAppearance.tsx +188 -0
  316. package/src/components/main/labelHeight.ts +5 -0
  317. package/src/components/navigation/CategoryButton.tsx +83 -57
  318. package/src/components/navigation/CategoryNavIcon.tsx +73 -194
  319. package/src/components/navigation/CategoryNavigation.tsx +118 -46
  320. package/src/config/categoryConfig.ts +7 -3
  321. package/src/config/cdnUrls.ts +11 -21
  322. package/src/config/compareConfig.ts +128 -30
  323. package/src/config/config.ts +228 -24
  324. package/src/config/defaultPickerConfiguration.ts +7 -0
  325. package/src/config/mutableConfig.ts +44 -20
  326. package/src/config/useConfig.ts +130 -37
  327. package/src/data/defaultEmojiData.ts +49 -0
  328. package/src/data/registerDefaultEmojiData.ts +8 -0
  329. package/src/data-core/pickerData.ts +173 -0
  330. package/src/data-core/prepare.ts +178 -0
  331. package/src/data-core/search.ts +122 -0
  332. package/src/data-core/types.ts +36 -0
  333. package/src/data.ts +10 -0
  334. package/src/dataUtils/DataTypes.ts +4 -0
  335. package/src/dataUtils/emojiSelectors.ts +58 -63
  336. package/src/dataUtils/emojiUtils.ts +17 -74
  337. package/src/dataUtils/nativeEmojiSequenceSupport.ts +116 -0
  338. package/src/dataUtils/nativeEmojiSupport.ts +160 -0
  339. package/src/dataUtils/suggested.ts +8 -4
  340. package/src/dataUtils/suggestedEmojis.ts +92 -0
  341. package/src/env.d.ts +4 -0
  342. package/src/hooks/preloadEmoji.ts +3 -3
  343. package/src/hooks/useActiveCategoryScrollDetection.ts +10 -1
  344. package/src/hooks/useCategoryHeight.ts +82 -8
  345. package/src/hooks/useDataIdentityStabilityWarning.ts +86 -0
  346. package/src/hooks/useDebouncedState.ts +13 -1
  347. package/src/hooks/useDisallowedEmojis.ts +26 -42
  348. package/src/hooks/useEmojiPreviewEvents.ts +43 -10
  349. package/src/hooks/useEmojiVirtualization.tsx +38 -4
  350. package/src/hooks/useFilter.ts +85 -127
  351. package/src/hooks/useFocus.ts +14 -24
  352. package/src/hooks/useIsEmojiHidden.ts +10 -6
  353. package/src/hooks/useKeyboardNavigation.ts +322 -429
  354. package/src/hooks/useMouseDownHandlers.ts +91 -16
  355. package/src/hooks/useOnFocus.ts +12 -3
  356. package/src/hooks/useOnScroll.ts +18 -2
  357. package/src/hooks/useReactionsFocus.ts +118 -0
  358. package/src/hooks/useRegisterRegion.ts +92 -0
  359. package/src/hooks/useResolvedEmojiData.ts +200 -0
  360. package/src/hooks/useScrollCategoryIntoView.ts +74 -12
  361. package/src/hooks/useSearchController.ts +369 -0
  362. package/src/hooks/useSearchResults.ts +51 -0
  363. package/src/hooks/useSetVariationPicker.ts +4 -1
  364. package/src/index.tsx +67 -5
  365. package/src/primitives/CategoryNav.tsx +40 -0
  366. package/src/primitives/Empty.tsx +68 -0
  367. package/src/primitives/List.tsx +61 -0
  368. package/src/primitives/LoadError.tsx +38 -0
  369. package/src/primitives/Loading.tsx +60 -0
  370. package/src/primitives/Panel.tsx +47 -0
  371. package/src/primitives/Preview.tsx +38 -0
  372. package/src/primitives/Reactions.tsx +2 -0
  373. package/src/primitives/Root.tsx +398 -0
  374. package/src/primitives/Search.tsx +41 -0
  375. package/src/primitives/SearchInput.tsx +110 -0
  376. package/src/primitives/SkinTone.tsx +76 -0
  377. package/src/primitives/Viewport.tsx +204 -0
  378. package/src/primitives/appearance.tsx +7 -0
  379. package/src/primitives/components.tsx +30 -0
  380. package/src/primitives/hooks.ts +165 -0
  381. package/src/primitives/index.ts +94 -0
  382. package/src/primitives/nativeProps.ts +90 -0
  383. package/src/primitives/scope.tsx +109 -0
  384. package/src/primitives/structuralStyles.tsx +133 -0
  385. package/src/primitives/tokens.ts +149 -0
  386. package/src/primitives/types.ts +192 -0
  387. package/src/state/activeEmojiStore.ts +39 -0
  388. package/src/state/navigationRegistry.ts +209 -0
  389. package/src/state/regionTraversal.ts +116 -0
  390. package/src/svg.d.ts +2 -2
  391. package/src/types/exposedTypes.ts +28 -1
  392. package/src/virtualization/virtualizationHelpers.ts +20 -6
  393. package/dist/components/body/Body.d.ts +0 -2
  394. package/dist/components/main/PickerMain.d.ts +0 -7
  395. package/dist/dataUtils/alphaNumericEmojiIndex.d.ts +0 -5
  396. package/dist/emoji-picker-react.cjs.development.js +0 -31413
  397. package/dist/emoji-picker-react.cjs.development.js.map +0 -1
  398. package/dist/emoji-picker-react.cjs.production.min.js +0 -2
  399. package/dist/emoji-picker-react.cjs.production.min.js.map +0 -1
  400. package/dist/emoji-picker-react.esm.js +0 -31411
  401. package/dist/emoji-picker-react.esm.js.map +0 -1
  402. package/dist/hooks/useHideEmojisByUniocode.d.ts +0 -1
  403. package/src/components/body/Body.tsx +0 -43
  404. package/src/components/main/PickerMain.tsx +0 -249
  405. package/src/dataUtils/alphaNumericEmojiIndex.ts +0 -21
@@ -1,24 +1,25 @@
1
1
  import * as React from 'react';
2
2
 
3
- import { usePickerConfig } from '../components/context/PickerConfigContext';
3
+ import {
4
+ usePickerConfig,
5
+ useSearchSliceConfig,
6
+ } from '../components/context/PickerConfigContext';
4
7
  import { useReactionsModeState } from '../components/context/PickerContext';
5
8
  import {
6
9
  EmojiClickData,
7
- EmojiStyle,
10
+ EmojiStyleValue,
8
11
  SkinTonePickerLocation,
9
12
  SkinTones,
10
- SuggestionMode,
11
- Theme,
13
+ SuggestionModeValue,
14
+ ThemeValue,
12
15
  } from '../types/exposedTypes';
13
16
 
14
17
  import { CategoriesConfig } from './categoryConfig';
15
18
  import {
19
+ DEFAULT_LABELS,
16
20
  DEFAULT_SEARCH_PLACEHOLDER,
17
- DEFAULT_SEARCH_CLEAR_BUTTON_LABEL,
18
- SEARCH_RESULTS_NO_RESULTS_FOUND,
19
- SEARCH_RESULTS_ONE_RESULT_FOUND,
20
- SEARCH_RESULTS_MULTIPLE_RESULTS_FOUND,
21
21
  PickerDimensions,
22
+ PickerLabels,
22
23
  PreviewConfig,
23
24
  } from './config';
24
25
  import { CustomEmoji } from './customEmojiConfig';
@@ -29,23 +30,68 @@ export enum MOUSE_EVENT_SOURCE {
29
30
  PICKER = 'picker',
30
31
  }
31
32
 
33
+ /**
34
+ * Resolved user-facing strings: English defaults, then the legacy
35
+ * individual props, then `labels` (highest precedence).
36
+ */
37
+ export function useLabels(): PickerLabels {
38
+ const {
39
+ labels,
40
+ searchPlaceHolder,
41
+ searchPlaceholder,
42
+ searchLabel,
43
+ searchClearButtonLabel,
44
+ } = usePickerConfig();
45
+ return React.useMemo(() => {
46
+ const legacyPlaceholder = [searchPlaceHolder, searchPlaceholder].find(
47
+ (p) => p !== undefined && p !== DEFAULT_SEARCH_PLACEHOLDER,
48
+ );
49
+ const resolved: PickerLabels = { ...DEFAULT_LABELS };
50
+ if (legacyPlaceholder !== undefined) {
51
+ resolved.searchPlaceholder = legacyPlaceholder;
52
+ }
53
+ if (searchLabel !== undefined) {
54
+ resolved.searchLabel = searchLabel;
55
+ }
56
+ if (searchClearButtonLabel !== undefined) {
57
+ resolved.searchClear = searchClearButtonLabel;
58
+ }
59
+ if (labels) {
60
+ for (const key of Object.keys(labels) as Array<keyof PickerLabels>) {
61
+ const value = labels[key];
62
+ if (value !== undefined) {
63
+ resolved[key] = value;
64
+ }
65
+ }
66
+ }
67
+ return resolved;
68
+ }, [
69
+ labels,
70
+ searchPlaceHolder,
71
+ searchPlaceholder,
72
+ searchLabel,
73
+ searchClearButtonLabel,
74
+ ]);
75
+ }
76
+
32
77
  export function useSearchPlaceHolderConfig(): string {
33
- const { searchPlaceHolder, searchPlaceholder } = usePickerConfig();
34
- return (
35
- [searchPlaceHolder, searchPlaceholder].find(
36
- (p) => p !== DEFAULT_SEARCH_PLACEHOLDER,
37
- ) ?? DEFAULT_SEARCH_PLACEHOLDER
38
- );
78
+ return useLabels().searchPlaceholder;
39
79
  }
40
80
 
41
81
  export function useSearchClearButtonLabelConfig(): string {
42
- const { searchClearButtonLabel } = usePickerConfig();
43
- return searchClearButtonLabel ?? DEFAULT_SEARCH_CLEAR_BUTTON_LABEL;
82
+ return useLabels().searchClear;
44
83
  }
45
84
 
85
+ // Literal values are the enum members' own strings, so the public
86
+ // SkinTonesValue narrows to the enum the engine compares against.
46
87
  export function useDefaultSkinToneConfig(): SkinTones {
47
88
  const { defaultSkinTone } = usePickerConfig();
48
- return defaultSkinTone;
89
+ return defaultSkinTone as SkinTones;
90
+ }
91
+
92
+ export function useSkinToneConfig(): SkinTones | undefined {
93
+ const { skinTone } = usePickerConfig();
94
+ return skinTone as SkinTones | undefined;
49
95
  }
50
96
 
51
97
  export function useAllowExpandReactions(): boolean {
@@ -58,7 +104,7 @@ export function useSkinTonesDisabledConfig(): boolean {
58
104
  return skinTonesDisabled;
59
105
  }
60
106
 
61
- export function useEmojiStyleConfig(): EmojiStyle {
107
+ export function useEmojiStyleConfig(): EmojiStyleValue {
62
108
  const { emojiStyle } = usePickerConfig();
63
109
  return emojiStyle;
64
110
  }
@@ -91,23 +137,36 @@ export function useOpenConfig(): boolean {
91
137
  export function useOnEmojiClickConfig(
92
138
  mouseEventSource: MOUSE_EVENT_SOURCE,
93
139
  ): (emoji: EmojiClickData, event: MouseEvent) => void {
140
+ // The ref object is stable; callbacks are read at call time so a
141
+ // subscribed listener always invokes the latest parent callback even
142
+ // when the memoized tree skips rerendering on callback-only updates.
143
+ // Reading them during render would freeze the first-render callback
144
+ // into every listener closure.
94
145
  const { current } = useMutableConfig();
95
146
  const [, setReactionsOpen] = useReactionsModeState();
96
147
 
97
- const handler = current.onEmojiClick || (() => {});
98
- const { onReactionClick } = current;
99
-
100
- if (mouseEventSource === MOUSE_EVENT_SOURCE.REACTIONS && onReactionClick) {
101
- return (...args) =>
102
- onReactionClick(...args, {
148
+ if (mouseEventSource === MOUSE_EVENT_SOURCE.REACTIONS) {
149
+ return (...args) => {
150
+ const onReactionClick = current.onReactionClick;
151
+ if (onReactionClick) {
152
+ return onReactionClick(...args, {
153
+ collapseToReactions: () => {
154
+ setReactionsOpen(true);
155
+ },
156
+ });
157
+ }
158
+ const onEmojiClick = current.onEmojiClick || noop;
159
+ return onEmojiClick(...args, {
103
160
  collapseToReactions: () => {
104
- setReactionsOpen((o) => o);
161
+ setReactionsOpen(true);
105
162
  },
106
163
  });
164
+ };
107
165
  }
108
166
 
109
167
  return (...args) => {
110
- handler(...args, {
168
+ const onEmojiClick = current.onEmojiClick || noop;
169
+ return onEmojiClick(...args, {
111
170
  collapseToReactions: () => {
112
171
  setReactionsOpen(true);
113
172
  },
@@ -118,7 +177,9 @@ export function useOnEmojiClickConfig(
118
177
  export function useOnSkinToneChangeConfig(): (skinTone: SkinTones) => void {
119
178
  const { current } = useMutableConfig();
120
179
 
121
- return current.onSkinToneChange || (() => {});
180
+ return (skinTone: SkinTones) => {
181
+ current.onSkinToneChange?.(skinTone);
182
+ };
122
183
  }
123
184
 
124
185
  export function usePreviewConfig(): PreviewConfig {
@@ -126,13 +187,13 @@ export function usePreviewConfig(): PreviewConfig {
126
187
  return previewConfig;
127
188
  }
128
189
 
129
- export function useThemeConfig(): Theme {
190
+ export function useThemeConfig(): ThemeValue {
130
191
  const { theme } = usePickerConfig();
131
192
 
132
193
  return theme;
133
194
  }
134
195
 
135
- export function useSuggestedEmojisModeConfig(): SuggestionMode {
196
+ export function useSuggestedEmojisModeConfig(): SuggestionModeValue {
136
197
  const { suggestedEmojisMode } = usePickerConfig();
137
198
  return suggestedEmojisMode;
138
199
  }
@@ -157,6 +218,17 @@ export function useReactionsOpenConfig(): boolean {
157
218
  return reactionsDefaultOpen;
158
219
  }
159
220
 
221
+ export function useColumnsConfig(): number | undefined {
222
+ const { columns } = usePickerConfig();
223
+ return validColumns(columns);
224
+ }
225
+
226
+ export function validColumns(columns: unknown): number | undefined {
227
+ return Number.isInteger(columns) && (columns as number) > 0
228
+ ? (columns as number)
229
+ : undefined;
230
+ }
231
+
160
232
  export function useEmojiVersionConfig(): string | null {
161
233
  const { emojiVersion } = usePickerConfig();
162
234
  return emojiVersion;
@@ -184,30 +256,51 @@ export function useReactionsConfig(): string[] {
184
256
 
185
257
  export function useGetEmojiUrlConfig(): (
186
258
  unified: string,
187
- style: EmojiStyle,
259
+ style: EmojiStyleValue,
188
260
  ) => string {
189
261
  const { getEmojiUrl } = usePickerConfig();
190
262
  return getEmojiUrl;
191
263
  }
192
264
 
265
+ export function useSearchValueConfig(): string | undefined {
266
+ const { searchValue } = useSearchSliceConfig();
267
+ return searchValue;
268
+ }
269
+
270
+ export function useDefaultSearchValueConfig(): string | undefined {
271
+ const { defaultSearchValue } = useSearchSliceConfig();
272
+ return defaultSearchValue;
273
+ }
274
+
275
+ export function useSearchLabelConfig(): string {
276
+ return useLabels().searchLabel;
277
+ }
278
+
279
+ export function useSuggestedEmojisConfig(): string[] | undefined {
280
+ const { suggestedEmojis } = usePickerConfig();
281
+ return suggestedEmojis;
282
+ }
283
+
284
+ function noop() {}
285
+
193
286
  function getDimension(dimensionConfig: PickerDimensions): PickerDimensions {
194
287
  return typeof dimensionConfig === 'number'
195
288
  ? `${dimensionConfig}px`
196
289
  : dimensionConfig;
197
290
  }
198
291
 
199
- export function useSearchResultsConfig(searchResultsCount: number): string {
292
+ export function formatSearchResultsLabel(
293
+ labels: PickerLabels,
294
+ searchResultsCount: number,
295
+ ): string {
200
296
  const hasResults = searchResultsCount > 0;
201
297
  const isPlural = searchResultsCount > 1;
202
298
 
203
299
  if (hasResults) {
204
300
  return isPlural
205
- ? SEARCH_RESULTS_MULTIPLE_RESULTS_FOUND.replace(
206
- '%n',
207
- searchResultsCount.toString(),
208
- )
209
- : SEARCH_RESULTS_ONE_RESULT_FOUND;
301
+ ? labels.searchResultsMany.replace('%n', searchResultsCount.toString())
302
+ : labels.searchResultsOne;
210
303
  }
211
304
 
212
- return SEARCH_RESULTS_NO_RESULTS_FOUND;
305
+ return labels.searchResultsNone;
213
306
  }
@@ -0,0 +1,49 @@
1
+ import type { EmojiData } from '../types/exposedTypes';
2
+
3
+ // The bundled English dataset is no longer a static dependency of the
4
+ // picker core. Entry points that promise synchronous data (the default
5
+ // `emoji-picker-react` entry and `emoji-picker-react/data`) register it on
6
+ // import; `emoji-picker-react/primitives` does not, so a lean composition
7
+ // ships without it and loads it on demand (a separate chunk in ESM builds)
8
+ // unless `emojiData` is supplied.
9
+
10
+ let registered: EmojiData | null = null;
11
+
12
+ export function registerDefaultEmojiData(data: EmojiData): void {
13
+ registered = data;
14
+ }
15
+
16
+ export function getRegisteredDefaultEmojiData(): EmojiData | null {
17
+ return registered;
18
+ }
19
+
20
+ /** Stable empty dataset used while data is loading. */
21
+ export const EMPTY_EMOJI_DATA: EmojiData = Object.freeze({
22
+ categories: {},
23
+ emojis: {},
24
+ }) as EmojiData;
25
+
26
+ /** The registered dataset, or the empty placeholder before registration. */
27
+ export function defaultEmojiDataOrEmpty(): EmojiData {
28
+ return registered ?? EMPTY_EMOJI_DATA;
29
+ }
30
+
31
+ let pending: Promise<EmojiData> | null = null;
32
+
33
+ /** Load (once) and register the bundled English dataset. */
34
+ export function loadDefaultEmojiData(): Promise<EmojiData> {
35
+ if (registered) {
36
+ return Promise.resolve(registered);
37
+ }
38
+ if (!pending) {
39
+ pending = import('./emojis').then((module) => {
40
+ const data = (module.default ?? module) as unknown as EmojiData;
41
+ registered = registered ?? data;
42
+ return registered;
43
+ }).catch((error) => {
44
+ pending = null;
45
+ throw error;
46
+ });
47
+ }
48
+ return pending;
49
+ }
@@ -0,0 +1,8 @@
1
+ // Side-effect module: makes the bundled English dataset available
2
+ // synchronously. Imported by entries that guarantee sync data.
3
+ import type { EmojiData } from '../types/exposedTypes';
4
+
5
+ import { registerDefaultEmojiData } from './defaultEmojiData';
6
+ import emojis from './emojis';
7
+
8
+ registerDefaultEmojiData(emojis as unknown as EmojiData);
@@ -0,0 +1,173 @@
1
+ // Shared picker-data derivation.
2
+ //
3
+ // Requirements:
4
+ // - prepare/search-index immutable emoji data outside transient UI state;
5
+ // - cache prepared base data by `emojiData` object identity (WeakMap);
6
+ // - default packaged data shares one prepared base core across Roots;
7
+ // - custom-emoji derivation cached separately by `customEmojis` identity;
8
+ // - `emojiVersion` and `hiddenEmojis` stay per-Root filtering layers and MUST NOT
9
+ // force rebuilding the base index;
10
+ // - caller-provided `emojiData` and `customEmojis` are never mutated;
11
+ // - do not JSON stringify/parse the complete dataset on each Root mount;
12
+ // - no React / ShipStyles imports in this module graph.
13
+
14
+ import type { CustomEmoji } from '../config/customEmojiConfig';
15
+ import { defaultEmojiDataOrEmpty } from '../data/defaultEmojiData';
16
+ import {
17
+ DataEmoji,
18
+ DataEmojis,
19
+ EmojiProperties,
20
+ EmojiProperties as Keys,
21
+ } from '../dataUtils/DataTypes';
22
+ import { Categories, type EmojiData } from '../types/exposedTypes';
23
+
24
+ import { getPreparedCore } from './prepare';
25
+
26
+ export interface PickerDataSnapshot {
27
+ emojiData: EmojiData;
28
+ allEmojis: DataEmojis;
29
+ allEmojisByUnified: Record<string, DataEmoji>;
30
+ customGroups: Record<string, DataEmojis>;
31
+ }
32
+
33
+ interface CustomLayer {
34
+ /** Ungrouped customs: becomes `emojis[CUSTOM]` when customs are present. */
35
+ ungrouped: DataEmojis;
36
+ groups: Record<string, DataEmojis>;
37
+ }
38
+
39
+ const customLayerCache = new WeakMap<object, CustomLayer>();
40
+ const EMPTY_CUSTOM_LAYER: CustomLayer = {
41
+ ungrouped: [],
42
+ groups: Object.create(null),
43
+ };
44
+
45
+ // Outer key: dataset source object. Inner key: customEmojis array identity,
46
+ // or NO_CUSTOM sentinel when no customs are supplied.
47
+ const snapshotCache = new WeakMap<object, WeakMap<object, PickerDataSnapshot>>();
48
+ const NO_CUSTOM: object = {};
49
+
50
+ function customToRegularEmoji(emoji: CustomEmoji): DataEmoji {
51
+ return {
52
+ [EmojiProperties.name]: emoji.names.map((name: string) =>
53
+ name.toLowerCase(),
54
+ ),
55
+ [EmojiProperties.unified]: emoji.id.toLowerCase(),
56
+ [EmojiProperties.added_in]: '0',
57
+ [EmojiProperties.imgUrl]: emoji.imgUrl,
58
+ };
59
+ }
60
+
61
+ function getCustomLayer(customEmojis?: CustomEmoji[]): CustomLayer {
62
+ if (!customEmojis || customEmojis.length === 0) {
63
+ return EMPTY_CUSTOM_LAYER;
64
+ }
65
+ const cached = customLayerCache.get(customEmojis);
66
+ if (cached) {
67
+ return cached;
68
+ }
69
+ // Never mutate caller data: derive fresh objects/arrays only.
70
+ const groups: Record<string, DataEmojis> = Object.create(null);
71
+ const ungrouped: DataEmojis = [];
72
+ for (const emoji of customEmojis) {
73
+ const derived = customToRegularEmoji(emoji);
74
+ if (emoji.group) {
75
+ groups[emoji.group] = groups[emoji.group] ?? [];
76
+ groups[emoji.group].push(derived);
77
+ } else {
78
+ ungrouped.push(derived);
79
+ }
80
+ }
81
+ const layer: CustomLayer = { ungrouped, groups };
82
+ customLayerCache.set(customEmojis, layer);
83
+ return layer;
84
+ }
85
+
86
+ export function getPickerDataSnapshot(
87
+ genericEmojiData?: EmojiData,
88
+ customEmojis?: CustomEmoji[],
89
+ ): PickerDataSnapshot {
90
+ const source = (genericEmojiData ??
91
+ defaultEmojiDataOrEmpty()) as EmojiData;
92
+
93
+ // Warm the shared prepared core the picker actually searches through
94
+ // (queryFilterDict in PickerDataContext). One construction per dataset
95
+ // identity backs the "one base index for 10 same-dataset Roots"
96
+ // invariant; there is no second per-snapshot index.
97
+ getPreparedCore(source);
98
+
99
+ const hasCustoms = !!customEmojis && customEmojis.length > 0;
100
+ const customKey: object = hasCustoms
101
+ ? (customEmojis as object)
102
+ : NO_CUSTOM;
103
+
104
+ let inner = snapshotCache.get(source);
105
+ if (inner) {
106
+ const hit = inner.get(customKey);
107
+ if (hit) {
108
+ return hit;
109
+ }
110
+ } else {
111
+ inner = new WeakMap<object, PickerDataSnapshot>();
112
+ snapshotCache.set(source, inner);
113
+ }
114
+
115
+ const customLayer = getCustomLayer(customEmojis);
116
+
117
+ // Shallow structural copy only: every non-custom category array keeps its
118
+ // shared source reference. No JSON clone of the full dataset, no mutation
119
+ // (and no freezing) of caller-provided data.
120
+ const sourceEmojis = source.emojis ?? {};
121
+ const nextEmojis: Record<string, DataEmoji[]> = { ...sourceEmojis };
122
+ if (hasCustoms) {
123
+ nextEmojis[Categories.CUSTOM] = customLayer.ungrouped as DataEmoji[];
124
+ }
125
+
126
+ const emojiData = {
127
+ ...source,
128
+ emojis: nextEmojis,
129
+ } as EmojiData;
130
+
131
+ const customGroups = customLayer.groups;
132
+
133
+ const allEmojis: DataEmojis = Object.values(nextEmojis)
134
+ .concat(Object.values(customGroups))
135
+ .flat();
136
+
137
+ const allEmojisByUnified: Record<string, DataEmoji> = Object.create(null);
138
+
139
+ allEmojis.forEach((emoji) => {
140
+ const unified = emoji[Keys.unified];
141
+ allEmojisByUnified[unified] = emoji;
142
+
143
+ if (emoji[Keys.variations]) {
144
+ emoji[Keys.variations]?.forEach((variation) => {
145
+ allEmojisByUnified[variation] = emoji;
146
+ });
147
+ }
148
+ });
149
+
150
+ const snapshot: PickerDataSnapshot = {
151
+ emojiData,
152
+ allEmojis,
153
+ allEmojisByUnified,
154
+ customGroups,
155
+ };
156
+ inner.set(customKey, snapshot);
157
+ return snapshot;
158
+ }
159
+
160
+ /** Test-only: expose cache state without breaking WeakMap encapsulation. */
161
+ export function __isSnapshotCached(
162
+ genericEmojiData?: EmojiData,
163
+ customEmojis?: CustomEmoji[],
164
+ ): boolean {
165
+ const source = (genericEmojiData ??
166
+ defaultEmojiDataOrEmpty()) as EmojiData;
167
+ const inner = snapshotCache.get(source);
168
+ if (!inner) {
169
+ return false;
170
+ }
171
+ const hasCustoms = !!customEmojis && customEmojis.length > 0;
172
+ return inner.has(hasCustoms ? (customEmojis as object) : NO_CUSTOM);
173
+ }
@@ -0,0 +1,178 @@
1
+ // Pure immutable prepared-data core for v5.
2
+ //
3
+ // Requirements (see also docs/v5/DATA_API.md):
4
+ // - prepare/search-index immutable emoji data outside transient UI state;
5
+ // - cache prepared base data by `emojiData` object identity (WeakMap);
6
+ // - default packaged data shares one prepared core across Roots;
7
+ // - caller-provided data is never mutated or frozen;
8
+ // - prepared public records are deeply frozen once and safely shared;
9
+ // - no JSON stringify/parse of the full dataset per Root;
10
+ // - no React / ShipStyles imports in this module graph.
11
+
12
+ import { defaultEmojiDataOrEmpty } from '../data/defaultEmojiData';
13
+ import { parseNativeEmoji } from '../dataUtils/parseNativeEmoji';
14
+ import type { EmojiData } from '../types/exposedTypes';
15
+
16
+ import type { EmojiInfo } from './types';
17
+
18
+ export interface PreparedCore {
19
+ /** Stable dataset order of canonical base records. */
20
+ readonly records: readonly EmojiInfo[];
21
+ /** Base unified -> record plus variation unified -> base record. */
22
+ readonly byUnified: ReadonlyMap<string, EmojiInfo>;
23
+ /**
24
+ * Single-character bucket: char -> base records whose joined lowercase
25
+ * names include it, in dataset order. Preserves v4's useful O(1)
26
+ * first-keystroke narrowing on top of the shared immutable core, so
27
+ * cold single-char queries never pay a full scan.
28
+ */
29
+ readonly byChar: ReadonlyMap<string, readonly EmojiInfo[]>;
30
+ /** Per-core query memo: normalized query -> matching records. */
31
+ readonly queryMemo: Map<string, readonly EmojiInfo[]>;
32
+ }
33
+
34
+ const coreCache = new WeakMap<object, PreparedCore>();
35
+
36
+ // Instrumentation for the deterministic multi-root sharing invariant:
37
+ // mounting N Roots against the same dataset identity must construct the
38
+ // base index exactly once. Tests assert on this counter, not on timing.
39
+ let prepareCount = 0;
40
+
41
+ export function __getPrepareCount(): number {
42
+ return prepareCount;
43
+ }
44
+
45
+ export function __resetPrepareCount(): void {
46
+ prepareCount = 0;
47
+ }
48
+
49
+ export function __clearCoreCacheForTest(): void {
50
+ // WeakMap has no clear(); tests that need a cold cache can use a fresh
51
+ // dataset object identity instead. Kept as explicit no-op guard.
52
+ }
53
+
54
+ export function normalizeUnified(input: string): string {
55
+ return input.trim().toLowerCase();
56
+ }
57
+
58
+ export function normalizeQuery(input: string): string {
59
+ if (!input || typeof input !== 'string') {
60
+ return '';
61
+ }
62
+ return input.trim().toLowerCase();
63
+ }
64
+
65
+ function toEmojiInfo(
66
+ unified: string,
67
+ names: readonly string[],
68
+ variations: readonly string[] | undefined,
69
+ addedIn: string,
70
+ ): EmojiInfo {
71
+ return Object.freeze({
72
+ unified,
73
+ emoji: parseNativeEmoji(unified),
74
+ name: names[names.length - 1] || '',
75
+ names: Object.freeze([...names]),
76
+ variations: Object.freeze([...(variations ?? [])]),
77
+ addedIn,
78
+ });
79
+ }
80
+
81
+ function readStringArray(raw: unknown): string[] {
82
+ if (!Array.isArray(raw)) {
83
+ return [];
84
+ }
85
+ return (raw.filter((n) => typeof n === 'string') as string[]).map((n) =>
86
+ n.toLowerCase(),
87
+ );
88
+ }
89
+
90
+ function appendEntry(
91
+ byUnified: Map<string, EmojiInfo>,
92
+ byCharMutable: Map<string, EmojiInfo[]>,
93
+ records: EmojiInfo[],
94
+ entry: unknown,
95
+ ): void {
96
+ // Never mutate caller data: read raw fields defensively.
97
+ const rawUnified = (entry as { u?: unknown }).u;
98
+ if (typeof rawUnified !== 'string' || rawUnified.length === 0) {
99
+ return;
100
+ }
101
+ const unified = rawUnified.toLowerCase();
102
+ if (byUnified.has(unified)) {
103
+ return;
104
+ }
105
+
106
+ const names = readStringArray((entry as { n?: unknown }).n);
107
+ const variations = readStringArray((entry as { v?: unknown }).v);
108
+
109
+ const rawAddedIn = (entry as { a?: unknown }).a;
110
+ const addedIn =
111
+ typeof rawAddedIn === 'string' ? rawAddedIn : String(rawAddedIn ?? '');
112
+
113
+ const info = toEmojiInfo(unified, names, variations, addedIn);
114
+ records.push(info);
115
+ byUnified.set(unified, info);
116
+ for (const variation of info.variations) {
117
+ if (!byUnified.has(variation)) {
118
+ byUnified.set(variation, info);
119
+ }
120
+ }
121
+ indexCharBucket(byCharMutable, names, info);
122
+ }
123
+
124
+ // One bucket entry per distinct character keeps the per-record cost
125
+ // proportional to name length, not name length squared.
126
+ function indexCharBucket(
127
+ byCharMutable: Map<string, EmojiInfo[]>,
128
+ names: readonly string[],
129
+ info: EmojiInfo,
130
+ ): void {
131
+ const distinctChars = Array.from(new Set(names.join('')));
132
+ for (const char of distinctChars) {
133
+ const bucket = byCharMutable.get(char);
134
+ if (bucket) {
135
+ bucket.push(info);
136
+ } else {
137
+ byCharMutable.set(char, [info]);
138
+ }
139
+ }
140
+ }
141
+
142
+ export function getPreparedCore(emojiData?: EmojiData): PreparedCore {
143
+ const source = (emojiData ?? defaultEmojiDataOrEmpty()) as EmojiData;
144
+ const cached = coreCache.get(source);
145
+ if (cached) {
146
+ return cached;
147
+ }
148
+
149
+ prepareCount += 1;
150
+
151
+ const byUnified = new Map<string, EmojiInfo>();
152
+ const byCharMutable = new Map<string, EmojiInfo[]>();
153
+ const records: EmojiInfo[] = [];
154
+
155
+ const groups = source.emojis ?? {};
156
+
157
+ for (const key of Object.keys(groups)) {
158
+ const list = groups[key] ?? [];
159
+ for (const entry of list) {
160
+ appendEntry(byUnified, byCharMutable, records, entry);
161
+ }
162
+ }
163
+
164
+ const byChar = new Map<string, readonly EmojiInfo[]>();
165
+ byCharMutable.forEach((bucket, char) => {
166
+ byChar.set(char, Object.freeze(bucket));
167
+ });
168
+
169
+ const core: PreparedCore = {
170
+ records,
171
+ byUnified,
172
+ byChar,
173
+ queryMemo: new Map<string, readonly EmojiInfo[]>(),
174
+ };
175
+
176
+ coreCache.set(source, core);
177
+ return core;
178
+ }