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.
- package/README.md +331 -49
- package/dist/DomUtils/categoryNameFromDom.d.ts +1 -1
- package/dist/DomUtils/classNames.d.ts +24 -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 +7 -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-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-46BUDQLE.mjs +228 -0
- package/dist/esm/chunks/chunk-7ZGTUZFB.mjs +16 -0
- package/dist/esm/chunks/chunk-FQIJ4HBW.mjs +25 -0
- package/dist/esm/chunks/chunk-KQPOIRSC.mjs +9305 -0
- package/dist/esm/chunks/chunk-LO4TPPLH.mjs +43 -0
- package/dist/esm/chunks/emojis-6L3GRBAP.mjs +9 -0
- package/dist/esm/data.mjs +18 -0
- package/dist/esm/index.mjs +389 -0
- package/dist/esm/primitives/index.mjs +67 -0
- package/dist/hooks/preloadEmoji.d.ts +9 -9
- package/dist/hooks/useActiveCategoryScrollDetection.d.ts +11 -11
- package/dist/hooks/useCategoryHeight.d.ts +6 -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 +21 -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 +20 -18
- package/llms-full.txt +3680 -0
- package/llms.txt +66 -480
- package/package.json +180 -66
- package/src/DomUtils/categoryNameFromDom.ts +1 -1
- package/src/DomUtils/classNames.ts +1 -0
- package/src/DomUtils/elementPositionInRow.ts +26 -4
- 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 +105 -149
- package/src/DomUtils/scrollTo.ts +18 -16
- package/src/DomUtils/selectors.ts +20 -111
- 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 +55 -23
- 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 +110 -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 +77 -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 +188 -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-core/pickerData.ts +173 -0
- package/src/data-core/prepare.ts +178 -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 +58 -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/suggested.ts +8 -4
- package/src/dataUtils/suggestedEmojis.ts +92 -0
- package/src/env.d.ts +4 -0
- package/src/hooks/preloadEmoji.ts +3 -3
- package/src/hooks/useActiveCategoryScrollDetection.ts +10 -1
- package/src/hooks/useCategoryHeight.ts +82 -8
- package/src/hooks/useDataIdentityStabilityWarning.ts +86 -0
- package/src/hooks/useDebouncedState.ts +13 -1
- package/src/hooks/useDisallowedEmojis.ts +26 -42
- package/src/hooks/useEmojiPreviewEvents.ts +43 -10
- package/src/hooks/useEmojiVirtualization.tsx +38 -4
- package/src/hooks/useFilter.ts +85 -127
- package/src/hooks/useFocus.ts +14 -24
- package/src/hooks/useIsEmojiHidden.ts +10 -6
- package/src/hooks/useKeyboardNavigation.ts +322 -429
- 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 +133 -0
- package/src/primitives/tokens.ts +149 -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 +20 -6
- 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,160 @@
|
|
|
1
|
+
import { isJsdom } from '../DomUtils/isJsdom';
|
|
2
|
+
|
|
3
|
+
import { createNativeEmojiSequenceSupport } from './nativeEmojiSequenceSupport';
|
|
4
|
+
|
|
5
|
+
// Native emoji support detection.
|
|
6
|
+
//
|
|
7
|
+
// The native emoji style renders with the platform's emoji font, which lags
|
|
8
|
+
// behind the dataset: an emoji newer than the OS font draws as a blank box
|
|
9
|
+
// ("tofu") or decomposes into several glyphs, and Windows has no country
|
|
10
|
+
// flag glyphs at all. The picker probes the platform on the client and
|
|
11
|
+
// refreshes after emoji font changes or loading. SSR output is unaffected;
|
|
12
|
+
// detected unsupported glyphs are hidden.
|
|
13
|
+
//
|
|
14
|
+
// Detection runs in two phases so it never stalls the first paint.
|
|
15
|
+
// Drawing every sequence costs 0.2-0.8 s of main-thread time, depending
|
|
16
|
+
// on the browser and font, so before paint the picker samples one glyph
|
|
17
|
+
// per emoji version and a country flag, and predicts from that. Sequences
|
|
18
|
+
// about to be shown (cells near the viewport, their skin tones and the
|
|
19
|
+
// reactions) are then checked exactly in cached pixel batches between
|
|
20
|
+
// tasks. Exact results replace predictions: one version sample cannot
|
|
21
|
+
// prove font coverage.
|
|
22
|
+
|
|
23
|
+
export const DEFAULT_NATIVE_EMOJI_FONT =
|
|
24
|
+
'"Segoe UI Emoji", "Segoe UI Symbol", "Segoe UI", "Apple Color Emoji", "Twemoji Mozilla", "Noto Color Emoji", "EmojiOne Color", "Android Emoji"';
|
|
25
|
+
|
|
26
|
+
const BASELINE_EMOJI = '\u{1F600}'; // grinning face (Emoji 1.0)
|
|
27
|
+
|
|
28
|
+
const FLAG_SAMPLE = '1f1fa-1f1f8'; // regional indicators U + S
|
|
29
|
+
|
|
30
|
+
export type NativeEmojiProbe = ReturnType<
|
|
31
|
+
typeof createNativeEmojiSequenceSupport
|
|
32
|
+
>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Platform support as filtering reads it. `supports` returns a probed
|
|
36
|
+
* identity's exact result (null when inconclusive, undefined when not
|
|
37
|
+
* probed yet); the version and flag fields predict the rest.
|
|
38
|
+
*/
|
|
39
|
+
export type NativeEmojiSupport = {
|
|
40
|
+
supports: (unified: string) => boolean | null | undefined;
|
|
41
|
+
/** Emoji versions whose sample the font cannot draw. */
|
|
42
|
+
failedVersions: Set<number>;
|
|
43
|
+
countryFlags: boolean;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const UNKNOWN_SUPPORT = {};
|
|
47
|
+
|
|
48
|
+
let cache = new WeakMap<
|
|
49
|
+
Document,
|
|
50
|
+
Map<string, NativeEmojiProbe | typeof UNKNOWN_SUPPORT>
|
|
51
|
+
>();
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Probes the font synchronously, sampling versions and flags only.
|
|
55
|
+
* Exact sequence results accumulate on the returned probe through
|
|
56
|
+
* `prime`, which pickers call in background batches.
|
|
57
|
+
*/
|
|
58
|
+
export function detectNativeEmojiSupport(
|
|
59
|
+
fontFamily: string = DEFAULT_NATIVE_EMOJI_FONT,
|
|
60
|
+
refresh = false,
|
|
61
|
+
ownerDocument: Document | undefined = typeof document === 'undefined'
|
|
62
|
+
? undefined
|
|
63
|
+
: document,
|
|
64
|
+
): NativeEmojiProbe | typeof UNKNOWN_SUPPORT {
|
|
65
|
+
if (!ownerDocument) return UNKNOWN_SUPPORT;
|
|
66
|
+
const fonts =
|
|
67
|
+
cache.get(ownerDocument) ||
|
|
68
|
+
new Map<string, NativeEmojiProbe | typeof UNKNOWN_SUPPORT>();
|
|
69
|
+
cache.set(ownerDocument, fonts);
|
|
70
|
+
if (refresh) fonts.delete(fontFamily);
|
|
71
|
+
const cached = fonts.get(fontFamily);
|
|
72
|
+
if (cached) {
|
|
73
|
+
return cached;
|
|
74
|
+
}
|
|
75
|
+
const result = probe(fontFamily, ownerDocument);
|
|
76
|
+
// Bound retained font caches even when an application switches themes
|
|
77
|
+
// through many dynamically named families. Mounted Roots retain theirs.
|
|
78
|
+
if (fonts.size >= 16) fonts.clear();
|
|
79
|
+
fonts.set(fontFamily, result);
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Whether a rendered identity should show. An exact result wins; until
|
|
85
|
+
* one exists, flags and emojis of a version whose sample failed are
|
|
86
|
+
* predicted unsupported. `addedIn` is the emoji's version, when known.
|
|
87
|
+
*/
|
|
88
|
+
export function isNativeEmojiSupported(
|
|
89
|
+
support: NativeEmojiSupport | null,
|
|
90
|
+
unified: string,
|
|
91
|
+
addedIn = 0,
|
|
92
|
+
): boolean {
|
|
93
|
+
const exact = support && support.supports(unified);
|
|
94
|
+
return typeof exact === 'boolean'
|
|
95
|
+
? exact
|
|
96
|
+
: !support ||
|
|
97
|
+
!(
|
|
98
|
+
support.failedVersions.has(addedIn) ||
|
|
99
|
+
(!support.countryFlags && isCountryFlagUnified(unified))
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** The picker's view of a probe: frozen results, never drawing. */
|
|
104
|
+
/**
|
|
105
|
+
* The picker's view of a probe: frozen results, never drawing, plus a
|
|
106
|
+
* prediction for identities not checked yet. `samples` maps each emoji
|
|
107
|
+
* version to one emoji introduced in it. Versions are judged one by one:
|
|
108
|
+
* fonts often draw a newer version's single glyphs but not an older
|
|
109
|
+
* version's sequences.
|
|
110
|
+
*/
|
|
111
|
+
export function nativeSupportSnapshot(
|
|
112
|
+
probe: NativeEmojiProbe,
|
|
113
|
+
samples: Map<number, string>,
|
|
114
|
+
): NativeEmojiSupport {
|
|
115
|
+
probe.prime([FLAG_SAMPLE, ...samples.values()]);
|
|
116
|
+
const failedVersions = new Set<number>();
|
|
117
|
+
samples.forEach((unified, version) => {
|
|
118
|
+
if (probe.supports(unified) === false) failedVersions.add(version);
|
|
119
|
+
});
|
|
120
|
+
return {
|
|
121
|
+
supports: probe.snapshot(),
|
|
122
|
+
failedVersions,
|
|
123
|
+
// Only a definite failure predicts missing flags.
|
|
124
|
+
countryFlags: probe.supports(FLAG_SAMPLE) !== false,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Test-only: forget cached probe results. */
|
|
129
|
+
export function __resetNativeEmojiSupportForTest(): void {
|
|
130
|
+
cache = new WeakMap();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function probe(
|
|
134
|
+
fontFamily: string,
|
|
135
|
+
ownerDocument: Document,
|
|
136
|
+
): NativeEmojiProbe | typeof UNKNOWN_SUPPORT {
|
|
137
|
+
if (isJsdom()) return UNKNOWN_SUPPORT;
|
|
138
|
+
try {
|
|
139
|
+
const canvas = ownerDocument.createElement('canvas');
|
|
140
|
+
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
|
141
|
+
if (!ctx) return UNKNOWN_SUPPORT;
|
|
142
|
+
ctx.font = `24px ${fontFamily}`;
|
|
143
|
+
ctx.textBaseline = 'top';
|
|
144
|
+
const baselineWidth = ctx.measureText(BASELINE_EMOJI).width;
|
|
145
|
+
const sequences = createNativeEmojiSequenceSupport(ctx, baselineWidth);
|
|
146
|
+
// A stable baseline rejects text fallback, noise and blocked readbacks.
|
|
147
|
+
if (!baselineWidth || sequences.supports('1f600') !== true)
|
|
148
|
+
return UNKNOWN_SUPPORT;
|
|
149
|
+
return sequences;
|
|
150
|
+
} catch {
|
|
151
|
+
return UNKNOWN_SUPPORT;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Whether a unified code is a regional-indicator country flag. */
|
|
156
|
+
export function isCountryFlagUnified(unified: string): boolean {
|
|
157
|
+
// Regional-indicator pairs, plus tag-sequence subdivision flags: fonts
|
|
158
|
+
// without the former lack the latter too.
|
|
159
|
+
return /^(1f1[ef][\da-f]-1f1[ef][\da-f]$|1f3f4-e00)/i.test(unified);
|
|
160
|
+
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
SkinTones,
|
|
3
|
+
SuggestionMode,
|
|
4
|
+
SuggestionModeValue,
|
|
5
|
+
} from '../types/exposedTypes';
|
|
2
6
|
|
|
3
7
|
import { DataEmoji } from './DataTypes';
|
|
4
|
-
import { emojiUnified } from './emojiUtils';
|
|
8
|
+
import { emojiUnified, emojiCanonicalUnified } from './emojiUtils';
|
|
5
9
|
|
|
6
10
|
const SUGGESTED_LS_KEY = 'epr_suggested';
|
|
7
11
|
|
|
@@ -13,7 +17,7 @@ type SuggestedItem = {
|
|
|
13
17
|
|
|
14
18
|
type Suggested = SuggestedItem[];
|
|
15
19
|
|
|
16
|
-
export function getSuggested(mode?:
|
|
20
|
+
export function getSuggested(mode?: SuggestionModeValue): Suggested {
|
|
17
21
|
try {
|
|
18
22
|
if (typeof window === 'undefined' || !window.localStorage) {
|
|
19
23
|
return [];
|
|
@@ -36,7 +40,7 @@ export function setSuggested(emoji: DataEmoji, skinTone: SkinTones) {
|
|
|
36
40
|
const recent = getSuggested();
|
|
37
41
|
|
|
38
42
|
const unified = emojiUnified(emoji, skinTone);
|
|
39
|
-
const originalUnified =
|
|
43
|
+
const originalUnified = emojiCanonicalUnified(emoji);
|
|
40
44
|
|
|
41
45
|
let existing = recent.find(({ unified: u }) => u === unified);
|
|
42
46
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { DataEmoji, EmojiProperties } from './DataTypes';
|
|
2
|
+
|
|
3
|
+
// Caller-defined Suggested contents.
|
|
4
|
+
//
|
|
5
|
+
// While `suggestedEmojis` is present it fully determines the Suggested
|
|
6
|
+
// category contents/order and `suggestedEmojisMode` is ignored for
|
|
7
|
+
// selection/order. One case-insensitive rule covers both Unicode and
|
|
8
|
+
// custom IDs, matching how `customEmojis` are indexed (lowercased on
|
|
9
|
+
// entry). A valid skin-tone variation keeps that exact variation for
|
|
10
|
+
// rendering rather than collapsing to the neutral base. Unknown entries
|
|
11
|
+
// are ignored; duplicates collapse by resolved render identity with first
|
|
12
|
+
// occurrence winning; caller order is otherwise preserved. The input
|
|
13
|
+
// array is never mutated. Native emoji characters ("🧠", "©️") are accepted
|
|
14
|
+
// too and resolved to their unified ID, so apps that store recents as the
|
|
15
|
+
// characters they insert can pass them through unchanged.
|
|
16
|
+
|
|
17
|
+
export function resolveSuggestedRenderIds(
|
|
18
|
+
entries: readonly string[],
|
|
19
|
+
lookup: (normalizedId: string) => DataEmoji | undefined,
|
|
20
|
+
): string[] {
|
|
21
|
+
const seen = new Set<string>();
|
|
22
|
+
const result: string[] = [];
|
|
23
|
+
|
|
24
|
+
for (const entry of entries) {
|
|
25
|
+
if (typeof entry !== 'string') {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const id = resolveEntryId(entry.trim().toLowerCase(), lookup);
|
|
29
|
+
if (!id || seen.has(id)) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
seen.add(id);
|
|
33
|
+
result.push(id);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* A lowercased unified ID, custom ID or emoji character, resolved to a
|
|
41
|
+
* known render identity (see resolveSuggestedRenderIds).
|
|
42
|
+
*/
|
|
43
|
+
export function resolveEntryId(
|
|
44
|
+
id: string,
|
|
45
|
+
lookup: (normalizedId: string) => DataEmoji | undefined,
|
|
46
|
+
): string | undefined {
|
|
47
|
+
if (!id) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
if (isKnownRenderId(id, lookup)) {
|
|
51
|
+
return id;
|
|
52
|
+
}
|
|
53
|
+
// Unified IDs and custom IDs are ASCII; anything else may be the
|
|
54
|
+
// emoji character itself.
|
|
55
|
+
if (Array.from(id).every((char) => (char.codePointAt(0) as number) < 0x80)) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
return nativeToUnifiedCandidates(id).find((candidate) =>
|
|
59
|
+
isKnownRenderId(candidate, lookup),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Dataset IDs are zero-padded code points joined by "-", and carry U+FE0F
|
|
64
|
+
// where the emoji presentation needs it; typed or stored characters often
|
|
65
|
+
// omit or add it. Try the exact sequence, then without FE0F, then with
|
|
66
|
+
// FE0F after the first code point (e.g. "©" -> "00a9-fe0f").
|
|
67
|
+
function nativeToUnifiedCandidates(native: string): string[] {
|
|
68
|
+
const codePoints = Array.from(native).map((char) =>
|
|
69
|
+
(char.codePointAt(0) as number).toString(16).padStart(4, '0'),
|
|
70
|
+
);
|
|
71
|
+
const exact = codePoints.join('-');
|
|
72
|
+
const bare = codePoints.filter((codePoint) => codePoint !== 'fe0f');
|
|
73
|
+
const withPresentation = [bare[0], 'fe0f', ...bare.slice(1)];
|
|
74
|
+
return [exact, bare.join('-'), withPresentation.join('-')];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// A render identity is valid only as the base unified or a listed
|
|
78
|
+
// variation. Anything else — e.g. a nonexistent variation that fell back
|
|
79
|
+
// to the base record — is unknown and ignored.
|
|
80
|
+
function isKnownRenderId(
|
|
81
|
+
id: string,
|
|
82
|
+
lookup: (normalizedId: string) => DataEmoji | undefined,
|
|
83
|
+
): boolean {
|
|
84
|
+
const record = lookup(id);
|
|
85
|
+
if (!record) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return (
|
|
89
|
+
record[EmojiProperties.unified] === id ||
|
|
90
|
+
(record[EmojiProperties.variations] ?? []).includes(id)
|
|
91
|
+
);
|
|
92
|
+
}
|
package/src/env.d.ts
ADDED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { GetEmojiUrl } from '../components/emoji/BaseEmojiProps';
|
|
2
2
|
import { DataEmoji } from '../dataUtils/DataTypes';
|
|
3
3
|
import { emojiUnified, emojiVariations } from '../dataUtils/emojiUtils';
|
|
4
|
-
import { EmojiStyle } from '../types/exposedTypes';
|
|
4
|
+
import { EmojiStyle, EmojiStyleValue } from '../types/exposedTypes';
|
|
5
5
|
import { Dimensions } from '../virtualization/virtualizationHelpers';
|
|
6
6
|
|
|
7
7
|
// eslint-disable-next-line max-params
|
|
8
8
|
export function preloadEmojiIfNeeded(
|
|
9
9
|
emoji: undefined | DataEmoji,
|
|
10
|
-
emojiStyles:
|
|
10
|
+
emojiStyles: EmojiStyleValue,
|
|
11
11
|
scrollTop: number,
|
|
12
12
|
clientHeight: number,
|
|
13
13
|
topOffset: number,
|
|
@@ -51,7 +51,7 @@ export function preloadEmojiIfNeeded(
|
|
|
51
51
|
export function preloadEmoji(
|
|
52
52
|
getEmojiUrl: GetEmojiUrl,
|
|
53
53
|
emoji: undefined | DataEmoji,
|
|
54
|
-
emojiStyle:
|
|
54
|
+
emojiStyle: EmojiStyleValue,
|
|
55
55
|
): void {
|
|
56
56
|
if (!emoji) {
|
|
57
57
|
return;
|
|
@@ -24,6 +24,7 @@ export function useActiveCategoryScrollDetection({
|
|
|
24
24
|
useEffect(() => {
|
|
25
25
|
const visibleCategories = new Map<string, number>();
|
|
26
26
|
const intersectingCategories = new Map<string, boolean>();
|
|
27
|
+
const targets = new Map<string, Element>();
|
|
27
28
|
const bodyRef = BodyRef.current;
|
|
28
29
|
const observer = new IntersectionObserver(
|
|
29
30
|
(entries) => {
|
|
@@ -40,6 +41,7 @@ export function useActiveCategoryScrollDetection({
|
|
|
40
41
|
|
|
41
42
|
visibleCategories.set(id, entry.intersectionRatio);
|
|
42
43
|
intersectingCategories.set(id, entry.isIntersecting);
|
|
44
|
+
targets.set(id, entry.target);
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
const ratios = Array.from(visibleCategories);
|
|
@@ -54,8 +56,15 @@ export function useActiveCategoryScrollDetection({
|
|
|
54
56
|
return setActiveCategory(lastCategory[0]);
|
|
55
57
|
}
|
|
56
58
|
|
|
59
|
+
const viewportTop = bodyRef.getBoundingClientRect().top;
|
|
57
60
|
for (const [id, ratio] of ratios) {
|
|
58
|
-
|
|
61
|
+
// Fractional offsets can leave less than a pixel of the preceding
|
|
62
|
+
// section intersecting after a jump. It must not steal the tab
|
|
63
|
+
// highlight from the section aligned with the viewport top.
|
|
64
|
+
if (
|
|
65
|
+
ratio &&
|
|
66
|
+
targets.get(id)!.getBoundingClientRect().bottom > viewportTop + 1
|
|
67
|
+
) {
|
|
59
68
|
setActiveCategory(id);
|
|
60
69
|
break;
|
|
61
70
|
}
|
|
@@ -1,35 +1,87 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { asSelectors, ClassNames } from '../DomUtils/classNames';
|
|
4
|
+
import {
|
|
5
|
+
elementBorderBoxHeight,
|
|
6
|
+
EmojiButtonSelector,
|
|
7
|
+
} from '../DomUtils/selectors';
|
|
4
8
|
import {
|
|
5
9
|
useEmojiListRef,
|
|
6
10
|
usePickerMainRef,
|
|
7
11
|
} from '../components/context/ElementRefContext';
|
|
8
12
|
import {
|
|
13
|
+
useNavigationRegistry,
|
|
9
14
|
useReactionsModeState,
|
|
10
15
|
useVisibleCategoriesState,
|
|
11
16
|
useEmojiSizeState,
|
|
12
17
|
} from '../components/context/PickerContext';
|
|
18
|
+
import { useColumnsConfig } from '../config/useConfig';
|
|
19
|
+
|
|
20
|
+
import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';
|
|
13
21
|
|
|
14
22
|
const EMOJI_SIZE_DEFAULT = 40;
|
|
15
23
|
|
|
24
|
+
// Rows live inside the category content box, not the full list:
|
|
25
|
+
// category margins/padding shrink the usable width, and dividing
|
|
26
|
+
// the raw list width would overcount columns so the last emoji in
|
|
27
|
+
// every row spills past the content edge (into the scrollbar).
|
|
28
|
+
// Measure a rendered, visible content box when one exists; fall back
|
|
29
|
+
// to the list width (identical when categories carry no horizontal
|
|
30
|
+
// inset). Hidden categories report zero width and are skipped.
|
|
31
|
+
function firstVisibleContentWidth(
|
|
32
|
+
listEl: HTMLElement,
|
|
33
|
+
fallbackWidth: number,
|
|
34
|
+
): number {
|
|
35
|
+
const contentBoxes = listEl.querySelectorAll(
|
|
36
|
+
asSelectors(ClassNames.categoryContent),
|
|
37
|
+
);
|
|
38
|
+
for (const box of Array.from(contentBoxes)) {
|
|
39
|
+
const width = (box as HTMLElement).clientWidth;
|
|
40
|
+
if (width > 0) {
|
|
41
|
+
return width;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return fallbackWidth;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// A column-count change obsoletes rows computed by pending keyboard
|
|
48
|
+
// navigation.
|
|
49
|
+
function trackColumnCount(
|
|
50
|
+
previousRef: React.MutableRefObject<number | undefined>,
|
|
51
|
+
emojisPerRow: number,
|
|
52
|
+
registry: { invalidate(): void },
|
|
53
|
+
): void {
|
|
54
|
+
if (
|
|
55
|
+
previousRef.current !== undefined &&
|
|
56
|
+
previousRef.current !== emojisPerRow
|
|
57
|
+
) {
|
|
58
|
+
registry.invalidate();
|
|
59
|
+
}
|
|
60
|
+
previousRef.current = emojisPerRow;
|
|
61
|
+
}
|
|
62
|
+
|
|
16
63
|
export function useCategoryHeight(emojiCount: number):
|
|
17
64
|
| {
|
|
18
65
|
categoryHeight: number;
|
|
19
66
|
emojisPerRow: number;
|
|
20
67
|
emojiSize: number;
|
|
68
|
+
rowWidth: number;
|
|
21
69
|
}
|
|
22
70
|
| undefined {
|
|
23
71
|
const EmojiListRef = useEmojiListRef();
|
|
24
72
|
const [isReactionsMode] = useReactionsModeState();
|
|
25
73
|
const PickerMainRef = usePickerMainRef();
|
|
26
|
-
const emojiSizeRef = React.useRef<number | undefined>();
|
|
74
|
+
const emojiSizeRef = React.useRef<number | undefined>(undefined);
|
|
75
|
+
const emojisPerRowRef = React.useRef<number | undefined>(undefined);
|
|
76
|
+
const registry = useNavigationRegistry();
|
|
27
77
|
const [visibleCategories] = useVisibleCategoriesState();
|
|
28
78
|
const [emojiSizeFromContext] = useEmojiSizeState();
|
|
79
|
+
const columns = useColumnsConfig();
|
|
29
80
|
const [dimensions, setDimensions] = React.useState<{
|
|
30
81
|
categoryHeight: number;
|
|
31
82
|
emojisPerRow: number;
|
|
32
83
|
emojiSize: number;
|
|
84
|
+
rowWidth: number;
|
|
33
85
|
}>();
|
|
34
86
|
|
|
35
87
|
// Helper to compute and store dimensions based on current DOM
|
|
@@ -41,7 +93,8 @@ export function useCategoryHeight(emojiCount: number):
|
|
|
41
93
|
EmojiButtonSelector,
|
|
42
94
|
) as HTMLElement | null;
|
|
43
95
|
|
|
44
|
-
|
|
96
|
+
// Border-box geometry includes design-library button borders.
|
|
97
|
+
const measured = elementBorderBoxHeight(emojiElement);
|
|
45
98
|
if (measured) {
|
|
46
99
|
emojiSizeRef.current = measured;
|
|
47
100
|
}
|
|
@@ -54,15 +107,20 @@ export function useCategoryHeight(emojiCount: number):
|
|
|
54
107
|
|
|
55
108
|
if (pickerWidth === 0 || emojiSize === 0) return;
|
|
56
109
|
|
|
57
|
-
const
|
|
110
|
+
const rowWidth = firstVisibleContentWidth(listEl, pickerWidth);
|
|
111
|
+
const fit = Math.max(1, Math.floor(rowWidth / emojiSize));
|
|
112
|
+
const emojisPerRow = columns ? Math.min(columns, fit) : fit;
|
|
58
113
|
const rowCount = Math.ceil(emojiCount / emojisPerRow);
|
|
59
114
|
const categoryHeight = rowCount * emojiSize;
|
|
60
115
|
|
|
61
|
-
|
|
62
|
-
|
|
116
|
+
trackColumnCount(emojisPerRowRef, emojisPerRow, registry);
|
|
117
|
+
setDimensions({ categoryHeight, emojisPerRow, emojiSize, rowWidth });
|
|
118
|
+
}, [EmojiListRef, emojiCount, emojiSizeFromContext, registry, columns]);
|
|
63
119
|
|
|
64
|
-
//
|
|
65
|
-
|
|
120
|
+
// Restore measured row heights in the same commit as the filtered
|
|
121
|
+
// contents. A passive effect leaves one frame of stale offsets that
|
|
122
|
+
// category navigation can observe immediately after clearing search.
|
|
123
|
+
useIsomorphicLayoutEffect(() => {
|
|
66
124
|
computeAndSetDimensions();
|
|
67
125
|
}, [
|
|
68
126
|
emojiCount,
|
|
@@ -71,6 +129,22 @@ export function useCategoryHeight(emojiCount: number):
|
|
|
71
129
|
visibleCategories.length,
|
|
72
130
|
]);
|
|
73
131
|
|
|
132
|
+
// Fluid layouts (width: 100%, sheets, docks) resize without a CSS
|
|
133
|
+
// transition: recompute columns when the list's width changes.
|
|
134
|
+
React.useEffect(() => {
|
|
135
|
+
const listEl = EmojiListRef.current;
|
|
136
|
+
if (!listEl || typeof ResizeObserver === 'undefined') return;
|
|
137
|
+
let lastWidth = listEl.clientWidth;
|
|
138
|
+
const observer = new ResizeObserver(() => {
|
|
139
|
+
const width = listEl.clientWidth;
|
|
140
|
+
if (width === lastWidth) return;
|
|
141
|
+
lastWidth = width;
|
|
142
|
+
computeAndSetDimensions();
|
|
143
|
+
});
|
|
144
|
+
observer.observe(listEl);
|
|
145
|
+
return () => observer.disconnect();
|
|
146
|
+
}, [EmojiListRef, computeAndSetDimensions]);
|
|
147
|
+
|
|
74
148
|
// Listen to transitionend on the picker root (where height transition occurs)
|
|
75
149
|
React.useEffect(() => {
|
|
76
150
|
const rootEl = PickerMainRef.current;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/* global process: readonly */
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
import type { CustomEmoji } from '../config/customEmojiConfig';
|
|
5
|
+
import { defaultEmojiDataOrEmpty } from '../data/defaultEmojiData';
|
|
6
|
+
import type { EmojiData } from '../types/exposedTypes';
|
|
7
|
+
|
|
8
|
+
interface IdentityTracker {
|
|
9
|
+
prev: unknown;
|
|
10
|
+
consecutiveChanges: number;
|
|
11
|
+
warned: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function trackIdentity(
|
|
15
|
+
tracker: IdentityTracker,
|
|
16
|
+
value: unknown,
|
|
17
|
+
isDefault: boolean,
|
|
18
|
+
message: string,
|
|
19
|
+
): void {
|
|
20
|
+
if (isDefault) {
|
|
21
|
+
tracker.prev = value;
|
|
22
|
+
tracker.consecutiveChanges = 0;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (tracker.prev !== undefined && tracker.prev !== value) {
|
|
26
|
+
tracker.consecutiveChanges += 1;
|
|
27
|
+
} else if (tracker.prev === value) {
|
|
28
|
+
tracker.consecutiveChanges = 0;
|
|
29
|
+
}
|
|
30
|
+
tracker.prev = value;
|
|
31
|
+
if (tracker.consecutiveChanges >= 3 && !tracker.warned) {
|
|
32
|
+
tracker.warned = true;
|
|
33
|
+
// eslint-disable-next-line no-console
|
|
34
|
+
console.warn(message);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function freshTracker(): IdentityTracker {
|
|
39
|
+
return { prev: undefined, consecutiveChanges: 0, warned: false };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Dev-only diagnostic.
|
|
44
|
+
*
|
|
45
|
+
* Identity caching only helps when callers keep data props referentially
|
|
46
|
+
* stable. If a non-default `emojiData` (or `customEmojis`) changes identity
|
|
47
|
+
* on three consecutive committed renders, warn once that repeated identity
|
|
48
|
+
* churn defeats prepared-data caching. Behavior stays correct; this is
|
|
49
|
+
* diagnostic only. A single/occasional replacement (e.g. locale change)
|
|
50
|
+
* must not warn. No deep-compare or clone is performed.
|
|
51
|
+
*/
|
|
52
|
+
export const useDataIdentityStabilityWarning: (
|
|
53
|
+
emojiData: EmojiData | undefined,
|
|
54
|
+
customEmojis: CustomEmoji[] | undefined,
|
|
55
|
+
) => void =
|
|
56
|
+
// Development only: production bundles drop the trackers entirely.
|
|
57
|
+
process.env.NODE_ENV === 'production'
|
|
58
|
+
? () => undefined
|
|
59
|
+
: useIdentityChurnWarning;
|
|
60
|
+
|
|
61
|
+
function useIdentityChurnWarning(
|
|
62
|
+
emojiData: EmojiData | undefined,
|
|
63
|
+
customEmojis: CustomEmoji[] | undefined,
|
|
64
|
+
): void {
|
|
65
|
+
const emojiState = React.useRef<IdentityTracker>(freshTracker());
|
|
66
|
+
const customState = React.useRef<IdentityTracker>(freshTracker());
|
|
67
|
+
|
|
68
|
+
React.useEffect(() => {
|
|
69
|
+
trackIdentity(
|
|
70
|
+
emojiState.current,
|
|
71
|
+
emojiData,
|
|
72
|
+
!emojiData || emojiData === defaultEmojiDataOrEmpty(),
|
|
73
|
+
'[emoji-picker-react] `emojiData` changed identity on three consecutive renders. ' +
|
|
74
|
+
'Memoize/reuse the dataset object so the prepared-data cache can be shared. ' +
|
|
75
|
+
'Behavior remains correct; caching is only defeated by identity churn.',
|
|
76
|
+
);
|
|
77
|
+
trackIdentity(
|
|
78
|
+
customState.current,
|
|
79
|
+
customEmojis,
|
|
80
|
+
!customEmojis || customEmojis.length === 0,
|
|
81
|
+
'[emoji-picker-react] `customEmojis` changed identity on three consecutive renders. ' +
|
|
82
|
+
'Memoize/reuse the array so custom-emoji derivation can be cached. ' +
|
|
83
|
+
'Behavior remains correct; caching is only defeated by identity churn.',
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useRef, useState } from 'react';
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
export function useDebouncedState<T>(
|
|
4
4
|
initialValue: T,
|
|
@@ -7,6 +7,18 @@ export function useDebouncedState<T>(
|
|
|
7
7
|
const [state, setState] = useState<T>(initialValue);
|
|
8
8
|
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
9
9
|
|
|
10
|
+
// A pending trailing edge must never fire after unmount (React warns
|
|
11
|
+
// about the post-unmount setState and the work is wasted).
|
|
12
|
+
useEffect(
|
|
13
|
+
() => () => {
|
|
14
|
+
if (timer.current) {
|
|
15
|
+
clearTimeout(timer.current);
|
|
16
|
+
timer.current = null;
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
[],
|
|
20
|
+
);
|
|
21
|
+
|
|
10
22
|
function debouncedSetState(value: T) {
|
|
11
23
|
return new Promise<T>((resolve) => {
|
|
12
24
|
if (timer.current) {
|
|
@@ -1,52 +1,36 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
useActiveSkinToneState,
|
|
3
|
+
useNativeEmojiSupport,
|
|
4
|
+
} from '../components/context/PickerContext';
|
|
5
|
+
import { useEmojiVersionConfig, useUnicodeToHide } from '../config/useConfig';
|
|
5
6
|
import { DataEmoji } from '../dataUtils/DataTypes';
|
|
6
7
|
import {
|
|
7
8
|
addedIn,
|
|
8
9
|
emojiUnified,
|
|
9
10
|
unifiedWithoutSkinTone,
|
|
10
11
|
} from '../dataUtils/emojiUtils';
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
export function useDisallowedEmojis() {
|
|
15
|
-
const DisallowedEmojisRef = useRef<Record<string, boolean>>({});
|
|
16
|
-
const emojiVersionConfig = useEmojiVersionConfig();
|
|
17
|
-
const { allEmojis } = usePickerDataContext();
|
|
18
|
-
|
|
19
|
-
return useMemo(() => {
|
|
20
|
-
const emojiVersion = parseFloat(`${emojiVersionConfig}`);
|
|
21
|
-
|
|
22
|
-
if (!emojiVersionConfig || Number.isNaN(emojiVersion)) {
|
|
23
|
-
return DisallowedEmojisRef.current;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return allEmojis.reduce((disallowedEmojis, emoji) => {
|
|
27
|
-
if (addedInNewerVersion(emoji, emojiVersion)) {
|
|
28
|
-
disallowedEmojis[emojiUnified(emoji)] = true;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return disallowedEmojis;
|
|
32
|
-
}, DisallowedEmojisRef.current);
|
|
33
|
-
}, [emojiVersionConfig, allEmojis]);
|
|
34
|
-
}
|
|
12
|
+
import { isNativeEmojiSupported } from '../dataUtils/nativeEmojiSupport';
|
|
13
|
+
import { isCustomEmoji } from '../typeRefinements/typeRefinements';
|
|
35
14
|
|
|
36
15
|
export function useIsEmojiDisallowed() {
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
16
|
+
const configured = parseFloat(useEmojiVersionConfig() || '');
|
|
17
|
+
const nativeSupport = useNativeEmojiSupport();
|
|
18
|
+
// A configured cap restricts native support; it never overrides it.
|
|
19
|
+
const cap = Number.isNaN(configured) ? Infinity : configured;
|
|
20
|
+
const unicodeToHide = useUnicodeToHide();
|
|
21
|
+
const [activeSkinTone] = useActiveSkinToneState();
|
|
22
|
+
|
|
23
|
+
return function isEmojiDisallowed(
|
|
24
|
+
emoji: DataEmoji,
|
|
25
|
+
renderedUnified = emojiUnified(emoji, activeSkinTone),
|
|
26
|
+
) {
|
|
27
|
+
if (unicodeToHide.has(unifiedWithoutSkinTone(emojiUnified(emoji))))
|
|
28
|
+
return true;
|
|
29
|
+
if (isCustomEmoji(emoji)) return false;
|
|
30
|
+
const version = addedIn(emoji);
|
|
31
|
+
return (
|
|
32
|
+
version > cap ||
|
|
33
|
+
!isNativeEmojiSupported(nativeSupport, renderedUnified, version)
|
|
34
|
+
);
|
|
44
35
|
};
|
|
45
36
|
}
|
|
46
|
-
|
|
47
|
-
function addedInNewerVersion(
|
|
48
|
-
emoji: DataEmoji,
|
|
49
|
-
supportedLevel: number,
|
|
50
|
-
): boolean {
|
|
51
|
-
return addedIn(emoji) > supportedLevel;
|
|
52
|
-
}
|