emoji-picker-react 4.22.3 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +331 -49
- package/dist/DomUtils/categoryNameFromDom.d.ts +1 -1
- package/dist/DomUtils/classNames.d.ts +23 -23
- package/dist/DomUtils/detectEmojyPartiallyBelowFold.d.ts +2 -2
- package/dist/DomUtils/elementPositionInRow.d.ts +5 -5
- package/dist/DomUtils/eventBelongsToPicker.d.ts +2 -0
- package/dist/DomUtils/focusElement.d.ts +5 -5
- package/dist/DomUtils/getActiveElement.d.ts +2 -2
- package/dist/DomUtils/isJsdom.d.ts +1 -0
- package/dist/DomUtils/isRtl.d.ts +1 -0
- package/dist/DomUtils/keyboardNavigation.d.ts +15 -8
- package/dist/DomUtils/scrollTo.d.ts +4 -5
- package/dist/DomUtils/selectors.d.ts +31 -35
- package/dist/EmojiPickerReact.d.ts +5 -5
- package/dist/Stylesheet/DedupedStyle.d.ts +13 -0
- package/dist/Stylesheet/stylesheet.d.ts +40 -37
- package/dist/components/ErrorBoundary.d.ts +15 -15
- package/dist/components/Layout/Absolute.d.ts +8 -8
- package/dist/components/Layout/Flex.d.ts +13 -13
- package/dist/components/Layout/Relative.d.ts +8 -8
- package/dist/components/Layout/Space.d.ts +7 -7
- package/dist/components/Reactions/BtnPlus.d.ts +2 -2
- package/dist/components/Reactions/DEFAULT_REACTIONS.d.ts +1 -1
- package/dist/components/Reactions/Reactions.d.ts +3 -2
- package/dist/components/atoms/Button.d.ts +11 -6
- package/dist/components/body/EmojiCategory.d.ts +13 -12
- package/dist/components/body/EmojiList.d.ts +7 -4
- package/dist/components/body/EmojiVariationPicker.d.ts +2 -2
- package/dist/components/body/MeasureEmoji.d.ts +2 -2
- package/dist/components/body/listComponents.d.ts +47 -0
- package/dist/components/context/ElementRefContext.d.ts +18 -16
- package/dist/components/context/PickerConfigContext.d.ts +14 -9
- package/dist/components/context/PickerContext.d.ts +57 -25
- package/dist/components/context/PickerDataContext.d.ts +23 -17
- package/dist/components/emoji/BaseEmojiProps.d.ts +14 -13
- package/dist/components/emoji/ClickableEmojiButton.d.ts +23 -15
- package/dist/components/emoji/Emoji.d.ts +18 -15
- package/dist/components/emoji/EmojiImg.d.ts +11 -11
- package/dist/components/emoji/ExportedEmoji.d.ts +11 -11
- package/dist/components/emoji/NativeEmoji.d.ts +6 -6
- package/dist/components/emoji/ViewOnlyEmoji.d.ts +3 -3
- package/dist/components/emoji/emojiStyles.d.ts +6 -1
- package/dist/components/footer/Preview.d.ts +7 -7
- package/dist/components/header/Header.d.ts +2 -2
- package/dist/components/header/Search/BtnClearSearch.d.ts +2 -2
- package/dist/components/header/Search/IcnSearch.d.ts +2 -2
- package/dist/components/header/Search/Search.d.ts +11 -3
- package/dist/components/header/SkinTonePicker/BtnSkinToneVariation.d.ts +12 -12
- package/dist/components/header/SkinTonePicker/SkinTonePicker.d.ts +11 -11
- package/dist/components/icons/svgIcons.d.ts +4 -0
- package/dist/components/main/PickerMainBehaviors.d.ts +4 -0
- package/dist/components/main/defaultAppearance.d.ts +12 -0
- package/dist/components/main/labelHeight.d.ts +1 -0
- package/dist/components/navigation/CategoryButton.d.ts +12 -12
- package/dist/components/navigation/CategoryNavIcon.d.ts +4 -4
- package/dist/components/navigation/CategoryNavigation.d.ts +19 -2
- package/dist/config/categoryConfig.d.ts +34 -34
- package/dist/config/cdnUrls.d.ts +2 -2
- package/dist/config/compareConfig.d.ts +10 -2
- package/dist/config/config.d.ts +166 -64
- package/dist/config/customEmojiConfig.d.ts +14 -14
- package/dist/config/defaultPickerConfiguration.d.ts +10 -0
- package/dist/config/mutableConfig.d.ts +32 -10
- package/dist/config/useConfig.d.ts +48 -36
- package/dist/data/defaultEmojiData.d.ts +9 -0
- package/dist/data/defaultEmojiData.js +40 -0
- package/dist/data/emojis-bn.d.mts +39 -0
- package/dist/data/emojis-bn.d.ts +38 -2
- package/dist/data/emojis-bn.js +25 -2
- package/dist/data/emojis-bn.mjs +2 -0
- package/dist/data/emojis-da.d.mts +39 -0
- package/dist/data/emojis-da.d.ts +38 -2
- package/dist/data/emojis-da.js +25 -2
- package/dist/data/emojis-da.mjs +2 -0
- package/dist/data/emojis-de.d.mts +39 -0
- package/dist/data/emojis-de.d.ts +38 -2
- package/dist/data/emojis-de.js +25 -2
- package/dist/data/emojis-de.mjs +2 -0
- package/dist/data/emojis-en-gb.d.mts +39 -0
- package/dist/data/emojis-en-gb.d.ts +38 -2
- package/dist/data/emojis-en-gb.js +25 -2
- package/dist/data/emojis-en-gb.mjs +2 -0
- package/dist/data/emojis-en.d.mts +39 -0
- package/dist/data/emojis-en.d.ts +38 -2
- package/dist/data/emojis-en.js +25 -2
- package/dist/data/emojis-en.mjs +2 -0
- package/dist/data/emojis-es-mx.d.mts +39 -0
- package/dist/data/emojis-es-mx.d.ts +38 -2
- package/dist/data/emojis-es-mx.js +25 -2
- package/dist/data/emojis-es-mx.mjs +2 -0
- package/dist/data/emojis-es.d.mts +39 -0
- package/dist/data/emojis-es.d.ts +38 -2
- package/dist/data/emojis-es.js +25 -2
- package/dist/data/emojis-es.mjs +2 -0
- package/dist/data/emojis-et.d.mts +39 -0
- package/dist/data/emojis-et.d.ts +38 -2
- package/dist/data/emojis-et.js +25 -2
- package/dist/data/emojis-et.mjs +2 -0
- package/dist/data/emojis-fi.d.mts +39 -0
- package/dist/data/emojis-fi.d.ts +38 -2
- package/dist/data/emojis-fi.js +25 -2
- package/dist/data/emojis-fi.mjs +2 -0
- package/dist/data/emojis-fr.d.mts +39 -0
- package/dist/data/emojis-fr.d.ts +38 -2
- package/dist/data/emojis-fr.js +25 -2
- package/dist/data/emojis-fr.mjs +2 -0
- package/dist/data/emojis-hi.d.mts +39 -0
- package/dist/data/emojis-hi.d.ts +38 -2
- package/dist/data/emojis-hi.js +25 -2
- package/dist/data/emojis-hi.mjs +2 -0
- package/dist/data/emojis-hu.d.mts +39 -0
- package/dist/data/emojis-hu.d.ts +38 -2
- package/dist/data/emojis-hu.js +25 -2
- package/dist/data/emojis-hu.mjs +2 -0
- package/dist/data/emojis-it.d.mts +39 -0
- package/dist/data/emojis-it.d.ts +38 -2
- package/dist/data/emojis-it.js +25 -2
- package/dist/data/emojis-it.mjs +2 -0
- package/dist/data/emojis-ja.d.mts +39 -0
- package/dist/data/emojis-ja.d.ts +38 -2
- package/dist/data/emojis-ja.js +25 -2
- package/dist/data/emojis-ja.mjs +2 -0
- package/dist/data/emojis-ko.d.mts +39 -0
- package/dist/data/emojis-ko.d.ts +38 -2
- package/dist/data/emojis-ko.js +25 -2
- package/dist/data/emojis-ko.mjs +2 -0
- package/dist/data/emojis-lt.d.mts +39 -0
- package/dist/data/emojis-lt.d.ts +38 -2
- package/dist/data/emojis-lt.js +25 -2
- package/dist/data/emojis-lt.mjs +2 -0
- package/dist/data/emojis-ms.d.mts +39 -0
- package/dist/data/emojis-ms.d.ts +38 -2
- package/dist/data/emojis-ms.js +25 -2
- package/dist/data/emojis-ms.mjs +2 -0
- package/dist/data/emojis-nb.d.mts +39 -0
- package/dist/data/emojis-nb.d.ts +38 -2
- package/dist/data/emojis-nb.js +25 -2
- package/dist/data/emojis-nb.mjs +2 -0
- package/dist/data/emojis-nl.d.mts +39 -0
- package/dist/data/emojis-nl.d.ts +38 -2
- package/dist/data/emojis-nl.js +25 -2
- package/dist/data/emojis-nl.mjs +2 -0
- package/dist/data/emojis-pl.d.mts +39 -0
- package/dist/data/emojis-pl.d.ts +38 -2
- package/dist/data/emojis-pl.js +25 -2
- package/dist/data/emojis-pl.mjs +2 -0
- package/dist/data/emojis-pt.d.mts +39 -0
- package/dist/data/emojis-pt.d.ts +38 -2
- package/dist/data/emojis-pt.js +25 -2
- package/dist/data/emojis-pt.mjs +2 -0
- package/dist/data/emojis-ru.d.mts +39 -0
- package/dist/data/emojis-ru.d.ts +38 -2
- package/dist/data/emojis-ru.js +25 -2
- package/dist/data/emojis-ru.mjs +2 -0
- package/dist/data/emojis-sv.d.mts +39 -0
- package/dist/data/emojis-sv.d.ts +38 -2
- package/dist/data/emojis-sv.js +25 -2
- package/dist/data/emojis-sv.mjs +2 -0
- package/dist/data/emojis-th.d.mts +39 -0
- package/dist/data/emojis-th.d.ts +38 -2
- package/dist/data/emojis-th.js +25 -2
- package/dist/data/emojis-th.mjs +2 -0
- package/dist/data/emojis-uk.d.mts +39 -0
- package/dist/data/emojis-uk.d.ts +38 -2
- package/dist/data/emojis-uk.js +25 -2
- package/dist/data/emojis-uk.mjs +2 -0
- package/dist/data/emojis-vi.d.mts +39 -0
- package/dist/data/emojis-vi.d.ts +38 -2
- package/dist/data/emojis-vi.js +25 -2
- package/dist/data/emojis-vi.mjs +2 -0
- package/dist/data/emojis-zh-hant.d.mts +39 -0
- package/dist/data/emojis-zh-hant.d.ts +38 -2
- package/dist/data/emojis-zh-hant.js +25 -2
- package/dist/data/emojis-zh-hant.mjs +2 -0
- package/dist/data/emojis-zh.d.mts +39 -0
- package/dist/data/emojis-zh.d.ts +38 -2
- package/dist/data/emojis-zh.js +25 -2
- package/dist/data/emojis-zh.mjs +2 -0
- package/dist/data/index.js +1 -0
- package/dist/data/registerDefaultEmojiData.d.ts +1 -0
- package/dist/data/registerDefaultEmojiData.js +3 -0
- package/dist/data/skinToneVariations.js +2 -2
- package/dist/data/skinToneVariations.ts +8 -7
- package/dist/data-core/pickerData.d.ts +12 -0
- package/dist/data-core/prepare.d.ts +23 -0
- package/dist/data-core/search.d.ts +13 -0
- package/dist/data-core/types.d.ts +23 -0
- package/dist/data.d.mts +72 -0
- package/dist/data.d.ts +72 -0
- package/dist/dataUtils/DataTypes.d.ts +23 -19
- package/dist/dataUtils/emojiSelectors.d.ts +15 -16
- package/dist/dataUtils/emojiUtils.d.ts +1 -12
- package/dist/dataUtils/nativeEmojiSequenceSupport.d.ts +10 -0
- package/dist/dataUtils/nativeEmojiSupport.d.ts +40 -0
- package/dist/dataUtils/parseNativeEmoji.d.ts +1 -1
- package/dist/dataUtils/suggested.d.ts +11 -11
- package/dist/dataUtils/suggestedEmojis.d.ts +7 -0
- package/dist/esm/chunks/chunk-7ZGTUZFB.mjs +8025 -0
- package/dist/esm/chunks/chunk-ACXAQEKG.mjs +149 -0
- package/dist/esm/chunks/chunk-F2KS7DCA.mjs +6735 -0
- package/dist/esm/chunks/chunk-L5W4PFRJ.mjs +14 -0
- package/dist/esm/chunks/chunk-LO4TPPLH.mjs +33 -0
- package/dist/esm/chunks/emojis-6L3GRBAP.mjs +7 -0
- package/dist/esm/data.mjs +11 -0
- package/dist/esm/index.mjs +279 -0
- package/dist/esm/primitives/index.mjs +8 -0
- package/dist/hooks/preloadEmoji.d.ts +5 -9
- package/dist/hooks/useActiveCategoryScrollDetection.d.ts +11 -11
- package/dist/hooks/useCategoryHeight.d.ts +7 -5
- package/dist/hooks/useCloseAllOpenToggles.d.ts +2 -2
- package/dist/hooks/useDataIdentityStabilityWarning.d.ts +13 -0
- package/dist/hooks/useDebouncedState.d.ts +1 -1
- package/dist/hooks/useDisallowMouseMove.d.ts +4 -4
- package/dist/hooks/useDisallowedEmojis.d.ts +2 -3
- package/dist/hooks/useEmojiPreviewEvents.d.ts +3 -3
- package/dist/hooks/useEmojiVirtualization.d.ts +22 -17
- package/dist/hooks/useFilter.d.ts +10 -16
- package/dist/hooks/useFocus.d.ts +3 -3
- package/dist/hooks/useInitialLoad.d.ts +2 -2
- package/dist/hooks/useIsEmojiHidden.d.ts +8 -8
- package/dist/hooks/useIsMounted.d.ts +1 -1
- package/dist/hooks/useIsSearchMode.d.ts +1 -1
- package/dist/hooks/useIsomorphicLayoutEffect.d.ts +2 -2
- package/dist/hooks/useKeyboardNavigation.d.ts +3 -1
- package/dist/hooks/useMouseDownHandlers.d.ts +8 -4
- package/dist/hooks/useOnFocus.d.ts +1 -1
- package/dist/hooks/useOnScroll.d.ts +2 -2
- package/dist/hooks/useReactionsFocus.d.ts +1 -0
- package/dist/hooks/useRegisterRegion.d.ts +24 -0
- package/dist/hooks/useResolvedEmojiData.d.ts +37 -0
- package/dist/hooks/useScrollCategoryIntoView.d.ts +1 -1
- package/dist/hooks/useSearchController.d.ts +17 -0
- package/dist/hooks/useSearchResults.d.ts +8 -0
- package/dist/hooks/useSetVariationPicker.d.ts +2 -2
- package/dist/hooks/useShouldHideCustomEmojis.d.ts +1 -1
- package/dist/hooks/useShouldShowSkinTonePicker.d.ts +4 -4
- package/dist/index.d.mts +376 -0
- package/dist/index.d.ts +376 -9
- package/dist/index.js +2 -8
- package/dist/primitives/CategoryNav.d.ts +4 -0
- package/dist/primitives/Empty.d.ts +6 -0
- package/dist/primitives/List.d.ts +4 -0
- package/dist/primitives/LoadError.d.ts +8 -0
- package/dist/primitives/Loading.d.ts +4 -0
- package/dist/primitives/Panel.d.ts +3 -0
- package/dist/primitives/Preview.d.ts +3 -0
- package/dist/primitives/Reactions.d.ts +2 -0
- package/dist/primitives/Root.d.ts +26 -0
- package/dist/primitives/Search.d.ts +5 -0
- package/dist/primitives/SearchInput.d.ts +3 -0
- package/dist/primitives/SkinTone.d.ts +4 -0
- package/dist/primitives/Viewport.d.ts +19 -0
- package/dist/primitives/appearance.d.ts +3 -0
- package/dist/primitives/components.d.ts +27 -0
- package/dist/primitives/hooks.d.ts +48 -0
- package/dist/primitives/index.d.mts +561 -0
- package/dist/primitives/index.d.ts +561 -0
- package/dist/primitives/index.js +2 -0
- package/dist/primitives/nativeProps.d.ts +15 -0
- package/dist/primitives/scope.d.ts +13 -0
- package/dist/primitives/structuralStyles.d.ts +16 -0
- package/dist/primitives/tokens.d.ts +18 -0
- package/dist/primitives/types.d.ts +85 -0
- package/dist/state/activeEmojiStore.d.ts +7 -0
- package/dist/state/navigationRegistry.d.ts +61 -0
- package/dist/state/regionTraversal.d.ts +20 -0
- package/dist/typeRefinements/typeRefinements.d.ts +6 -6
- package/dist/types/exposedTypes.d.ts +16 -6
- package/dist/types/exposedTypes.js +5 -0
- package/dist/virtualization/virtualizationHelpers.d.ts +22 -18
- package/llms-full.txt +3683 -0
- package/llms.txt +66 -480
- package/package.json +180 -66
- package/src/DomUtils/categoryNameFromDom.ts +1 -1
- package/src/DomUtils/classNames.ts +1 -4
- package/src/DomUtils/elementPositionInRow.ts +28 -6
- package/src/DomUtils/eventBelongsToPicker.ts +17 -0
- package/src/DomUtils/getActiveElement.ts +7 -1
- package/src/DomUtils/isJsdom.ts +12 -0
- package/src/DomUtils/isRtl.ts +5 -0
- package/src/DomUtils/keyboardNavigation.ts +164 -144
- package/src/DomUtils/scrollTo.ts +20 -18
- package/src/DomUtils/selectors.ts +30 -127
- package/src/EmojiPickerReact.tsx +155 -48
- package/src/Stylesheet/DedupedStyle.tsx +80 -0
- package/src/Stylesheet/stylesheet.tsx +128 -59
- package/src/components/Layout/Flex.tsx +12 -11
- package/src/components/Reactions/BtnPlus.tsx +61 -51
- package/src/components/Reactions/Reactions.tsx +113 -55
- package/src/components/atoms/Button.tsx +18 -15
- package/src/components/body/EmojiCategory.tsx +77 -40
- package/src/components/body/EmojiList.tsx +107 -44
- package/src/components/body/EmojiVariationPicker.tsx +113 -66
- package/src/components/body/MeasureEmoji.tsx +9 -2
- package/src/components/body/listComponents.tsx +79 -0
- package/src/components/context/ElementRefContext.tsx +46 -35
- package/src/components/context/PickerConfigContext.tsx +55 -40
- package/src/components/context/PickerContext.tsx +567 -92
- package/src/components/context/PickerDataContext.tsx +114 -99
- package/src/components/emoji/BaseEmojiProps.ts +5 -4
- package/src/components/emoji/ClickableEmojiButton.tsx +148 -63
- package/src/components/emoji/Emoji.tsx +24 -2
- package/src/components/emoji/EmojiImg.tsx +13 -12
- package/src/components/emoji/ExportedEmoji.tsx +5 -4
- package/src/components/emoji/NativeEmoji.tsx +19 -16
- package/src/components/emoji/ViewOnlyEmoji.tsx +55 -30
- package/src/components/emoji/emojiStyles.ts +12 -11
- package/src/components/footer/Preview.tsx +86 -91
- package/src/components/header/Header.tsx +21 -6
- package/src/components/header/Search/BtnClearSearch.tsx +69 -55
- package/src/components/header/Search/IcnSearch.tsx +21 -21
- package/src/components/header/Search/Search.tsx +79 -137
- package/src/components/header/SkinTonePicker/BtnSkinToneVariation.tsx +99 -62
- package/src/components/header/SkinTonePicker/SkinTonePicker.tsx +84 -46
- package/src/components/icons/svgIcons.ts +17 -0
- package/src/components/main/PickerMainBehaviors.tsx +163 -0
- package/src/components/main/defaultAppearance.tsx +189 -0
- package/src/components/main/labelHeight.ts +5 -0
- package/src/components/navigation/CategoryButton.tsx +83 -57
- package/src/components/navigation/CategoryNavIcon.tsx +73 -194
- package/src/components/navigation/CategoryNavigation.tsx +118 -46
- package/src/config/categoryConfig.ts +7 -3
- package/src/config/cdnUrls.ts +11 -21
- package/src/config/compareConfig.ts +128 -30
- package/src/config/config.ts +228 -24
- package/src/config/defaultPickerConfiguration.ts +7 -0
- package/src/config/mutableConfig.ts +44 -20
- package/src/config/useConfig.ts +130 -37
- package/src/data/defaultEmojiData.ts +49 -0
- package/src/data/registerDefaultEmojiData.ts +8 -0
- package/src/data/skinToneVariations.ts +8 -7
- package/src/data-core/pickerData.ts +171 -0
- package/src/data-core/prepare.ts +163 -0
- package/src/data-core/search.ts +122 -0
- package/src/data-core/types.ts +36 -0
- package/src/data.ts +10 -0
- package/src/dataUtils/DataTypes.ts +4 -0
- package/src/dataUtils/emojiSelectors.ts +59 -63
- package/src/dataUtils/emojiUtils.ts +17 -74
- package/src/dataUtils/nativeEmojiSequenceSupport.ts +116 -0
- package/src/dataUtils/nativeEmojiSupport.ts +160 -0
- package/src/dataUtils/parseNativeEmoji.ts +3 -4
- package/src/dataUtils/suggested.ts +8 -4
- package/src/dataUtils/suggestedEmojis.ts +92 -0
- package/src/env.d.ts +4 -0
- package/src/hooks/preloadEmoji.ts +2 -47
- package/src/hooks/useActiveCategoryScrollDetection.ts +28 -16
- package/src/hooks/useCategoryHeight.ts +116 -10
- package/src/hooks/useDataIdentityStabilityWarning.ts +86 -0
- package/src/hooks/useDebouncedState.ts +13 -1
- package/src/hooks/useDisallowedEmojis.ts +31 -42
- package/src/hooks/useEmojiPreviewEvents.ts +43 -10
- package/src/hooks/useEmojiVirtualization.tsx +112 -52
- package/src/hooks/useFilter.ts +92 -132
- package/src/hooks/useFocus.ts +14 -24
- package/src/hooks/useIsEmojiHidden.ts +22 -14
- package/src/hooks/useKeyboardNavigation.ts +412 -432
- package/src/hooks/useMouseDownHandlers.ts +91 -16
- package/src/hooks/useOnFocus.ts +12 -3
- package/src/hooks/useOnScroll.ts +18 -2
- package/src/hooks/useReactionsFocus.ts +118 -0
- package/src/hooks/useRegisterRegion.ts +92 -0
- package/src/hooks/useResolvedEmojiData.ts +200 -0
- package/src/hooks/useScrollCategoryIntoView.ts +74 -12
- package/src/hooks/useSearchController.ts +369 -0
- package/src/hooks/useSearchResults.ts +51 -0
- package/src/hooks/useSetVariationPicker.ts +4 -1
- package/src/index.tsx +67 -5
- package/src/primitives/CategoryNav.tsx +40 -0
- package/src/primitives/Empty.tsx +68 -0
- package/src/primitives/List.tsx +61 -0
- package/src/primitives/LoadError.tsx +38 -0
- package/src/primitives/Loading.tsx +60 -0
- package/src/primitives/Panel.tsx +47 -0
- package/src/primitives/Preview.tsx +38 -0
- package/src/primitives/Reactions.tsx +2 -0
- package/src/primitives/Root.tsx +398 -0
- package/src/primitives/Search.tsx +41 -0
- package/src/primitives/SearchInput.tsx +110 -0
- package/src/primitives/SkinTone.tsx +76 -0
- package/src/primitives/Viewport.tsx +204 -0
- package/src/primitives/appearance.tsx +7 -0
- package/src/primitives/components.tsx +30 -0
- package/src/primitives/hooks.ts +165 -0
- package/src/primitives/index.ts +94 -0
- package/src/primitives/nativeProps.ts +90 -0
- package/src/primitives/scope.tsx +109 -0
- package/src/primitives/structuralStyles.tsx +136 -0
- package/src/primitives/tokens.ts +150 -0
- package/src/primitives/types.ts +192 -0
- package/src/state/activeEmojiStore.ts +39 -0
- package/src/state/navigationRegistry.ts +209 -0
- package/src/state/regionTraversal.ts +116 -0
- package/src/svg.d.ts +2 -2
- package/src/types/exposedTypes.ts +28 -1
- package/src/virtualization/virtualizationHelpers.ts +29 -8
- package/dist/components/body/Body.d.ts +0 -2
- package/dist/components/main/PickerMain.d.ts +0 -7
- package/dist/dataUtils/alphaNumericEmojiIndex.d.ts +0 -5
- package/dist/emoji-picker-react.cjs.development.js +0 -31413
- package/dist/emoji-picker-react.cjs.development.js.map +0 -1
- package/dist/emoji-picker-react.cjs.production.min.js +0 -2
- package/dist/emoji-picker-react.cjs.production.min.js.map +0 -1
- package/dist/emoji-picker-react.esm.js +0 -31411
- package/dist/emoji-picker-react.esm.js.map +0 -1
- package/dist/hooks/useHideEmojisByUniocode.d.ts +0 -1
- package/src/components/body/Body.tsx +0 -43
- package/src/components/main/PickerMain.tsx +0 -249
- package/src/dataUtils/alphaNumericEmojiIndex.ts +0 -21
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Pure v5 data operations shared by the picker and `emoji-picker-react/data`.
|
|
2
|
+
// No React / ShipStyles imports allowed in this module graph.
|
|
3
|
+
|
|
4
|
+
import { getPreparedCore, normalizeQuery, normalizeUnified } from './prepare';
|
|
5
|
+
import type { EmojiDataOptions, EmojiInfo } from './types';
|
|
6
|
+
|
|
7
|
+
export type { EmojiDataOptions, EmojiInfo };
|
|
8
|
+
|
|
9
|
+
const EMPTY_RESULTS: readonly EmojiInfo[] = Object.freeze([]);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Bound on distinct memoized queries per prepared core. The memo is shared
|
|
13
|
+
* process-wide per dataset identity (including server use), so unbounded
|
|
14
|
+
* growth is a leak: evict the least recently used entry past this size.
|
|
15
|
+
* 500 distinct recent queries is generous for interactive sessions while
|
|
16
|
+
* keeping worst-case memory proportional to one result window.
|
|
17
|
+
*/
|
|
18
|
+
const MAX_QUERY_MEMO_SIZE = 500;
|
|
19
|
+
|
|
20
|
+
function memoizeQuery(
|
|
21
|
+
core: ReturnType<typeof getPreparedCore>,
|
|
22
|
+
normalized: string,
|
|
23
|
+
results: readonly EmojiInfo[],
|
|
24
|
+
): void {
|
|
25
|
+
const memo = core.queryMemo;
|
|
26
|
+
if (memo.has(normalized)) {
|
|
27
|
+
memo.delete(normalized);
|
|
28
|
+
} else if (memo.size >= MAX_QUERY_MEMO_SIZE) {
|
|
29
|
+
const oldest = memo.keys().next();
|
|
30
|
+
if (!oldest.done) {
|
|
31
|
+
memo.delete(oldest.value);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
memo.set(normalized, results);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A record whose names contain the query contains every query character,
|
|
39
|
+
* so it sits in each of those characters' buckets. Scanning the smallest
|
|
40
|
+
* one (buckets keep record order) yields exactly the full-scan matches in
|
|
41
|
+
* the same order, for a fraction of the records.
|
|
42
|
+
*/
|
|
43
|
+
function smallestCandidateBucket(
|
|
44
|
+
core: ReturnType<typeof getPreparedCore>,
|
|
45
|
+
normalized: string,
|
|
46
|
+
): readonly EmojiInfo[] {
|
|
47
|
+
let smallest: readonly EmojiInfo[] = core.records;
|
|
48
|
+
// Code points, matching how buckets are keyed (astral chars stay whole).
|
|
49
|
+
for (const char of Array.from(normalized)) {
|
|
50
|
+
const bucket = core.byChar.get(char);
|
|
51
|
+
if (!bucket) {
|
|
52
|
+
return EMPTY_RESULTS;
|
|
53
|
+
}
|
|
54
|
+
if (bucket.length < smallest.length) {
|
|
55
|
+
smallest = bucket;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return smallest;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Lookup by base or variation unified code.
|
|
63
|
+
* Trims/lowercases input, returns the canonical base record or undefined.
|
|
64
|
+
*/
|
|
65
|
+
export function getEmojiByUnified(
|
|
66
|
+
unified: string,
|
|
67
|
+
options?: EmojiDataOptions,
|
|
68
|
+
): EmojiInfo | undefined {
|
|
69
|
+
if (!unified || typeof unified !== 'string') {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
const normalized = normalizeUnified(unified);
|
|
73
|
+
if (!normalized) {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
const core = getPreparedCore(options?.emojiData);
|
|
77
|
+
return core.byUnified.get(normalized);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Dataset search (not a snapshot of one picker instance's visible results).
|
|
82
|
+
* Picker-only layers such as emojiVersion/hiddenEmojis/customEmojis are
|
|
83
|
+
* intentionally not applied here; they remain Root-local filters.
|
|
84
|
+
*/
|
|
85
|
+
export function searchEmojis(
|
|
86
|
+
query: string,
|
|
87
|
+
options?: EmojiDataOptions,
|
|
88
|
+
): readonly EmojiInfo[] {
|
|
89
|
+
const normalized = normalizeQuery(query);
|
|
90
|
+
if (!normalized) {
|
|
91
|
+
return EMPTY_RESULTS;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const core = getPreparedCore(options?.emojiData);
|
|
95
|
+
const memoized = core.queryMemo.get(normalized);
|
|
96
|
+
if (memoized) {
|
|
97
|
+
memoizeQuery(core, normalized, memoized);
|
|
98
|
+
return Object.freeze([...memoized]) as readonly EmojiInfo[];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Single-character queries hit the prepared bucket (same membership as
|
|
102
|
+
// a full scan: joined names contain the char iff some name includes it).
|
|
103
|
+
if (normalized.length === 1) {
|
|
104
|
+
const bucket = core.byChar.get(normalized) ?? EMPTY_RESULTS;
|
|
105
|
+
memoizeQuery(core, normalized, bucket);
|
|
106
|
+
return Object.freeze([...bucket]) as readonly EmojiInfo[];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const matches: EmojiInfo[] = [];
|
|
110
|
+
for (const record of smallestCandidateBucket(core, normalized)) {
|
|
111
|
+
const names = record.names;
|
|
112
|
+
for (let i = 0; i < names.length; i += 1) {
|
|
113
|
+
if (names[i].includes(normalized)) {
|
|
114
|
+
matches.push(record);
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
memoizeQuery(core, normalized, matches);
|
|
121
|
+
return Object.freeze([...matches]) as readonly EmojiInfo[];
|
|
122
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// v5 pure shared data core types.
|
|
2
|
+
//
|
|
3
|
+
// This module is intentionally free of React and ShipStyles imports so the
|
|
4
|
+
// `emoji-picker-react/data` entry can share it without pulling UI runtime.
|
|
5
|
+
|
|
6
|
+
import type { EmojiData } from '../types/exposedTypes';
|
|
7
|
+
|
|
8
|
+
export type { EmojiData };
|
|
9
|
+
|
|
10
|
+
export type EmojiInfo = Readonly<{
|
|
11
|
+
/** Canonical/base lowercase unified code. */
|
|
12
|
+
unified: string;
|
|
13
|
+
|
|
14
|
+
/** The native emoji text for `unified`, ready to insert or render. */
|
|
15
|
+
emoji: string;
|
|
16
|
+
|
|
17
|
+
/** Display name: the dataset's full name (the last of `names`). */
|
|
18
|
+
name: string;
|
|
19
|
+
|
|
20
|
+
/** Search/display names in dataset order. */
|
|
21
|
+
names: readonly string[];
|
|
22
|
+
|
|
23
|
+
/** Lowercase unified codes for supported variations. */
|
|
24
|
+
variations: readonly string[];
|
|
25
|
+
|
|
26
|
+
/** Unicode/emoji version from the dataset. */
|
|
27
|
+
addedIn: string;
|
|
28
|
+
}>;
|
|
29
|
+
|
|
30
|
+
export type EmojiDataOptions = Readonly<{
|
|
31
|
+
/**
|
|
32
|
+
* Optional dataset in the same shape accepted by the picker's emojiData prop.
|
|
33
|
+
* Omit for the packaged default English dataset.
|
|
34
|
+
*/
|
|
35
|
+
emojiData?: EmojiData;
|
|
36
|
+
}>;
|
package/src/data.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Public v5 data entry point: `emoji-picker-react/data`.
|
|
2
|
+
//
|
|
3
|
+
// Additive to the existing top-level `emojiByUnified` export. Presents a
|
|
4
|
+
// normalized, documented shape and shares the pure data core with the picker.
|
|
5
|
+
// Must not import React or ShipStyles (enforced by npm run check:package).
|
|
6
|
+
|
|
7
|
+
import './data/registerDefaultEmojiData';
|
|
8
|
+
|
|
9
|
+
export { getEmojiByUnified, searchEmojis } from './data-core/search';
|
|
10
|
+
export type { EmojiData, EmojiDataOptions, EmojiInfo } from './data-core/types';
|
|
@@ -9,6 +9,10 @@ export enum EmojiProperties {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export interface DataEmoji extends WithName {
|
|
12
|
+
/** Exact rendered identity; canonical unified remains the search identity. */
|
|
13
|
+
renderUnified?: string;
|
|
14
|
+
/** Stored recent identity when the active tone has no matching variation. */
|
|
15
|
+
fallbackUnified?: string;
|
|
12
16
|
[EmojiProperties.unified]: string;
|
|
13
17
|
[EmojiProperties.variations]?: string[];
|
|
14
18
|
[EmojiProperties.added_in]: string;
|
|
@@ -2,24 +2,21 @@
|
|
|
2
2
|
// imports no React components, hooks, or context providers so it can never
|
|
3
3
|
// form an import cycle with them. Context-aware lookups live in
|
|
4
4
|
// components/context/PickerDataContext instead.
|
|
5
|
-
import { Categories } from '../config/categoryConfig';
|
|
6
5
|
import { cdnUrl } from '../config/cdnUrls';
|
|
7
|
-
import {
|
|
8
|
-
import emojis from '../data/emojis';
|
|
6
|
+
import { getRegisteredDefaultEmojiData } from '../data/defaultEmojiData';
|
|
9
7
|
import skinToneVariations, {
|
|
10
8
|
skinTonesMapped,
|
|
11
9
|
} from '../data/skinToneVariations';
|
|
12
|
-
import {
|
|
10
|
+
import { EmojiData, EmojiStyleValue, SkinTones } from '../types/exposedTypes';
|
|
13
11
|
|
|
14
|
-
import { DataEmoji,
|
|
15
|
-
import { indexEmoji } from './alphaNumericEmojiIndex';
|
|
12
|
+
import { DataEmoji, EmojiProperties, WithName } from './DataTypes';
|
|
16
13
|
|
|
17
14
|
export function emojiNames(emoji: WithName): string[] {
|
|
18
15
|
return emoji[EmojiProperties.name] ?? [];
|
|
19
16
|
}
|
|
20
17
|
|
|
21
18
|
export function addedIn(emoji: DataEmoji): number {
|
|
22
|
-
return parseFloat(emoji[EmojiProperties.added_in]);
|
|
19
|
+
return parseFloat(emoji[EmojiProperties.added_in] || '0');
|
|
23
20
|
}
|
|
24
21
|
|
|
25
22
|
export function emojiName(emoji?: WithName): string {
|
|
@@ -42,20 +39,23 @@ export function unifiedWithoutSkinTone(unified: string): string {
|
|
|
42
39
|
return unified;
|
|
43
40
|
}
|
|
44
41
|
|
|
45
|
-
export function
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (!skinTone || !emojiHasVariations(emoji)) {
|
|
49
|
-
return unified;
|
|
50
|
-
}
|
|
42
|
+
export function emojiCanonicalUnified(emoji: DataEmoji): string {
|
|
43
|
+
return emoji[EmojiProperties.unified];
|
|
44
|
+
}
|
|
51
45
|
|
|
52
|
-
|
|
46
|
+
export function emojiUnified(emoji: DataEmoji, skinTone?: string): string {
|
|
47
|
+
return (
|
|
48
|
+
emoji.renderUnified ??
|
|
49
|
+
(skinTone ? emojiVariationUnified(emoji, skinTone) : undefined) ??
|
|
50
|
+
emoji.fallbackUnified ??
|
|
51
|
+
emojiCanonicalUnified(emoji)
|
|
52
|
+
);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
// WARNING: DO NOT USE DIRECTLY
|
|
56
56
|
export function emojiUrlByUnified(
|
|
57
57
|
unified: string,
|
|
58
|
-
emojiStyle:
|
|
58
|
+
emojiStyle: EmojiStyleValue,
|
|
59
59
|
): string {
|
|
60
60
|
return `${cdnUrl(emojiStyle)}${unified}.png`;
|
|
61
61
|
}
|
|
@@ -77,67 +77,63 @@ export function emojiVariationUnified(
|
|
|
77
77
|
: emojiUnified(emoji);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
// Legacy global lookup (the public top-level `emojiByUnified` export) over
|
|
81
|
+
// the registered default dataset. Indexed lazily on first use and again
|
|
82
|
+
// only if the registered dataset changes, so importing this module costs
|
|
83
|
+
// nothing and does not pull the dataset into a bundle.
|
|
84
|
+
let indexedSource: EmojiData | null = null;
|
|
85
|
+
let allEmojisByUnified: Record<string, DataEmoji> =
|
|
86
|
+
/* @__PURE__ */ Object.create(null);
|
|
87
|
+
|
|
88
|
+
function registryIndex(): Record<string, DataEmoji> {
|
|
89
|
+
const source = getRegisteredDefaultEmojiData();
|
|
90
|
+
if (source === indexedSource) {
|
|
91
|
+
return allEmojisByUnified;
|
|
92
|
+
}
|
|
93
|
+
indexedSource = source;
|
|
94
|
+
allEmojisByUnified = Object.create(null);
|
|
95
|
+
const groups = source?.emojis ?? {};
|
|
96
|
+
for (const group of Object.keys(groups)) {
|
|
97
|
+
for (const emoji of groups[group]) {
|
|
98
|
+
allEmojisByUnified[emojiUnified(emoji)] = emoji;
|
|
99
|
+
emojiVariations(emoji).forEach((variation) => {
|
|
100
|
+
allEmojisByUnified[variation] = emoji;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return allEmojisByUnified;
|
|
105
|
+
}
|
|
106
|
+
|
|
80
107
|
export function emojiByUnified(unified?: string): DataEmoji | undefined {
|
|
81
108
|
if (!unified) {
|
|
82
109
|
return;
|
|
83
110
|
}
|
|
84
111
|
|
|
85
|
-
|
|
86
|
-
|
|
112
|
+
const index = registryIndex();
|
|
113
|
+
if (index[unified]) {
|
|
114
|
+
return index[unified];
|
|
87
115
|
}
|
|
88
116
|
|
|
89
117
|
const withoutSkinTone = unifiedWithoutSkinTone(unified);
|
|
90
|
-
return
|
|
118
|
+
return index[withoutSkinTone];
|
|
91
119
|
}
|
|
92
120
|
|
|
93
|
-
export const allEmojis: DataEmojis = Object.values(emojis.emojis).flat();
|
|
94
|
-
|
|
95
|
-
export function setCustomEmojis(customEmojis: CustomEmoji[]): void {
|
|
96
|
-
emojis.emojis[Categories.CUSTOM].length = 0;
|
|
97
|
-
|
|
98
|
-
customEmojis.forEach((emoji) => {
|
|
99
|
-
const emojiData = customToRegularEmoji(emoji);
|
|
100
|
-
|
|
101
|
-
emojis.emojis[Categories.CUSTOM].push(emojiData as never);
|
|
102
|
-
|
|
103
|
-
if (allEmojisByUnified[emojiData[EmojiProperties.unified]]) {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
allEmojis.push(emojiData);
|
|
108
|
-
allEmojisByUnified[emojiData[EmojiProperties.unified]] = emojiData;
|
|
109
|
-
indexEmoji(emojiData);
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function customToRegularEmoji(emoji: CustomEmoji): DataEmoji {
|
|
114
|
-
return {
|
|
115
|
-
[EmojiProperties.name]: emoji.names.map((name) => name.toLowerCase()),
|
|
116
|
-
[EmojiProperties.unified]: emoji.id.toLowerCase(),
|
|
117
|
-
[EmojiProperties.added_in]: '0',
|
|
118
|
-
[EmojiProperties.imgUrl]: emoji.imgUrl,
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const allEmojisByUnified: {
|
|
123
|
-
[unified: string]: DataEmoji;
|
|
124
|
-
} = {};
|
|
125
|
-
|
|
126
|
-
allEmojis.reduce((allEmojis, Emoji) => {
|
|
127
|
-
allEmojis[emojiUnified(Emoji)] = Emoji;
|
|
128
|
-
|
|
129
|
-
if (emojiHasVariations(Emoji)) {
|
|
130
|
-
emojiVariations(Emoji).forEach((variation) => {
|
|
131
|
-
allEmojis[variation] = Emoji;
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return allEmojis;
|
|
136
|
-
}, allEmojisByUnified);
|
|
137
|
-
|
|
138
121
|
export function activeVariationFromUnified(unified: string): SkinTones | null {
|
|
139
122
|
const [, suspectedSkinTone] = unified.split('-') as [string, SkinTones];
|
|
140
123
|
return skinToneVariations.includes(suspectedSkinTone)
|
|
141
124
|
? suspectedSkinTone
|
|
142
125
|
: null;
|
|
143
126
|
}
|
|
127
|
+
|
|
128
|
+
// A button's exact identity is authoritative, including an explicit neutral
|
|
129
|
+
// variation. Preview metadata and selection share this resolution.
|
|
130
|
+
export function skinToneFromEmoji(
|
|
131
|
+
emoji: DataEmoji,
|
|
132
|
+
unified: string,
|
|
133
|
+
activeTone: SkinTones,
|
|
134
|
+
): SkinTones {
|
|
135
|
+
return (
|
|
136
|
+
activeVariationFromUnified(unified) ??
|
|
137
|
+
(emojiHasVariations(emoji) ? SkinTones.NEUTRAL : activeTone)
|
|
138
|
+
);
|
|
139
|
+
}
|
|
@@ -1,74 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const names = emojiNames(emoji);
|
|
21
|
-
return names[names.length - 1];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function unifiedWithoutSkinTone(unified: string): string {
|
|
25
|
-
const splat = unified.split('-');
|
|
26
|
-
const [skinTone] = splat.splice(1, 1);
|
|
27
|
-
|
|
28
|
-
if (skinTonesMapped[skinTone]) {
|
|
29
|
-
return splat.join('-');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return unified;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function emojiUnified(emoji: DataEmoji, skinTone?: string): string {
|
|
36
|
-
const unified = emoji[EmojiProperties.unified];
|
|
37
|
-
|
|
38
|
-
if (!skinTone || !emojiHasVariations(emoji)) {
|
|
39
|
-
return unified;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return emojiVariationUnified(emoji, skinTone) ?? unified;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function emojiUrlByUnified(
|
|
46
|
-
unified: string,
|
|
47
|
-
emojiStyle: EmojiStyle,
|
|
48
|
-
): string {
|
|
49
|
-
return `${cdnUrl(emojiStyle)}${unified}.png`;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function emojiVariations(emoji: DataEmoji): string[] {
|
|
53
|
-
return emoji[EmojiProperties.variations] ?? [];
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function emojiHasVariations(emoji: DataEmoji): boolean {
|
|
57
|
-
return emojiVariations(emoji).length > 0;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function emojiVariationUnified(
|
|
61
|
-
emoji: DataEmoji,
|
|
62
|
-
skinTone?: string,
|
|
63
|
-
): string | undefined {
|
|
64
|
-
return skinTone
|
|
65
|
-
? emojiVariations(emoji).find((variation) => variation.includes(skinTone))
|
|
66
|
-
: emojiUnified(emoji);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function activeVariationFromUnified(unified: string): SkinTones | null {
|
|
70
|
-
const [, suspectedSkinTone] = unified.split('-') as [string, SkinTones];
|
|
71
|
-
return Object.keys(skinTonesMapped).includes(suspectedSkinTone)
|
|
72
|
-
? suspectedSkinTone
|
|
73
|
-
: null;
|
|
74
|
-
}
|
|
1
|
+
// Compatibility surface for the pure emoji helpers. The implementation lives
|
|
2
|
+
// in ./emojiSelectors (one copy in the bundle); this module only re-exports
|
|
3
|
+
// it so the many existing import sites keep working.
|
|
4
|
+
export {
|
|
5
|
+
activeVariationFromUnified,
|
|
6
|
+
skinToneFromEmoji,
|
|
7
|
+
addedIn,
|
|
8
|
+
emojiHasVariations,
|
|
9
|
+
emojiName,
|
|
10
|
+
emojiNames,
|
|
11
|
+
emojiUnified,
|
|
12
|
+
emojiCanonicalUnified,
|
|
13
|
+
emojiUrlByUnified,
|
|
14
|
+
emojiVariations,
|
|
15
|
+
emojiVariationUnified,
|
|
16
|
+
unifiedWithoutSkinTone,
|
|
17
|
+
} from './emojiSelectors';
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { parseNativeEmoji } from './parseNativeEmoji';
|
|
2
|
+
|
|
3
|
+
const CELL = 32;
|
|
4
|
+
export const NATIVE_PROBE_BATCH = 64;
|
|
5
|
+
type Candidate = [unified: string, text: string, comparison: string];
|
|
6
|
+
|
|
7
|
+
/** One reusable canvas and result cache per detected document/font. */
|
|
8
|
+
export function createNativeEmojiSequenceSupport(
|
|
9
|
+
ctx: CanvasRenderingContext2D,
|
|
10
|
+
baselineWidth: number,
|
|
11
|
+
) {
|
|
12
|
+
const results = new Map<string, boolean | null>();
|
|
13
|
+
const font = ctx.font;
|
|
14
|
+
|
|
15
|
+
function prime(ids: Iterable<string>): void {
|
|
16
|
+
const candidates: Candidate[] = [];
|
|
17
|
+
for (const unified of ids) {
|
|
18
|
+
if (results.has(unified)) continue;
|
|
19
|
+
// Also deduplicates identities queued in this synchronous batch.
|
|
20
|
+
results.set(unified, null);
|
|
21
|
+
try {
|
|
22
|
+
const text = parseNativeEmoji(unified);
|
|
23
|
+
const width = ctx.measureText(text).width;
|
|
24
|
+
// Uncombined joins, modifiers and flags occupy multiple advances.
|
|
25
|
+
if (width <= 0 || width >= baselineWidth * 1.5) {
|
|
26
|
+
results.set(unified, false);
|
|
27
|
+
} else {
|
|
28
|
+
candidates.push([
|
|
29
|
+
unified,
|
|
30
|
+
text,
|
|
31
|
+
text.replace(/[\u{e0020}-\u{e007f}]/gu, ''),
|
|
32
|
+
]);
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
// Invalid identities/restricted rendering stay inconclusive.
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
for (
|
|
39
|
+
let offset = 0;
|
|
40
|
+
offset < candidates.length;
|
|
41
|
+
offset += NATIVE_PROBE_BATCH
|
|
42
|
+
) {
|
|
43
|
+
probe(candidates.slice(offset, offset + NATIVE_PROBE_BATCH));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function draw(text: string, y: number, ink: string): void {
|
|
48
|
+
// Glyph overhang must not contaminate a neighboring cell's pixels.
|
|
49
|
+
ctx.save();
|
|
50
|
+
ctx.beginPath();
|
|
51
|
+
ctx.rect(0, y, CELL, CELL);
|
|
52
|
+
ctx.clip();
|
|
53
|
+
ctx.fillStyle = ink;
|
|
54
|
+
ctx.fillText(text, 0, y);
|
|
55
|
+
ctx.restore();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function probe(candidates: Candidate[]): void {
|
|
59
|
+
try {
|
|
60
|
+
const height = candidates.length * CELL;
|
|
61
|
+
ctx.canvas.width = CELL;
|
|
62
|
+
ctx.canvas.height = height * 2;
|
|
63
|
+
ctx.font = font;
|
|
64
|
+
ctx.textBaseline = 'top';
|
|
65
|
+
candidates.forEach(([, text, comparison], index) => {
|
|
66
|
+
const y = index * CELL;
|
|
67
|
+
draw(text, y, '#000');
|
|
68
|
+
draw(comparison, y + height, text === comparison ? '#f00' : '#000');
|
|
69
|
+
});
|
|
70
|
+
// One readback per bounded atlas instead of one per sequence.
|
|
71
|
+
const pixels = ctx.getImageData(0, 0, CELL, height * 2).data;
|
|
72
|
+
const artwork = new Uint32Array(
|
|
73
|
+
pixels.buffer,
|
|
74
|
+
pixels.byteOffset,
|
|
75
|
+
pixels.length / 4,
|
|
76
|
+
);
|
|
77
|
+
candidates.forEach(([unified, text, comparison], index) => {
|
|
78
|
+
const start = index * CELL * CELL;
|
|
79
|
+
let alpha = 0;
|
|
80
|
+
let difference = 0;
|
|
81
|
+
for (let pixel = start; pixel < start + CELL * CELL; pixel++) {
|
|
82
|
+
alpha |= pixels[pixel * 4 + 3];
|
|
83
|
+
difference |= artwork[pixel] ^ artwork[pixel + CELL * height];
|
|
84
|
+
}
|
|
85
|
+
// Ordinary emoji artwork must ignore text ink (including grayscale
|
|
86
|
+
// artwork). A subdivision flag must differ from its tagless black
|
|
87
|
+
// flag fallback; unsupported tag sequences silently draw that flag.
|
|
88
|
+
results.set(
|
|
89
|
+
unified,
|
|
90
|
+
alpha !== 0 &&
|
|
91
|
+
(text === comparison ? difference === 0 : difference !== 0),
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
} catch {
|
|
95
|
+
// A blocked/unavailable readback must not empty the picker.
|
|
96
|
+
for (const [unified] of candidates) results.set(unified, null);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
prime,
|
|
102
|
+
supports(unified: string): boolean | null {
|
|
103
|
+
// Direct callers get an answer for any identity. Pickers read
|
|
104
|
+
// snapshots instead, so rendering never draws on the canvas.
|
|
105
|
+
if (!results.has(unified)) prime([unified]);
|
|
106
|
+
return results.get(unified) as boolean | null;
|
|
107
|
+
},
|
|
108
|
+
/** Whether an identity was probed (results may still be null). */
|
|
109
|
+
has: (unified: string): boolean => results.has(unified),
|
|
110
|
+
/** Frozen results so far. Unprobed identities read as undefined. */
|
|
111
|
+
snapshot(): (unified: string) => boolean | null | undefined {
|
|
112
|
+
const frozen = new Map(results);
|
|
113
|
+
return (unified) => frozen.get(unified);
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|