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,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,57 +1,12 @@
|
|
|
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 {
|
|
5
|
-
import { Dimensions } from '../virtualization/virtualizationHelpers';
|
|
6
|
-
|
|
7
|
-
// eslint-disable-next-line max-params
|
|
8
|
-
export function preloadEmojiIfNeeded(
|
|
9
|
-
emoji: undefined | DataEmoji,
|
|
10
|
-
emojiStyles: EmojiStyle,
|
|
11
|
-
scrollTop: number,
|
|
12
|
-
clientHeight: number,
|
|
13
|
-
topOffset: number,
|
|
14
|
-
style: { top: number } | undefined,
|
|
15
|
-
dimensions: Dimensions,
|
|
16
|
-
getEmojiUrl: GetEmojiUrl,
|
|
17
|
-
): void {
|
|
18
|
-
if (!emoji) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (emojiStyles === EmojiStyle.NATIVE) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const unified = emojiUnified(emoji);
|
|
27
|
-
|
|
28
|
-
if (preloadedEmojs.has(unified)) {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// preload only if the emoji is a few rows below the viewport
|
|
33
|
-
if (!style || !dimensions) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
setTimeout(() => {
|
|
38
|
-
const emojiTop = topOffset + style.top;
|
|
39
|
-
const viewportBottom = scrollTop + clientHeight;
|
|
40
|
-
|
|
41
|
-
const isJustBelowViewport =
|
|
42
|
-
emojiTop >= viewportBottom &&
|
|
43
|
-
emojiTop < viewportBottom + dimensions.emojiSize * 2;
|
|
44
|
-
|
|
45
|
-
if (isJustBelowViewport) {
|
|
46
|
-
preloadEmoji(getEmojiUrl, emoji, emojiStyles);
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
}
|
|
4
|
+
import { EmojiStyleValue } from '../types/exposedTypes';
|
|
50
5
|
|
|
51
6
|
export function preloadEmoji(
|
|
52
7
|
getEmojiUrl: GetEmojiUrl,
|
|
53
8
|
emoji: undefined | DataEmoji,
|
|
54
|
-
emojiStyle:
|
|
9
|
+
emojiStyle: EmojiStyleValue,
|
|
55
10
|
): void {
|
|
56
11
|
if (!emoji) {
|
|
57
12
|
return;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
2
|
|
|
3
3
|
import { categoryNameFromDom } from '../DomUtils/categoryNameFromDom';
|
|
4
|
-
import {
|
|
4
|
+
import { ClassNames } from '../DomUtils/classNames';
|
|
5
5
|
import { useBodyRef } from '../components/context/ElementRefContext';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
CategoriesConfig,
|
|
8
|
+
categoryIdFromCategoryConfig,
|
|
9
|
+
} from '../config/categoryConfig';
|
|
7
10
|
|
|
8
11
|
export function useActiveCategoryScrollDetection({
|
|
9
12
|
setActiveCategory,
|
|
@@ -22,8 +25,11 @@ export function useActiveCategoryScrollDetection({
|
|
|
22
25
|
const BodyRef = useBodyRef();
|
|
23
26
|
|
|
24
27
|
useEffect(() => {
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
if (typeof IntersectionObserver === 'undefined') {
|
|
29
|
+
setVisibleCategories(categories.map(categoryIdFromCategoryConfig));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const observed = new Map<string, IntersectionObserverEntry>();
|
|
27
33
|
const bodyRef = BodyRef.current;
|
|
28
34
|
const observer = new IntersectionObserver(
|
|
29
35
|
(entries) => {
|
|
@@ -38,24 +44,32 @@ export function useActiveCategoryScrollDetection({
|
|
|
38
44
|
continue;
|
|
39
45
|
}
|
|
40
46
|
|
|
41
|
-
|
|
42
|
-
intersectingCategories.set(id, entry.isIntersecting);
|
|
47
|
+
observed.set(id, entry);
|
|
43
48
|
}
|
|
44
49
|
|
|
45
|
-
const ratios = Array.from(
|
|
50
|
+
const ratios = Array.from(observed);
|
|
46
51
|
const visibleCats = ratios
|
|
47
|
-
.filter(
|
|
52
|
+
.filter(
|
|
53
|
+
([, entry]) => entry.intersectionRatio > 0 || entry.isIntersecting,
|
|
54
|
+
)
|
|
48
55
|
.map(([id]) => id);
|
|
49
56
|
|
|
50
57
|
setVisibleCategories(visibleCats);
|
|
51
58
|
const lastCategory = ratios[ratios.length - 1];
|
|
52
59
|
|
|
53
|
-
if (lastCategory?.[1]
|
|
60
|
+
if (lastCategory?.[1].intersectionRatio === 1) {
|
|
54
61
|
return setActiveCategory(lastCategory[0]);
|
|
55
62
|
}
|
|
56
63
|
|
|
57
|
-
|
|
58
|
-
|
|
64
|
+
const viewportTop = bodyRef.getBoundingClientRect().top;
|
|
65
|
+
for (const [id, entry] of ratios) {
|
|
66
|
+
// Fractional offsets can leave less than a pixel of the preceding
|
|
67
|
+
// section intersecting after a jump. It must not steal the tab
|
|
68
|
+
// highlight from the section aligned with the viewport top.
|
|
69
|
+
if (
|
|
70
|
+
entry.intersectionRatio &&
|
|
71
|
+
entry.target.getBoundingClientRect().bottom > viewportTop + 1
|
|
72
|
+
) {
|
|
59
73
|
setActiveCategory(id);
|
|
60
74
|
break;
|
|
61
75
|
}
|
|
@@ -66,11 +80,9 @@ export function useActiveCategoryScrollDetection({
|
|
|
66
80
|
threshold: [0, 1],
|
|
67
81
|
},
|
|
68
82
|
);
|
|
69
|
-
bodyRef
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
observer.observe(el);
|
|
73
|
-
});
|
|
83
|
+
bodyRef?.querySelectorAll(`.${ClassNames.category}`).forEach((el) => {
|
|
84
|
+
observer.observe(el);
|
|
85
|
+
});
|
|
74
86
|
|
|
75
87
|
return () => {
|
|
76
88
|
observer.disconnect();
|
|
@@ -1,35 +1,94 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { ClassNames } from '../DomUtils/classNames';
|
|
4
4
|
import {
|
|
5
|
+
elementBorderBoxHeight,
|
|
6
|
+
EmojiButtonSelector,
|
|
7
|
+
} from '../DomUtils/selectors';
|
|
8
|
+
import {
|
|
9
|
+
useBodyRef,
|
|
5
10
|
useEmojiListRef,
|
|
6
11
|
usePickerMainRef,
|
|
7
12
|
} from '../components/context/ElementRefContext';
|
|
8
13
|
import {
|
|
14
|
+
useNavigationRegistry,
|
|
9
15
|
useReactionsModeState,
|
|
10
16
|
useVisibleCategoriesState,
|
|
11
17
|
useEmojiSizeState,
|
|
12
18
|
} from '../components/context/PickerContext';
|
|
19
|
+
import { useColumnsConfig } from '../config/useConfig';
|
|
20
|
+
|
|
21
|
+
import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';
|
|
13
22
|
|
|
14
23
|
const EMOJI_SIZE_DEFAULT = 40;
|
|
15
24
|
|
|
16
|
-
|
|
25
|
+
// Rows live inside the category content box, not the full list:
|
|
26
|
+
// category margins/padding shrink the usable width, and dividing
|
|
27
|
+
// the raw list width would overcount columns so the last emoji in
|
|
28
|
+
// every row spills past the content edge (into the scrollbar).
|
|
29
|
+
// Measure a rendered, visible content box when one exists; fall back
|
|
30
|
+
// to the list width (identical when categories carry no horizontal
|
|
31
|
+
// inset). Hidden categories report zero width and are skipped.
|
|
32
|
+
function firstVisibleContentWidth(
|
|
33
|
+
listEl: HTMLElement,
|
|
34
|
+
fallbackWidth: number,
|
|
35
|
+
): number {
|
|
36
|
+
const contentBoxes = listEl.querySelectorAll(
|
|
37
|
+
`.${ClassNames.categoryContent}`,
|
|
38
|
+
);
|
|
39
|
+
for (const box of Array.from(contentBoxes)) {
|
|
40
|
+
const width = (box as HTMLElement).clientWidth;
|
|
41
|
+
if (width > 0) {
|
|
42
|
+
return width;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return fallbackWidth;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// A column-count change obsoletes rows computed by pending keyboard
|
|
49
|
+
// navigation.
|
|
50
|
+
function trackColumnCount(
|
|
51
|
+
previousRef: React.MutableRefObject<number | undefined>,
|
|
52
|
+
emojisPerRow: number,
|
|
53
|
+
registry: { invalidate(): void },
|
|
54
|
+
): void {
|
|
55
|
+
if (
|
|
56
|
+
previousRef.current !== undefined &&
|
|
57
|
+
previousRef.current !== emojisPerRow
|
|
58
|
+
) {
|
|
59
|
+
registry.invalidate();
|
|
60
|
+
}
|
|
61
|
+
previousRef.current = emojisPerRow;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function useCategoryHeight(
|
|
65
|
+
emojiCount: number,
|
|
66
|
+
ready = true,
|
|
67
|
+
):
|
|
17
68
|
| {
|
|
18
69
|
categoryHeight: number;
|
|
19
70
|
emojisPerRow: number;
|
|
20
71
|
emojiSize: number;
|
|
72
|
+
rowWidth: number;
|
|
73
|
+
viewportHeight?: number;
|
|
21
74
|
}
|
|
22
75
|
| undefined {
|
|
23
76
|
const EmojiListRef = useEmojiListRef();
|
|
77
|
+
const BodyRef = useBodyRef();
|
|
24
78
|
const [isReactionsMode] = useReactionsModeState();
|
|
25
79
|
const PickerMainRef = usePickerMainRef();
|
|
26
|
-
const emojiSizeRef = React.useRef<number | undefined>();
|
|
80
|
+
const emojiSizeRef = React.useRef<number | undefined>(undefined);
|
|
81
|
+
const emojisPerRowRef = React.useRef<number | undefined>(undefined);
|
|
82
|
+
const registry = useNavigationRegistry();
|
|
27
83
|
const [visibleCategories] = useVisibleCategoriesState();
|
|
28
84
|
const [emojiSizeFromContext] = useEmojiSizeState();
|
|
85
|
+
const columns = useColumnsConfig();
|
|
29
86
|
const [dimensions, setDimensions] = React.useState<{
|
|
30
87
|
categoryHeight: number;
|
|
31
88
|
emojisPerRow: number;
|
|
32
89
|
emojiSize: number;
|
|
90
|
+
rowWidth: number;
|
|
91
|
+
viewportHeight?: number;
|
|
33
92
|
}>();
|
|
34
93
|
|
|
35
94
|
// Helper to compute and store dimensions based on current DOM
|
|
@@ -41,36 +100,83 @@ export function useCategoryHeight(emojiCount: number):
|
|
|
41
100
|
EmojiButtonSelector,
|
|
42
101
|
) as HTMLElement | null;
|
|
43
102
|
|
|
44
|
-
|
|
103
|
+
// Border-box geometry includes design-library button borders.
|
|
104
|
+
const measured = elementBorderBoxHeight(emojiElement);
|
|
45
105
|
if (measured) {
|
|
46
106
|
emojiSizeRef.current = measured;
|
|
47
107
|
}
|
|
48
108
|
const emojiSize =
|
|
49
|
-
emojiSizeFromContext ||
|
|
50
109
|
measured ||
|
|
110
|
+
emojiSizeFromContext ||
|
|
51
111
|
emojiSizeRef.current ||
|
|
52
112
|
EMOJI_SIZE_DEFAULT;
|
|
53
113
|
const pickerWidth = listEl.clientWidth;
|
|
54
114
|
|
|
55
115
|
if (pickerWidth === 0 || emojiSize === 0) return;
|
|
56
116
|
|
|
57
|
-
const
|
|
117
|
+
const rowWidth = firstVisibleContentWidth(listEl, pickerWidth);
|
|
118
|
+
const fit = Math.max(1, Math.floor(rowWidth / emojiSize));
|
|
119
|
+
const emojisPerRow = columns ? Math.min(columns, fit) : fit;
|
|
58
120
|
const rowCount = Math.ceil(emojiCount / emojisPerRow);
|
|
59
121
|
const categoryHeight = rowCount * emojiSize;
|
|
60
122
|
|
|
61
|
-
|
|
62
|
-
|
|
123
|
+
const next = {
|
|
124
|
+
categoryHeight,
|
|
125
|
+
emojisPerRow,
|
|
126
|
+
emojiSize,
|
|
127
|
+
rowWidth,
|
|
128
|
+
// Virtualization reads the viewport height during render, so a picker
|
|
129
|
+
// that mounts collapsed (reactions first) must re-render once it opens.
|
|
130
|
+
viewportHeight: BodyRef.current?.clientHeight,
|
|
131
|
+
};
|
|
63
132
|
|
|
64
|
-
|
|
65
|
-
|
|
133
|
+
trackColumnCount(emojisPerRowRef, emojisPerRow, registry);
|
|
134
|
+
// Returning `previous` when nothing changed is what stops the layout
|
|
135
|
+
// effect below, which depends on `dimensions`, from looping forever.
|
|
136
|
+
// Both objects come from the literal above, so their keys share an order.
|
|
137
|
+
setDimensions((previous) =>
|
|
138
|
+
JSON.stringify(previous) === JSON.stringify(next) ? previous : next,
|
|
139
|
+
);
|
|
140
|
+
}, [
|
|
141
|
+
EmojiListRef,
|
|
142
|
+
BodyRef,
|
|
143
|
+
emojiCount,
|
|
144
|
+
emojiSizeFromContext,
|
|
145
|
+
registry,
|
|
146
|
+
columns,
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
// Restore measured row heights in the same commit as the filtered
|
|
150
|
+
// contents. A passive effect leaves one frame of stale offsets that
|
|
151
|
+
// category navigation can observe immediately after clearing search.
|
|
152
|
+
// Recheck an estimated size when positioned/visible cells first mount.
|
|
153
|
+
useIsomorphicLayoutEffect(() => {
|
|
66
154
|
computeAndSetDimensions();
|
|
67
155
|
}, [
|
|
68
156
|
emojiCount,
|
|
69
157
|
isReactionsMode,
|
|
70
158
|
computeAndSetDimensions,
|
|
71
159
|
visibleCategories.length,
|
|
160
|
+
dimensions,
|
|
161
|
+
ready,
|
|
72
162
|
]);
|
|
73
163
|
|
|
164
|
+
// Fluid layouts (width: 100%, sheets, docks) resize without a CSS
|
|
165
|
+
// transition: recompute columns when the list's width changes.
|
|
166
|
+
React.useEffect(() => {
|
|
167
|
+
const listEl = EmojiListRef.current;
|
|
168
|
+
if (!listEl || typeof ResizeObserver === 'undefined') return;
|
|
169
|
+
let lastWidth = listEl.clientWidth;
|
|
170
|
+
const observer = new ResizeObserver(() => {
|
|
171
|
+
const width = listEl.clientWidth;
|
|
172
|
+
if (width === lastWidth) return;
|
|
173
|
+
lastWidth = width;
|
|
174
|
+
computeAndSetDimensions();
|
|
175
|
+
});
|
|
176
|
+
observer.observe(listEl);
|
|
177
|
+
return () => observer.disconnect();
|
|
178
|
+
}, [EmojiListRef, computeAndSetDimensions]);
|
|
179
|
+
|
|
74
180
|
// Listen to transitionend on the picker root (where height transition occurs)
|
|
75
181
|
React.useEffect(() => {
|
|
76
182
|
const rootEl = PickerMainRef.current;
|