emoji-picker-react 4.0.0-rc.8 → 4.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 +176 -43
- package/dist/DomUtils/classNames.d.ts +9 -1
- package/dist/DomUtils/elementPositionInRow.d.ts +10 -0
- package/dist/DomUtils/focusElement.d.ts +5 -0
- package/dist/DomUtils/getActiveElement.d.ts +2 -0
- package/dist/DomUtils/isDarkTheme.d.ts +1 -0
- package/dist/DomUtils/keyboardNavigation.d.ts +8 -0
- package/dist/DomUtils/scrollTo.d.ts +3 -1
- package/dist/DomUtils/selectors.d.ts +24 -11
- package/dist/EmojiPickerReact.d.ts +6 -0
- package/dist/components/ErrorBoundary.d.ts +15 -0
- package/dist/components/body/{RecentlyUsed.d.ts → Suggested.d.ts} +1 -1
- package/dist/components/context/ElementRefContext.d.ts +10 -13
- package/dist/components/context/PickerContext.d.ts +3 -3
- package/dist/components/emoji/Emoji.d.ts +11 -7
- package/dist/components/emoji/ExportedEmoji.d.ts +8 -0
- package/dist/components/footer/Preview.d.ts +1 -1
- package/dist/config/categoryConfig.d.ts +7 -3
- package/dist/config/config.d.ts +18 -15
- package/dist/config/useConfig.d.ts +11 -4
- package/dist/data/skinToneVariations.d.ts +2 -0
- package/dist/dataUtils/emojiSelectors.d.ts +3 -2
- package/dist/dataUtils/suggested.d.ts +11 -0
- package/dist/emoji-picker-react.cjs.development.css +9 -9
- package/dist/emoji-picker-react.cjs.development.js +3774 -3070
- 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 +3784 -3080
- package/dist/emoji-picker-react.esm.js.map +1 -1
- package/dist/hooks/preloadEmoji.d.ts +1 -1
- package/dist/hooks/useActiveCategoryScrollDetection.d.ts +1 -2
- package/dist/hooks/useCloseAllOpenToggles.d.ts +2 -4
- package/dist/hooks/useDebouncedState.d.ts +1 -1
- package/dist/hooks/useDisallowMouseMove.d.ts +4 -0
- package/dist/hooks/useEmojiPreviewEvents.d.ts +3 -0
- package/dist/hooks/useFilter.d.ts +3 -3
- package/dist/hooks/useFocus.d.ts +3 -0
- package/dist/hooks/useInitialLoad.d.ts +1 -1
- package/dist/hooks/useIsEmojiHidden.d.ts +7 -1
- package/dist/hooks/useKeyboardNavigation.d.ts +1 -0
- package/dist/hooks/useMouseDownHandlers.d.ts +3 -2
- package/dist/hooks/useOnFocus.d.ts +1 -0
- package/dist/hooks/useScrollCategoryIntoView.d.ts +1 -0
- package/dist/hooks/useSetVariationPicker.d.ts +2 -0
- package/dist/index.d.ts +2 -2
- package/dist/types/exposedTypes.d.ts +5 -1
- package/package.json +13 -5
- package/src/DomUtils/classNames.ts +12 -1
- package/src/DomUtils/elementPositionInRow.ts +167 -0
- package/src/DomUtils/focusElement.ts +35 -0
- package/src/DomUtils/getActiveElement.ts +5 -0
- package/src/DomUtils/isDarkTheme.ts +7 -0
- package/src/DomUtils/keyboardNavigation.ts +161 -0
- package/src/DomUtils/scrollTo.ts +22 -1
- package/src/DomUtils/selectors.ts +200 -41
- package/src/EmojiPickerReact.css +99 -48
- package/src/EmojiPickerReact.tsx +26 -0
- package/src/components/ErrorBoundary.tsx +28 -0
- package/src/components/body/Body.tsx +3 -2
- package/src/components/body/EmojiCategory.css +14 -28
- package/src/components/body/EmojiCategory.tsx +5 -4
- package/src/components/body/EmojiList.tsx +13 -13
- package/src/components/body/EmojiVariationPicker.css +13 -5
- package/src/components/body/EmojiVariationPicker.tsx +25 -17
- package/src/components/body/Suggested.tsx +54 -0
- package/src/components/context/ElementRefContext.tsx +54 -19
- package/src/components/context/PickerContext.tsx +36 -33
- package/src/components/emoji/Emoji.css +42 -12
- package/src/components/emoji/Emoji.tsx +73 -34
- package/src/components/emoji/ExportedEmoji.tsx +30 -0
- package/src/components/footer/Preview.css +1 -1
- package/src/components/footer/Preview.tsx +16 -23
- package/src/components/header/Search.css +7 -7
- package/src/components/header/Search.tsx +31 -7
- package/src/components/header/SkinTonePicker.css +15 -8
- package/src/components/header/SkinTonePicker.tsx +23 -5
- package/src/components/main/PickerMain.css +0 -2
- package/src/components/main/PickerMain.tsx +14 -1
- package/src/components/navigation/CategoryNavigation.css +41 -10
- package/src/components/navigation/CategoryNavigation.tsx +22 -10
- package/src/components/navigation/svg/CategoryNav.svg +343 -379
- package/src/config/categoryConfig.ts +40 -14
- package/src/config/config.ts +59 -13
- package/src/config/useConfig.ts +35 -7
- package/src/data/emojis.json +1 -1
- package/src/data/skinToneVariations.ts +19 -0
- package/src/dataUtils/emojiSelectors.ts +25 -5
- package/src/dataUtils/{recentlyUsed.ts → suggested.ts} +20 -18
- package/src/hooks/preloadEmoji.ts +13 -2
- package/src/hooks/useActiveCategoryScrollDetection.ts +10 -8
- package/src/hooks/useCloseAllOpenToggles.ts +15 -4
- package/src/hooks/useDebouncedState.ts +10 -7
- package/src/hooks/useDisallowMouseMove.ts +47 -0
- package/src/hooks/useEmojiPreviewEvents.ts +103 -0
- package/src/hooks/useFilter.ts +46 -47
- package/src/hooks/useFocus.ts +40 -0
- package/src/hooks/useInitialLoad.ts +1 -0
- package/src/hooks/useIsEmojiHidden.ts +16 -3
- package/src/hooks/useKeyboardNavigation.ts +438 -0
- package/src/hooks/useMouseDownHandlers.ts +97 -89
- package/src/hooks/useOnFocus.ts +46 -0
- package/src/hooks/useOnScroll.ts +2 -2
- package/src/hooks/useScrollCategoryIntoView.ts +28 -0
- package/src/hooks/useSetVariationPicker.ts +28 -0
- package/src/index.tsx +9 -17
- package/src/types/exposedTypes.ts +6 -1
- package/dist/DomUtils/emojiElementRef.d.ts +0 -10
- package/dist/DomUtils/scrollCategoryIntoView.d.ts +0 -1
- package/dist/dataUtils/recentlyUsed.d.ts +0 -11
- package/dist/hooks/useEmojiElements.d.ts +0 -1
- package/dist/hooks/useEmojiMouseEnter.d.ts +0 -3
- package/src/DomUtils/emojiElementRef.ts +0 -42
- package/src/DomUtils/scrollCategoryIntoView.ts +0 -18
- package/src/components/body/RecentlyUsed.tsx +0 -50
- package/src/hooks/useEmojiElements.ts +0 -7
- package/src/hooks/useEmojiMouseEnter.ts +0 -59
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
} from '../../config/categoryConfig';
|
|
8
8
|
import {
|
|
9
9
|
useCategoriesConfig,
|
|
10
|
-
useEmojiStyleConfig
|
|
10
|
+
useEmojiStyleConfig,
|
|
11
|
+
useLazyLoadEmojisConfig
|
|
11
12
|
} from '../../config/useConfig';
|
|
12
13
|
import { emojisByCategory, emojiUnified } from '../../dataUtils/emojiSelectors';
|
|
13
14
|
import { useIsEmojiHidden } from '../../hooks/useIsEmojiHidden';
|
|
@@ -15,12 +16,12 @@ import {
|
|
|
15
16
|
useActiveSkinToneState,
|
|
16
17
|
useIsPastInitialLoad
|
|
17
18
|
} from '../context/PickerContext';
|
|
18
|
-
import {
|
|
19
|
+
import { ClickableEmoji } from '../emoji/Emoji';
|
|
19
20
|
|
|
20
21
|
import { EmojiCategory } from './EmojiCategory';
|
|
21
|
-
import {
|
|
22
|
+
import { Suggested } from './Suggested';
|
|
23
|
+
|
|
22
24
|
import './EmojiList.css';
|
|
23
|
-
import { getEmojiRef } from '../../DomUtils/emojiElementRef';
|
|
24
25
|
|
|
25
26
|
export function EmojiList() {
|
|
26
27
|
const categories = useCategoriesConfig();
|
|
@@ -30,10 +31,8 @@ export function EmojiList() {
|
|
|
30
31
|
{categories.map((categoryConfig, index) => {
|
|
31
32
|
const category = categoryFromCategoryConfig(categoryConfig);
|
|
32
33
|
|
|
33
|
-
if (category === Categories.
|
|
34
|
-
return
|
|
35
|
-
<RecentlyUsed key={category} categoryConfig={categoryConfig} />
|
|
36
|
-
);
|
|
34
|
+
if (category === Categories.SUGGESTED) {
|
|
35
|
+
return <Suggested key={category} categoryConfig={categoryConfig} />;
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
return (
|
|
@@ -59,6 +58,7 @@ function RenderCategory({
|
|
|
59
58
|
categoryConfig: CategoryConfig;
|
|
60
59
|
}) {
|
|
61
60
|
const isEmojiHidden = useIsEmojiHidden();
|
|
61
|
+
const lazyLoadEmojis = useLazyLoadEmojisConfig();
|
|
62
62
|
const emojiStyle = useEmojiStyleConfig();
|
|
63
63
|
const isPastInitialLoad = useIsPastInitialLoad();
|
|
64
64
|
const [activeSkinTone] = useActiveSkinToneState();
|
|
@@ -72,21 +72,21 @@ function RenderCategory({
|
|
|
72
72
|
|
|
73
73
|
const emojis = emojisToPush.map(emoji => {
|
|
74
74
|
const unified = emojiUnified(emoji, activeSkinTone);
|
|
75
|
-
const hidden = isEmojiHidden(emoji);
|
|
76
|
-
const emojiRef = getEmojiRef(emojiUnified(emoji));
|
|
75
|
+
const { failedToLoad, filteredOut, hidden } = isEmojiHidden(emoji);
|
|
77
76
|
|
|
78
77
|
if (hidden) {
|
|
79
78
|
hiddenCounter++;
|
|
80
79
|
}
|
|
81
80
|
|
|
82
81
|
return (
|
|
83
|
-
<
|
|
82
|
+
<ClickableEmoji
|
|
84
83
|
key={unified}
|
|
85
84
|
emoji={emoji}
|
|
86
85
|
unified={unified}
|
|
87
|
-
hidden={
|
|
86
|
+
hidden={failedToLoad}
|
|
87
|
+
hiddenOnSearch={filteredOut}
|
|
88
88
|
emojiStyle={emojiStyle}
|
|
89
|
-
|
|
89
|
+
lazyLoad={lazyLoadEmojis}
|
|
90
90
|
/>
|
|
91
91
|
);
|
|
92
92
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
.EmojiPickerReact .epr-emoji-variation-picker {
|
|
2
2
|
position: absolute;
|
|
3
3
|
top: 0;
|
|
4
|
-
right:
|
|
5
|
-
left:
|
|
4
|
+
right: 15px;
|
|
5
|
+
left: 15px;
|
|
6
6
|
padding: 5px;
|
|
7
7
|
height: 0;
|
|
8
8
|
box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2);
|
|
@@ -18,12 +18,20 @@
|
|
|
18
18
|
height: var(--epr-emoji-variation-picker-height);
|
|
19
19
|
z-index: var(--epr-skin-variation-picker-z-index);
|
|
20
20
|
background: var(--epr-emoji-variation-picker-bg-color);
|
|
21
|
+
transform: scale(0.9);
|
|
22
|
+
transition: transform 0.1s ease-out, opacity 0.2s ease-out;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.EmojiPickerReact .epr-emoji-variation-picker.pointing-up {
|
|
26
|
+
transform-origin: center 0%;
|
|
27
|
+
transform: scale(0.9);
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
.EmojiPickerReact .epr-emoji-variation-picker.visible {
|
|
24
31
|
opacity: 1;
|
|
25
32
|
visibility: visible;
|
|
26
33
|
pointer-events: all;
|
|
34
|
+
transform: scale(1);
|
|
27
35
|
}
|
|
28
36
|
.EmojiPickerReact
|
|
29
37
|
.epr-emoji-variation-picker
|
|
@@ -56,10 +64,10 @@
|
|
|
56
64
|
|
|
57
65
|
.EmojiPickerReact .epr-emoji-variation-picker .epr-emoji-pointer {
|
|
58
66
|
top: 100%;
|
|
59
|
-
transform: translateX(-
|
|
67
|
+
transform: translateX(-18px);
|
|
60
68
|
}
|
|
61
69
|
|
|
62
|
-
.EmojiPickerReact .epr-emoji-variation-picker .epr-emoji-pointer
|
|
70
|
+
.EmojiPickerReact .epr-emoji-variation-picker.pointing-up .epr-emoji-pointer {
|
|
63
71
|
top: 0;
|
|
64
|
-
transform: rotate(180deg) translateY(100%) translateX(
|
|
72
|
+
transform: rotate(180deg) translateY(100%) translateX(18px);
|
|
65
73
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
|
|
4
|
+
import { ClassNames } from '../../DomUtils/classNames';
|
|
5
|
+
import { focusElement } from '../../DomUtils/focusElement';
|
|
4
6
|
import {
|
|
5
|
-
|
|
7
|
+
buttonFromTarget,
|
|
6
8
|
elementHeight,
|
|
7
9
|
emojiTrueOffsetTop,
|
|
8
10
|
emojiTruOffsetLeft
|
|
@@ -17,10 +19,11 @@ import {
|
|
|
17
19
|
import {
|
|
18
20
|
useAnchoredEmojiRef,
|
|
19
21
|
useBodyRef,
|
|
20
|
-
useSetAnchoredEmojiRef
|
|
22
|
+
useSetAnchoredEmojiRef,
|
|
23
|
+
useVariationPickerRef
|
|
21
24
|
} from '../context/ElementRefContext';
|
|
22
25
|
import { useEmojiVariationPickerState } from '../context/PickerContext';
|
|
23
|
-
import {
|
|
26
|
+
import { ClickableEmoji } from '../emoji/Emoji';
|
|
24
27
|
import './EmojiVariationPicker.css';
|
|
25
28
|
|
|
26
29
|
enum Direction {
|
|
@@ -29,7 +32,8 @@ enum Direction {
|
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
export function EmojiVariationPicker() {
|
|
32
|
-
const
|
|
35
|
+
const AnchoredEmojiRef = useAnchoredEmojiRef();
|
|
36
|
+
const VariationPickerRef = useVariationPickerRef();
|
|
33
37
|
const [emoji] = useEmojiVariationPickerState();
|
|
34
38
|
const emojiStyle = useEmojiStyleConfig();
|
|
35
39
|
const { getTop, getMenuDirection } = useVariationPickerTop(
|
|
@@ -38,10 +42,18 @@ export function EmojiVariationPicker() {
|
|
|
38
42
|
const setAnchoredEmojiRef = useSetAnchoredEmojiRef();
|
|
39
43
|
const getPointerStyle = usePointerStyle(VariationPickerRef);
|
|
40
44
|
|
|
41
|
-
const
|
|
45
|
+
const button = buttonFromTarget(AnchoredEmojiRef.current);
|
|
46
|
+
|
|
47
|
+
const visible =
|
|
48
|
+
emoji &&
|
|
49
|
+
button &&
|
|
50
|
+
emojiHasVariations(emoji) &&
|
|
51
|
+
button.classList.contains(ClassNames.emojiHasVariatios);
|
|
52
|
+
|
|
42
53
|
let top, pointerStyle;
|
|
43
54
|
|
|
44
|
-
if (!visible) {
|
|
55
|
+
if (!visible && AnchoredEmojiRef.current) {
|
|
56
|
+
focusElement(AnchoredEmojiRef.current);
|
|
45
57
|
setAnchoredEmojiRef(null);
|
|
46
58
|
} else {
|
|
47
59
|
top = getTop();
|
|
@@ -54,7 +66,8 @@ export function EmojiVariationPicker() {
|
|
|
54
66
|
<div
|
|
55
67
|
ref={VariationPickerRef}
|
|
56
68
|
className={clsx('epr-emoji-variation-picker', {
|
|
57
|
-
visible
|
|
69
|
+
visible,
|
|
70
|
+
'pointing-up': getMenuDirection() === Direction.Down
|
|
58
71
|
})}
|
|
59
72
|
style={{ top }}
|
|
60
73
|
>
|
|
@@ -63,7 +76,7 @@ export function EmojiVariationPicker() {
|
|
|
63
76
|
.concat(emojiVariations(safeEmoji))
|
|
64
77
|
.slice(0, 6)
|
|
65
78
|
.map(unified => (
|
|
66
|
-
<
|
|
79
|
+
<ClickableEmoji
|
|
67
80
|
key={unified}
|
|
68
81
|
emoji={safeEmoji}
|
|
69
82
|
unified={unified}
|
|
@@ -72,12 +85,7 @@ export function EmojiVariationPicker() {
|
|
|
72
85
|
/>
|
|
73
86
|
))
|
|
74
87
|
: null}
|
|
75
|
-
<div
|
|
76
|
-
className={clsx('epr-emoji-pointer', {
|
|
77
|
-
'pointing-up': getMenuDirection() === Direction.Down
|
|
78
|
-
})}
|
|
79
|
-
style={pointerStyle}
|
|
80
|
-
/>
|
|
88
|
+
<div className="epr-emoji-pointer" style={pointerStyle} />
|
|
81
89
|
</div>
|
|
82
90
|
);
|
|
83
91
|
}
|
|
@@ -91,7 +99,7 @@ function usePointerStyle(VariationPickerRef: React.RefObject<HTMLElement>) {
|
|
|
91
99
|
}
|
|
92
100
|
|
|
93
101
|
if (AnchoredEmojiRef.current) {
|
|
94
|
-
const button =
|
|
102
|
+
const button = buttonFromTarget(AnchoredEmojiRef.current);
|
|
95
103
|
|
|
96
104
|
const offsetLeft = emojiTruOffsetLeft(button);
|
|
97
105
|
|
|
@@ -100,7 +108,7 @@ function usePointerStyle(VariationPickerRef: React.RefObject<HTMLElement>) {
|
|
|
100
108
|
}
|
|
101
109
|
|
|
102
110
|
// half of the button
|
|
103
|
-
style.left = offsetLeft + button?.clientWidth / 2
|
|
111
|
+
style.left = offsetLeft + button?.clientWidth / 2;
|
|
104
112
|
}
|
|
105
113
|
|
|
106
114
|
return style;
|
|
@@ -135,7 +143,7 @@ function useVariationPickerTop(
|
|
|
135
143
|
|
|
136
144
|
if (AnchoredEmojiRef.current) {
|
|
137
145
|
const bodyRef = BodyRef.current;
|
|
138
|
-
const button =
|
|
146
|
+
const button = buttonFromTarget(AnchoredEmojiRef.current);
|
|
139
147
|
|
|
140
148
|
const buttonHeight = elementHeight(button);
|
|
141
149
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { CategoryConfig } from '../../config/categoryConfig';
|
|
4
|
+
import {
|
|
5
|
+
useEmojiStyleConfig,
|
|
6
|
+
useSuggestedEmojisModeConfig
|
|
7
|
+
} from '../../config/useConfig';
|
|
8
|
+
import { emojiByUnified } from '../../dataUtils/emojiSelectors';
|
|
9
|
+
import { getsuggested } from '../../dataUtils/suggested';
|
|
10
|
+
import { useUpdateSuggested } from '../context/PickerContext';
|
|
11
|
+
import { ClickableEmoji } from '../emoji/Emoji';
|
|
12
|
+
|
|
13
|
+
import { EmojiCategory } from './EmojiCategory';
|
|
14
|
+
|
|
15
|
+
type Props = Readonly<{
|
|
16
|
+
categoryConfig: CategoryConfig;
|
|
17
|
+
}>;
|
|
18
|
+
|
|
19
|
+
export function Suggested({ categoryConfig }: Props) {
|
|
20
|
+
const [suggestedUpdated] = useUpdateSuggested();
|
|
21
|
+
const suggestedEmojisModeConfig = useSuggestedEmojisModeConfig();
|
|
22
|
+
const suggested = React.useMemo(
|
|
23
|
+
() => getsuggested(suggestedEmojisModeConfig) ?? [],
|
|
24
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
25
|
+
[suggestedUpdated, suggestedEmojisModeConfig]
|
|
26
|
+
);
|
|
27
|
+
const emojiStyle = useEmojiStyleConfig();
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<EmojiCategory
|
|
31
|
+
categoryConfig={categoryConfig}
|
|
32
|
+
hiddenOnSearch
|
|
33
|
+
hidden={suggested.length === 0}
|
|
34
|
+
>
|
|
35
|
+
{suggested.map(suggestedItem => {
|
|
36
|
+
const emoji = emojiByUnified(suggestedItem.original);
|
|
37
|
+
|
|
38
|
+
if (!emoji) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<ClickableEmoji
|
|
44
|
+
showVariations={false}
|
|
45
|
+
unified={suggestedItem.unified}
|
|
46
|
+
emojiStyle={emojiStyle}
|
|
47
|
+
emoji={emoji}
|
|
48
|
+
key={suggestedItem.unified}
|
|
49
|
+
/>
|
|
50
|
+
);
|
|
51
|
+
})}
|
|
52
|
+
</EmojiCategory>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
+
import { NullableElement } from '../../DomUtils/selectors';
|
|
4
|
+
|
|
3
5
|
export function ElementRefContextProvider({
|
|
4
6
|
children
|
|
5
7
|
}: {
|
|
@@ -7,55 +9,88 @@ export function ElementRefContextProvider({
|
|
|
7
9
|
}) {
|
|
8
10
|
const PickerMainRef = React.useRef<HTMLElement>(null);
|
|
9
11
|
const AnchoredEmojiRef = React.useRef<HTMLElement>(null);
|
|
10
|
-
const BodyRef = React.useRef<
|
|
12
|
+
const BodyRef = React.useRef<HTMLDivElement>(null);
|
|
13
|
+
const SearchInputRef = React.useRef<HTMLInputElement>(null);
|
|
14
|
+
const SkinTonePickerRef = React.useRef<HTMLDivElement>(null);
|
|
15
|
+
const CategoryNavigationRef = React.useRef<HTMLDivElement>(null);
|
|
16
|
+
const VariationPickerRef = React.useRef<HTMLDivElement>(null);
|
|
11
17
|
|
|
12
18
|
return (
|
|
13
19
|
<ElementRefContext.Provider
|
|
14
|
-
value={{
|
|
20
|
+
value={{
|
|
21
|
+
AnchoredEmojiRef,
|
|
22
|
+
BodyRef,
|
|
23
|
+
CategoryNavigationRef,
|
|
24
|
+
PickerMainRef,
|
|
25
|
+
SearchInputRef,
|
|
26
|
+
SkinTonePickerRef,
|
|
27
|
+
VariationPickerRef
|
|
28
|
+
}}
|
|
15
29
|
>
|
|
16
30
|
{children}
|
|
17
31
|
</ElementRefContext.Provider>
|
|
18
32
|
);
|
|
19
33
|
}
|
|
20
34
|
|
|
21
|
-
export type ElementRef
|
|
35
|
+
export type ElementRef<
|
|
36
|
+
E extends HTMLElement = HTMLElement
|
|
37
|
+
> = React.MutableRefObject<E | null>;
|
|
22
38
|
|
|
23
39
|
type ElementRefs = {
|
|
24
40
|
PickerMainRef: ElementRef;
|
|
25
41
|
AnchoredEmojiRef: ElementRef;
|
|
26
|
-
|
|
42
|
+
SkinTonePickerRef: ElementRef<HTMLDivElement>;
|
|
43
|
+
SearchInputRef: ElementRef<HTMLInputElement>;
|
|
44
|
+
BodyRef: ElementRef<HTMLDivElement>;
|
|
45
|
+
CategoryNavigationRef: ElementRef<HTMLDivElement>;
|
|
46
|
+
VariationPickerRef: ElementRef<HTMLDivElement>;
|
|
27
47
|
};
|
|
28
48
|
|
|
29
49
|
const ElementRefContext = React.createContext<ElementRefs>({
|
|
30
|
-
PickerMainRef: React.createRef(),
|
|
31
50
|
AnchoredEmojiRef: React.createRef(),
|
|
32
|
-
BodyRef: React.createRef()
|
|
51
|
+
BodyRef: React.createRef(),
|
|
52
|
+
CategoryNavigationRef: React.createRef(),
|
|
53
|
+
PickerMainRef: React.createRef(),
|
|
54
|
+
SearchInputRef: React.createRef(),
|
|
55
|
+
SkinTonePickerRef: React.createRef(),
|
|
56
|
+
VariationPickerRef: React.createRef()
|
|
33
57
|
});
|
|
34
58
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
export function useElementRef(key: ElementRefKeys) {
|
|
38
|
-
const { [key]: elementRef } = React.useContext(ElementRefContext);
|
|
39
|
-
return elementRef;
|
|
59
|
+
function useElementRef() {
|
|
60
|
+
return React.useContext(ElementRefContext);
|
|
40
61
|
}
|
|
41
62
|
|
|
42
63
|
export function usePickerMainRef() {
|
|
43
|
-
return useElementRef('PickerMainRef'
|
|
64
|
+
return useElementRef()['PickerMainRef'];
|
|
44
65
|
}
|
|
45
66
|
|
|
46
67
|
export function useAnchoredEmojiRef() {
|
|
47
|
-
return useElementRef('AnchoredEmojiRef'
|
|
68
|
+
return useElementRef()['AnchoredEmojiRef'];
|
|
48
69
|
}
|
|
49
70
|
|
|
50
|
-
export function useSetAnchoredEmojiRef(): (target:
|
|
71
|
+
export function useSetAnchoredEmojiRef(): (target: NullableElement) => void {
|
|
51
72
|
const AnchoredEmojiRef = useAnchoredEmojiRef();
|
|
52
|
-
return (target:
|
|
73
|
+
return (target: NullableElement) => {
|
|
53
74
|
AnchoredEmojiRef.current = target;
|
|
54
75
|
};
|
|
55
76
|
}
|
|
56
77
|
|
|
57
|
-
export function useBodyRef()
|
|
58
|
-
return useElementRef(
|
|
59
|
-
|
|
60
|
-
|
|
78
|
+
export function useBodyRef() {
|
|
79
|
+
return useElementRef()['BodyRef'];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function useSearchInputRef() {
|
|
83
|
+
return useElementRef()['SearchInputRef'];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function useSkinTonePickerRef() {
|
|
87
|
+
return useElementRef()['SkinTonePickerRef'];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function useCategoryNavigationRef() {
|
|
91
|
+
return useElementRef()['CategoryNavigationRef'];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function useVariationPickerRef() {
|
|
95
|
+
return useElementRef()['VariationPickerRef'];
|
|
61
96
|
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
+
|
|
3
4
|
import { useDefaultSkinToneConfig } from '../../config/useConfig';
|
|
4
|
-
import { alphaNumericEmojiIndex } from '../../dataUtils/alphaNumericEmojiIndex';
|
|
5
5
|
import { DataEmoji } from '../../dataUtils/DataTypes';
|
|
6
|
-
import {
|
|
6
|
+
import { alphaNumericEmojiIndex } from '../../dataUtils/alphaNumericEmojiIndex';
|
|
7
7
|
import { useDebouncedState } from '../../hooks/useDebouncedState';
|
|
8
8
|
import { FilterDict } from '../../hooks/useFilter';
|
|
9
9
|
import { useMarkInitialLoad } from '../../hooks/useInitialLoad';
|
|
10
10
|
import { SkinTones } from '../../types/exposedTypes';
|
|
11
|
-
import { usePickerMainRef } from './ElementRefContext';
|
|
12
11
|
|
|
13
12
|
export function PickerContextProvider({ children }: Props) {
|
|
14
13
|
const defaultSkinTone = useDefaultSkinToneConfig();
|
|
@@ -16,7 +15,9 @@ export function PickerContextProvider({ children }: Props) {
|
|
|
16
15
|
// Initialize the filter with the inititial dictionary
|
|
17
16
|
const filterRef = React.useRef<FilterState>(alphaNumericEmojiIndex);
|
|
18
17
|
const disallowClickRef = React.useRef<boolean>(false);
|
|
18
|
+
const disallowMouseRef = React.useRef<boolean>(false);
|
|
19
19
|
|
|
20
|
+
const suggestedUpdateState = useDebouncedState(Date.now(), 200);
|
|
20
21
|
const searchTerm = useDebouncedState('', 100);
|
|
21
22
|
const skinToneFanOpenState = useState<boolean>(false);
|
|
22
23
|
const activeSkinTone = useState<SkinTones>(defaultSkinTone);
|
|
@@ -30,15 +31,17 @@ export function PickerContextProvider({ children }: Props) {
|
|
|
30
31
|
return (
|
|
31
32
|
<PickerContext.Provider
|
|
32
33
|
value={{
|
|
33
|
-
filterRef,
|
|
34
|
-
searchTerm,
|
|
35
34
|
activeCategoryState,
|
|
36
35
|
activeSkinTone,
|
|
36
|
+
disallowClickRef,
|
|
37
|
+
disallowMouseRef,
|
|
38
|
+
emojiVariationPickerState,
|
|
37
39
|
emojisThatFailedToLoadState,
|
|
40
|
+
filterRef,
|
|
38
41
|
isPastInitialLoad,
|
|
39
|
-
|
|
42
|
+
searchTerm,
|
|
40
43
|
skinToneFanOpenState,
|
|
41
|
-
|
|
44
|
+
suggestedUpdateState
|
|
42
45
|
}}
|
|
43
46
|
>
|
|
44
47
|
{children}
|
|
@@ -49,7 +52,8 @@ export function PickerContextProvider({ children }: Props) {
|
|
|
49
52
|
type ReactState<T> = [T, React.Dispatch<React.SetStateAction<T>>];
|
|
50
53
|
|
|
51
54
|
const PickerContext = React.createContext<{
|
|
52
|
-
searchTerm: [string, (term: string) =>
|
|
55
|
+
searchTerm: [string, (term: string) => Promise<string>];
|
|
56
|
+
suggestedUpdateState: [number, (term: number) => void];
|
|
53
57
|
activeCategoryState: ReactState<ActiveCategoryState>;
|
|
54
58
|
activeSkinTone: ReactState<SkinTones>;
|
|
55
59
|
emojisThatFailedToLoadState: ReactState<Set<string>>;
|
|
@@ -58,16 +62,19 @@ const PickerContext = React.createContext<{
|
|
|
58
62
|
skinToneFanOpenState: ReactState<boolean>;
|
|
59
63
|
filterRef: React.MutableRefObject<FilterState>;
|
|
60
64
|
disallowClickRef: React.MutableRefObject<boolean>;
|
|
65
|
+
disallowMouseRef: React.MutableRefObject<boolean>;
|
|
61
66
|
}>({
|
|
62
|
-
searchTerm: ['', () => {}],
|
|
63
67
|
activeCategoryState: [null, () => {}],
|
|
64
68
|
activeSkinTone: [SkinTones.NEUTRAL, () => {}],
|
|
69
|
+
disallowClickRef: { current: false },
|
|
70
|
+
disallowMouseRef: { current: false },
|
|
71
|
+
emojiVariationPickerState: [null, () => {}],
|
|
65
72
|
emojisThatFailedToLoadState: [new Set(), () => {}],
|
|
73
|
+
filterRef: { current: {} },
|
|
66
74
|
isPastInitialLoad: true,
|
|
67
|
-
|
|
75
|
+
searchTerm: ['', () => new Promise<string>(() => undefined)],
|
|
68
76
|
skinToneFanOpenState: [false, () => {}],
|
|
69
|
-
|
|
70
|
-
disallowClickRef: { current: false }
|
|
77
|
+
suggestedUpdateState: [Date.now(), () => {}]
|
|
71
78
|
});
|
|
72
79
|
|
|
73
80
|
type Props = Readonly<{
|
|
@@ -84,32 +91,16 @@ export function useDisallowClickRef() {
|
|
|
84
91
|
return disallowClickRef;
|
|
85
92
|
}
|
|
86
93
|
|
|
94
|
+
export function useDisallowMouseRef() {
|
|
95
|
+
const { disallowMouseRef } = React.useContext(PickerContext);
|
|
96
|
+
return disallowMouseRef;
|
|
97
|
+
}
|
|
98
|
+
|
|
87
99
|
export function useSearchTermState() {
|
|
88
100
|
const { searchTerm } = React.useContext(PickerContext);
|
|
89
101
|
return searchTerm;
|
|
90
102
|
}
|
|
91
103
|
|
|
92
|
-
export function useActiveCategoryState(): [
|
|
93
|
-
ActiveCategoryState,
|
|
94
|
-
(nextActive: string) => void,
|
|
95
|
-
(nextActive: string) => void
|
|
96
|
-
] {
|
|
97
|
-
const { activeCategoryState } = React.useContext(PickerContext);
|
|
98
|
-
const PickerMainRef = usePickerMainRef();
|
|
99
|
-
|
|
100
|
-
const [activeCategory, setActiveCategory] = activeCategoryState;
|
|
101
|
-
return [activeCategory, setCategory, setActiveCategoryAndScroll];
|
|
102
|
-
|
|
103
|
-
function setCategory(category: string) {
|
|
104
|
-
setActiveCategory(category);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function setActiveCategoryAndScroll(category: string) {
|
|
108
|
-
setCategory(category);
|
|
109
|
-
scrollCategoryIntoView(PickerMainRef.current, category);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
104
|
export function useActiveSkinToneState(): [
|
|
114
105
|
SkinTones,
|
|
115
106
|
(skinTone: SkinTones) => void
|
|
@@ -138,6 +129,18 @@ export function useSkinToneFanOpenState() {
|
|
|
138
129
|
return skinToneFanOpenState;
|
|
139
130
|
}
|
|
140
131
|
|
|
132
|
+
export function useUpdateSuggested(): [number, () => void] {
|
|
133
|
+
const { suggestedUpdateState } = React.useContext(PickerContext);
|
|
134
|
+
|
|
135
|
+
const [suggestedUpdated, setsuggestedUpdate] = suggestedUpdateState;
|
|
136
|
+
return [
|
|
137
|
+
suggestedUpdated,
|
|
138
|
+
function updateSuggested() {
|
|
139
|
+
setsuggestedUpdate(Date.now());
|
|
140
|
+
}
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
|
|
141
144
|
export type FilterState = Record<string, FilterDict>;
|
|
142
145
|
|
|
143
146
|
type ActiveCategoryState = null | string;
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
.EmojiPickerReact button.epr-emoji {
|
|
2
2
|
position: relative;
|
|
3
|
-
|
|
3
|
+
width: var(--epr-emoji-fullsize);
|
|
4
|
+
height: var(--epr-emoji-fullsize);
|
|
5
|
+
box-sizing: border-box;
|
|
6
|
+
display: flex;
|
|
7
|
+
align-items: center;
|
|
8
|
+
justify-content: center;
|
|
9
|
+
max-width: var(--epr-emoji-fullsize);
|
|
10
|
+
max-height: var(--epr-emoji-fullsize);
|
|
11
|
+
border-radius: 8px;
|
|
12
|
+
overflow: hidden;
|
|
4
13
|
}
|
|
5
14
|
|
|
6
15
|
.EmojiPickerReact button.epr-emoji.epr-emoji-has-variations:after {
|
|
@@ -8,8 +17,8 @@
|
|
|
8
17
|
display: block;
|
|
9
18
|
width: 0;
|
|
10
19
|
height: 0;
|
|
11
|
-
right:
|
|
12
|
-
bottom:
|
|
20
|
+
right: 0px;
|
|
21
|
+
bottom: 1px;
|
|
13
22
|
position: absolute;
|
|
14
23
|
border-left: 4px solid transparent;
|
|
15
24
|
border-right: 4px solid transparent;
|
|
@@ -18,27 +27,48 @@
|
|
|
18
27
|
z-index: var(--epr-emoji-variations-indictator-z-index);
|
|
19
28
|
}
|
|
20
29
|
|
|
30
|
+
.EmojiPickerReact button.epr-emoji .epr-emoji-img {
|
|
31
|
+
max-width: var(--epr-emoji-fullsize);
|
|
32
|
+
max-height: var(--epr-emoji-fullsize);
|
|
33
|
+
padding: var(--epr-emoji-padding);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.EmojiPickerReact button.epr-emoji .epr-emoji-native {
|
|
37
|
+
padding: var(--epr-emoji-padding);
|
|
38
|
+
}
|
|
39
|
+
|
|
21
40
|
.EmojiPickerReact button.epr-emoji > * {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
41
|
+
align-self: center;
|
|
42
|
+
justify-self: center;
|
|
43
|
+
display: block;
|
|
25
44
|
}
|
|
26
45
|
|
|
27
46
|
.EmojiPickerReact button.epr-emoji.epr-emoji-has-variations:hover:after {
|
|
28
47
|
border-bottom: 4px solid var(--epr-emoji-variation-indicator-color-hover);
|
|
29
48
|
}
|
|
30
49
|
|
|
31
|
-
.
|
|
50
|
+
.__EmojiPicker__.epr-emoji-img {
|
|
32
51
|
font-size: 0;
|
|
33
52
|
}
|
|
34
53
|
|
|
35
|
-
.
|
|
36
|
-
font-family: '
|
|
37
|
-
|
|
54
|
+
.__EmojiPicker__.epr-emoji-native {
|
|
55
|
+
font-family: 'Segoe UI Emoji', 'Segoe UI Symbol', 'Segoe UI',
|
|
56
|
+
'Apple Color Emoji', 'Twemoji Mozilla', 'Noto Color Emoji', 'Android Emoji',
|
|
57
|
+
Times, Symbola, Aegyptus, Code2000, Code2001, Code2002, Musica, serif,
|
|
58
|
+
LastResort;
|
|
59
|
+
position: relative;
|
|
38
60
|
line-height: 100%;
|
|
61
|
+
font-size: var(--epr-emoji-size);
|
|
62
|
+
text-align: center;
|
|
63
|
+
align-self: center;
|
|
64
|
+
justify-self: center;
|
|
65
|
+
letter-spacing: 0;
|
|
39
66
|
}
|
|
40
67
|
|
|
41
|
-
.EmojiPickerReact button.epr-emoji:hover {
|
|
42
|
-
border-radius: 8px;
|
|
68
|
+
.EmojiPickerReact button.epr-emoji:hover > * {
|
|
43
69
|
background-color: var(--epr-emoji-hover-color);
|
|
44
70
|
}
|
|
71
|
+
|
|
72
|
+
.EmojiPickerReact button.epr-emoji:focus > * {
|
|
73
|
+
background-color: var(--epr-focus-bg-color);
|
|
74
|
+
}
|