emoji-picker-react 4.15.1 → 4.15.2
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/dist/components/body/MeasureEmoji.d.ts +2 -0
- package/dist/components/context/PickerContext.d.ts +1 -0
- package/dist/emoji-picker-react.cjs.development.js +164 -114
- package/dist/emoji-picker-react.cjs.development.js.map +1 -1
- package/dist/emoji-picker-react.cjs.production.min.js +1 -1
- package/dist/emoji-picker-react.cjs.production.min.js.map +1 -1
- package/dist/emoji-picker-react.esm.js +165 -115
- package/dist/emoji-picker-react.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/body/EmojiCategory.tsx +2 -0
- package/src/components/body/EmojiList.tsx +2 -0
- package/src/components/body/MeasureEmoji.tsx +74 -0
- package/src/components/context/PickerContext.tsx +11 -2
- package/src/hooks/useCategoryHeight.ts +18 -12
- package/src/hooks/useEmojiVirtualization.tsx +15 -12
package/package.json
CHANGED
|
@@ -16,6 +16,7 @@ import { useEmojiListRef } from '../context/ElementRefContext';
|
|
|
16
16
|
import { useVisibleCategoriesState } from '../context/PickerContext';
|
|
17
17
|
|
|
18
18
|
import { EmojiCategory } from './EmojiCategory';
|
|
19
|
+
import { MeasureEmoji } from './MeasureEmoji';
|
|
19
20
|
|
|
20
21
|
export function EmojiList({ scrollTop }: { scrollTop: number }) {
|
|
21
22
|
const categories = useCategoriesConfig();
|
|
@@ -30,6 +31,7 @@ export function EmojiList({ scrollTop }: { scrollTop: number }) {
|
|
|
30
31
|
let topOffset = 0;
|
|
31
32
|
return (
|
|
32
33
|
<ul className={cx(styles.emojiList)} ref={EmojiListRef}>
|
|
34
|
+
<MeasureEmoji />
|
|
33
35
|
{categories.map(categoryConfig => {
|
|
34
36
|
const category = categoryFromCategoryConfig(categoryConfig);
|
|
35
37
|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
categoryFromCategoryConfig
|
|
5
|
+
} from '../../config/categoryConfig';
|
|
6
|
+
import {
|
|
7
|
+
useCategoriesConfig,
|
|
8
|
+
useEmojiStyleConfig,
|
|
9
|
+
useGetEmojiUrlConfig,
|
|
10
|
+
useLazyLoadEmojisConfig
|
|
11
|
+
} from '../../config/useConfig';
|
|
12
|
+
import {
|
|
13
|
+
useGetEmojisByCategory,
|
|
14
|
+
emojiUnified
|
|
15
|
+
} from '../../dataUtils/emojiSelectors';
|
|
16
|
+
import { useActiveSkinToneState, useEmojiSizeState } from '../context/PickerContext';
|
|
17
|
+
import { ClickableEmoji } from '../emoji/Emoji';
|
|
18
|
+
|
|
19
|
+
export function MeasureEmoji() {
|
|
20
|
+
const categories = useCategoriesConfig();
|
|
21
|
+
const getEmojisByCategory = useGetEmojisByCategory();
|
|
22
|
+
const emojiStyle = useEmojiStyleConfig();
|
|
23
|
+
const getEmojiUrl = useGetEmojiUrlConfig();
|
|
24
|
+
const lazyLoadEmojis = useLazyLoadEmojisConfig();
|
|
25
|
+
const [activeSkinTone] = useActiveSkinToneState();
|
|
26
|
+
const [emojiSize, setEmojiSize] = useEmojiSizeState();
|
|
27
|
+
const ref = React.useRef<HTMLDivElement>(null);
|
|
28
|
+
|
|
29
|
+
React.useLayoutEffect(() => {
|
|
30
|
+
if (ref.current) {
|
|
31
|
+
setEmojiSize(ref.current.clientHeight);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (emojiSize) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const firstCategory = categories[0];
|
|
40
|
+
const dummyEmoji = getEmojisByCategory(
|
|
41
|
+
categoryFromCategoryConfig(firstCategory)
|
|
42
|
+
)[0];
|
|
43
|
+
const unified = dummyEmoji
|
|
44
|
+
? emojiUnified(dummyEmoji, activeSkinTone)
|
|
45
|
+
: '';
|
|
46
|
+
|
|
47
|
+
if (!dummyEmoji) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div ref={ref}>
|
|
53
|
+
<ClickableEmoji
|
|
54
|
+
emoji={dummyEmoji}
|
|
55
|
+
unified={unified}
|
|
56
|
+
emojiStyle={emojiStyle}
|
|
57
|
+
getEmojiUrl={getEmojiUrl}
|
|
58
|
+
lazyLoad={lazyLoadEmojis}
|
|
59
|
+
showVariations={false}
|
|
60
|
+
hidden={false}
|
|
61
|
+
style={{
|
|
62
|
+
opacity: 0,
|
|
63
|
+
pointerEvents: 'none',
|
|
64
|
+
position: 'absolute',
|
|
65
|
+
top: 0,
|
|
66
|
+
left: 0,
|
|
67
|
+
zIndex: -1,
|
|
68
|
+
height: 'var(--epr-emoji-fullsize)',
|
|
69
|
+
width: 'var(--epr-emoji-fullsize)'
|
|
70
|
+
}}
|
|
71
|
+
/>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -36,6 +36,7 @@ export function PickerContextProvider({ children }: Props) {
|
|
|
36
36
|
const reactionsModeState = useState(reactionsDefaultOpen);
|
|
37
37
|
const [isPastInitialLoad, setIsPastInitialLoad] = useState(false);
|
|
38
38
|
const visibleCategoriesState = useState<string[]>([]);
|
|
39
|
+
const emojiSizeState = useState<number | null>(null);
|
|
39
40
|
|
|
40
41
|
useMarkInitialLoad(setIsPastInitialLoad);
|
|
41
42
|
|
|
@@ -55,7 +56,8 @@ export function PickerContextProvider({ children }: Props) {
|
|
|
55
56
|
skinToneFanOpenState,
|
|
56
57
|
suggestedUpdateState,
|
|
57
58
|
reactionsModeState,
|
|
58
|
-
visibleCategoriesState
|
|
59
|
+
visibleCategoriesState,
|
|
60
|
+
emojiSizeState
|
|
59
61
|
}}
|
|
60
62
|
>
|
|
61
63
|
{children}
|
|
@@ -80,6 +82,7 @@ const PickerContext = React.createContext<{
|
|
|
80
82
|
disallowedEmojisRef: React.MutableRefObject<Record<string, boolean>>;
|
|
81
83
|
reactionsModeState: ReactState<boolean>;
|
|
82
84
|
visibleCategoriesState: ReactState<Array<string>>;
|
|
85
|
+
emojiSizeState: ReactState<number | null>;
|
|
83
86
|
}>({
|
|
84
87
|
activeCategoryState: [null, () => {}],
|
|
85
88
|
activeSkinTone: [SkinTones.NEUTRAL, () => {}],
|
|
@@ -94,7 +97,8 @@ const PickerContext = React.createContext<{
|
|
|
94
97
|
skinToneFanOpenState: [false, () => {}],
|
|
95
98
|
suggestedUpdateState: [Date.now(), () => {}],
|
|
96
99
|
reactionsModeState: [false, () => {}],
|
|
97
|
-
visibleCategoriesState: [[], () => []]
|
|
100
|
+
visibleCategoriesState: [[], () => []],
|
|
101
|
+
emojiSizeState: [null, () => {}]
|
|
98
102
|
});
|
|
99
103
|
|
|
100
104
|
type Props = Readonly<{
|
|
@@ -164,6 +168,11 @@ export function useVisibleCategoriesState() {
|
|
|
164
168
|
return visibleCategoriesState;
|
|
165
169
|
}
|
|
166
170
|
|
|
171
|
+
export function useEmojiSizeState() {
|
|
172
|
+
const { emojiSizeState } = React.useContext(PickerContext);
|
|
173
|
+
return emojiSizeState;
|
|
174
|
+
}
|
|
175
|
+
|
|
167
176
|
export function useUpdateSuggested(): [number, () => void] {
|
|
168
177
|
const { suggestedUpdateState } = React.useContext(PickerContext);
|
|
169
178
|
|
|
@@ -3,18 +3,17 @@ import * as React from 'react';
|
|
|
3
3
|
import { EmojiButtonSelector } from '../DomUtils/selectors';
|
|
4
4
|
import {
|
|
5
5
|
useEmojiListRef,
|
|
6
|
-
usePickerMainRef
|
|
6
|
+
usePickerMainRef,
|
|
7
7
|
} from '../components/context/ElementRefContext';
|
|
8
8
|
import {
|
|
9
9
|
useReactionsModeState,
|
|
10
|
-
useVisibleCategoriesState
|
|
10
|
+
useVisibleCategoriesState,
|
|
11
|
+
useEmojiSizeState,
|
|
11
12
|
} from '../components/context/PickerContext';
|
|
12
13
|
|
|
13
|
-
const EMOJI_SIZE_DEFAULT =
|
|
14
|
+
const EMOJI_SIZE_DEFAULT = 40;
|
|
14
15
|
|
|
15
|
-
export function useCategoryHeight(
|
|
16
|
-
emojiCount: number
|
|
17
|
-
):
|
|
16
|
+
export function useCategoryHeight(emojiCount: number):
|
|
18
17
|
| {
|
|
19
18
|
categoryHeight: number;
|
|
20
19
|
emojisPerRow: number;
|
|
@@ -26,6 +25,7 @@ export function useCategoryHeight(
|
|
|
26
25
|
const PickerMainRef = usePickerMainRef();
|
|
27
26
|
const emojiSizeRef = React.useRef<number | undefined>();
|
|
28
27
|
const [visibleCategories] = useVisibleCategoriesState();
|
|
28
|
+
const [emojiSizeFromContext] = useEmojiSizeState();
|
|
29
29
|
const [dimensions, setDimensions] = React.useState<{
|
|
30
30
|
categoryHeight: number;
|
|
31
31
|
emojisPerRow: number;
|
|
@@ -38,12 +38,18 @@ export function useCategoryHeight(
|
|
|
38
38
|
if (!listEl) return;
|
|
39
39
|
|
|
40
40
|
const emojiElement = listEl.querySelector(
|
|
41
|
-
EmojiButtonSelector
|
|
41
|
+
EmojiButtonSelector,
|
|
42
42
|
) as HTMLElement | null;
|
|
43
43
|
|
|
44
44
|
const measured = emojiElement?.clientHeight;
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
if (measured) {
|
|
46
|
+
emojiSizeRef.current = measured;
|
|
47
|
+
}
|
|
48
|
+
const emojiSize =
|
|
49
|
+
emojiSizeFromContext ||
|
|
50
|
+
measured ||
|
|
51
|
+
emojiSizeRef.current ||
|
|
52
|
+
EMOJI_SIZE_DEFAULT;
|
|
47
53
|
const pickerWidth = listEl.clientWidth;
|
|
48
54
|
|
|
49
55
|
if (pickerWidth === 0 || emojiSize === 0) return;
|
|
@@ -53,7 +59,7 @@ export function useCategoryHeight(
|
|
|
53
59
|
const categoryHeight = rowCount * emojiSize;
|
|
54
60
|
|
|
55
61
|
setDimensions({ categoryHeight, emojisPerRow, emojiSize });
|
|
56
|
-
}, [EmojiListRef, emojiCount]);
|
|
62
|
+
}, [EmojiListRef, emojiCount, emojiSizeFromContext]);
|
|
57
63
|
|
|
58
64
|
// Recompute on data-count changes and when reactions mode toggles
|
|
59
65
|
React.useEffect(() => {
|
|
@@ -62,7 +68,7 @@ export function useCategoryHeight(
|
|
|
62
68
|
emojiCount,
|
|
63
69
|
isReactionsMode,
|
|
64
70
|
computeAndSetDimensions,
|
|
65
|
-
visibleCategories.length
|
|
71
|
+
visibleCategories.length,
|
|
66
72
|
]);
|
|
67
73
|
|
|
68
74
|
// Listen to transitionend on the picker root (where height transition occurs)
|
|
@@ -90,7 +96,7 @@ export function useCategoryHeight(
|
|
|
90
96
|
};
|
|
91
97
|
|
|
92
98
|
rootEl.addEventListener('transitionend', handler, {
|
|
93
|
-
passive: true
|
|
99
|
+
passive: true,
|
|
94
100
|
});
|
|
95
101
|
return () => {
|
|
96
102
|
rootEl.removeEventListener('transitionend', handler);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
import { ReactNode, useEffect } from 'react';
|
|
2
3
|
import * as React from 'react';
|
|
3
4
|
|
|
4
5
|
import { useBodyRef } from '../components/context/ElementRefContext';
|
|
@@ -61,20 +62,22 @@ export function useEmojiVirtualization({
|
|
|
61
62
|
}
|
|
62
63
|
}, [dimensions, onHeightReady, emojisToPush.length]);
|
|
63
64
|
|
|
65
|
+
const isVirtualized = (style: { top: number; left: number } | undefined) =>
|
|
66
|
+
dimensions &&
|
|
67
|
+
BodyRef.current &&
|
|
68
|
+
shouldVirtualize({
|
|
69
|
+
scrollTop,
|
|
70
|
+
clientHeight: BodyRef.current?.clientHeight ?? 0,
|
|
71
|
+
topOffset,
|
|
72
|
+
style,
|
|
73
|
+
dimensions
|
|
74
|
+
});
|
|
75
|
+
|
|
64
76
|
const emojis = emojisToPush.reduce((accumulator, emoji, index) => {
|
|
65
77
|
const unified = emojiUnified(emoji, activeSkinTone);
|
|
66
78
|
const style = getEmojiPositionStyle(dimensions, index);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
BodyRef.current &&
|
|
70
|
-
shouldVirtualize({
|
|
71
|
-
scrollTop,
|
|
72
|
-
clientHeight: BodyRef.current?.clientHeight ?? 0,
|
|
73
|
-
topOffset,
|
|
74
|
-
style,
|
|
75
|
-
dimensions
|
|
76
|
-
})
|
|
77
|
-
) {
|
|
79
|
+
|
|
80
|
+
if (isVirtualized(style)) {
|
|
78
81
|
virtualizedCounter++;
|
|
79
82
|
preloadEmojiIfNeeded(
|
|
80
83
|
emoji,
|