emoji-picker-react 4.0.0-rc.19 → 4.0.0-rc.21
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 +44 -4
- package/dist/DomUtils/classNames.d.ts +6 -1
- package/dist/DomUtils/elementPositionInRow.d.ts +10 -0
- package/dist/DomUtils/emojiElementRef.d.ts +4 -3
- package/dist/DomUtils/focusElement.d.ts +5 -0
- package/dist/DomUtils/getActiveElement.d.ts +2 -0
- package/dist/DomUtils/keyboardNavigation.d.ts +8 -0
- package/dist/DomUtils/scrollCategoryIntoView.d.ts +2 -1
- package/dist/DomUtils/scrollTo.d.ts +3 -1
- package/dist/DomUtils/selectors.d.ts +23 -11
- package/dist/components/context/ElementRefContext.d.ts +9 -13
- package/dist/components/context/PickerContext.d.ts +1 -1
- package/dist/components/emoji/Emoji.d.ts +2 -2
- package/dist/components/footer/Preview.d.ts +1 -1
- package/dist/config/config.d.ts +12 -15
- package/dist/config/useConfig.d.ts +2 -1
- package/dist/dataUtils/emojiSelectors.d.ts +2 -2
- package/dist/emoji-picker-react.cjs.development.css +7 -7
- package/dist/emoji-picker-react.cjs.development.js +1119 -302
- 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 +1129 -312
- package/dist/emoji-picker-react.esm.js.map +1 -1
- package/dist/hooks/useCloseAllOpenToggles.d.ts +1 -4
- package/dist/hooks/useDebouncedState.d.ts +1 -1
- 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/useKeyboardNavigation.d.ts +1 -0
- package/dist/hooks/useMouseDownHandlers.d.ts +3 -2
- package/package.json +7 -4
- package/src/DomUtils/classNames.ts +9 -1
- package/src/DomUtils/elementPositionInRow.ts +169 -0
- package/src/DomUtils/emojiElementRef.ts +4 -3
- package/src/DomUtils/focusElement.ts +35 -0
- package/src/DomUtils/getActiveElement.ts +5 -0
- package/src/DomUtils/keyboardNavigation.ts +159 -0
- package/src/DomUtils/scrollCategoryIntoView.ts +2 -1
- package/src/DomUtils/scrollTo.ts +22 -1
- package/src/DomUtils/selectors.ts +157 -32
- package/src/EmojiPickerReact.css +57 -29
- package/src/components/ErrorBoundary.tsx +1 -0
- package/src/components/body/EmojiCategory.css +14 -20
- package/src/components/body/EmojiCategory.tsx +3 -3
- package/src/components/body/EmojiList.tsx +8 -7
- package/src/components/body/EmojiVariationPicker.tsx +3 -3
- package/src/components/body/Suggested.tsx +3 -1
- package/src/components/context/ElementRefContext.tsx +46 -19
- package/src/components/context/PickerContext.tsx +16 -14
- package/src/components/emoji/Emoji.css +7 -2
- package/src/components/emoji/Emoji.tsx +10 -10
- package/src/components/footer/Preview.css +1 -1
- package/src/components/footer/Preview.tsx +13 -16
- package/src/components/header/Search.css +7 -7
- package/src/components/header/Search.tsx +6 -4
- package/src/components/header/SkinTonePicker.css +12 -5
- package/src/components/header/SkinTonePicker.tsx +11 -3
- package/src/components/main/PickerMain.tsx +6 -2
- package/src/components/navigation/CategoryNavigation.css +11 -0
- package/src/components/navigation/CategoryNavigation.tsx +5 -2
- package/src/config/config.ts +39 -10
- package/src/config/useConfig.ts +6 -5
- package/src/dataUtils/emojiSelectors.ts +3 -2
- package/src/dataUtils/suggested.ts +1 -0
- package/src/hooks/preloadEmoji.ts +1 -1
- package/src/hooks/useActiveCategoryScrollDetection.ts +7 -4
- package/src/hooks/useCloseAllOpenToggles.ts +7 -5
- package/src/hooks/useDebouncedState.ts +10 -7
- package/src/hooks/useEmojiPreviewEvents.ts +90 -0
- package/src/hooks/useFilter.ts +41 -21
- package/src/hooks/useFocus.ts +40 -0
- package/src/hooks/useInitialLoad.ts +1 -0
- package/src/hooks/useKeyboardNavigation.ts +389 -0
- package/src/hooks/useMouseDownHandlers.ts +100 -74
- package/src/hooks/useOnScroll.ts +2 -2
- package/src/index.tsx +3 -1
- package/dist/hooks/useEmojiElements.d.ts +0 -1
- package/dist/hooks/useEmojiMouseEnter.d.ts +0 -3
- package/src/hooks/useEmojiElements.ts +0 -7
- package/src/hooks/useEmojiMouseEnter.ts +0 -59
|
@@ -4,14 +4,26 @@ import {
|
|
|
4
4
|
unifiedWithoutSkinTone
|
|
5
5
|
} from '../dataUtils/emojiSelectors';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import { asSelectors, ClassNames } from './classNames';
|
|
8
|
+
import { firstVisibleElementInContainer } from './elementPositionInRow';
|
|
9
|
+
|
|
10
|
+
export type NullableElement = HTMLElement | null;
|
|
11
|
+
|
|
12
|
+
export const EmojiButtonSelector = `button${asSelectors(ClassNames.emoji)}`;
|
|
13
|
+
export const VisibleEmojiSelector = [
|
|
14
|
+
EmojiButtonSelector,
|
|
15
|
+
asSelectors(ClassNames.visible),
|
|
16
|
+
`:not(${asSelectors(ClassNames.hidden)})`
|
|
17
|
+
].join('');
|
|
18
|
+
|
|
19
|
+
export function buttonFromTarget(
|
|
20
|
+
emojiElement: NullableElement
|
|
9
21
|
): HTMLButtonElement | null {
|
|
10
|
-
return emojiElement?.closest(
|
|
22
|
+
return emojiElement?.closest(EmojiButtonSelector) ?? null;
|
|
11
23
|
}
|
|
12
24
|
|
|
13
25
|
export function emojiFromElement(
|
|
14
|
-
element:
|
|
26
|
+
element: NullableElement
|
|
15
27
|
): [DataEmoji, string] | [] {
|
|
16
28
|
const originalUnified = originalUnifiedFromEmojiElement(element);
|
|
17
29
|
const unified = unifiedFromEmojiElement(element);
|
|
@@ -29,65 +41,57 @@ export function emojiFromElement(
|
|
|
29
41
|
return [emoji, unified as string];
|
|
30
42
|
}
|
|
31
43
|
|
|
32
|
-
export function isEmojiElement(element:
|
|
44
|
+
export function isEmojiElement(element: NullableElement): boolean {
|
|
33
45
|
return Boolean(
|
|
34
|
-
element?.matches(
|
|
35
|
-
element?.parentElement?.matches(
|
|
46
|
+
element?.matches(EmojiButtonSelector) ||
|
|
47
|
+
element?.parentElement?.matches(EmojiButtonSelector)
|
|
36
48
|
);
|
|
37
49
|
}
|
|
38
50
|
|
|
39
|
-
export function closestCategory(
|
|
40
|
-
element: HTMLElement | null
|
|
41
|
-
): HTMLElement | null {
|
|
42
|
-
return element?.closest('.epr-emoji-category') ?? null;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
51
|
export function categoryLabelFromCategory(
|
|
46
|
-
category:
|
|
47
|
-
):
|
|
48
|
-
return category?.querySelector(
|
|
52
|
+
category: NullableElement
|
|
53
|
+
): NullableElement {
|
|
54
|
+
return category?.querySelector(asSelectors(ClassNames.label)) ?? null;
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
export function closestCategoryLabel(
|
|
52
|
-
element:
|
|
53
|
-
):
|
|
58
|
+
element: NullableElement
|
|
59
|
+
): NullableElement {
|
|
54
60
|
const category = closestCategory(element);
|
|
55
61
|
return categoryLabelFromCategory(category);
|
|
56
62
|
}
|
|
57
63
|
|
|
58
|
-
export function elementHeight(element:
|
|
64
|
+
export function elementHeight(element: NullableElement): number {
|
|
59
65
|
return element?.clientHeight ?? 0;
|
|
60
66
|
}
|
|
61
67
|
|
|
62
|
-
export function emojiTrueOffsetTop(element:
|
|
63
|
-
const button =
|
|
68
|
+
export function emojiTrueOffsetTop(element: NullableElement): number {
|
|
69
|
+
const button = buttonFromTarget(element);
|
|
64
70
|
const category = closestCategory(button);
|
|
65
71
|
|
|
66
72
|
return elementOffsetTop(button) + elementOffsetTop(category);
|
|
67
73
|
}
|
|
68
|
-
export function emojiTruOffsetLeft(element:
|
|
69
|
-
const button =
|
|
74
|
+
export function emojiTruOffsetLeft(element: NullableElement): number {
|
|
75
|
+
const button = buttonFromTarget(element);
|
|
70
76
|
const category = closestCategory(button);
|
|
71
77
|
|
|
72
78
|
return elementOffsetLeft(button) + elementOffsetLeft(category);
|
|
73
79
|
}
|
|
74
80
|
|
|
75
|
-
function elementOffsetTop(element:
|
|
81
|
+
function elementOffsetTop(element: NullableElement): number {
|
|
76
82
|
return element?.offsetTop ?? 0;
|
|
77
83
|
}
|
|
78
84
|
|
|
79
|
-
function elementOffsetLeft(element:
|
|
85
|
+
function elementOffsetLeft(element: NullableElement): number {
|
|
80
86
|
return element?.offsetLeft ?? 0;
|
|
81
87
|
}
|
|
82
88
|
|
|
83
|
-
export function unifiedFromEmojiElement(
|
|
84
|
-
emoji
|
|
85
|
-
): string | null {
|
|
86
|
-
return elementDataSetKey(buttonFromEmoji(emoji), 'unified') ?? null;
|
|
89
|
+
export function unifiedFromEmojiElement(emoji: NullableElement): string | null {
|
|
90
|
+
return elementDataSetKey(buttonFromTarget(emoji), 'unified') ?? null;
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
export function originalUnifiedFromEmojiElement(
|
|
90
|
-
emoji:
|
|
94
|
+
emoji: NullableElement
|
|
91
95
|
): string | null {
|
|
92
96
|
const unified = unifiedFromEmojiElement(emoji);
|
|
93
97
|
|
|
@@ -98,12 +102,133 @@ export function originalUnifiedFromEmojiElement(
|
|
|
98
102
|
}
|
|
99
103
|
|
|
100
104
|
function elementDataSetKey(
|
|
101
|
-
element:
|
|
105
|
+
element: NullableElement,
|
|
102
106
|
key: string
|
|
103
107
|
): string | null {
|
|
104
108
|
return elementDataSet(element)[key] ?? null;
|
|
105
109
|
}
|
|
106
110
|
|
|
107
|
-
function elementDataSet(element:
|
|
111
|
+
function elementDataSet(element: NullableElement): DOMStringMap {
|
|
108
112
|
return element?.dataset ?? {};
|
|
109
113
|
}
|
|
114
|
+
|
|
115
|
+
export function isVisibleEmoji(element: HTMLElement) {
|
|
116
|
+
return element.classList.contains(ClassNames.visible);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function isHidden(element: NullableElement) {
|
|
120
|
+
if (!element) return true;
|
|
121
|
+
|
|
122
|
+
return element.classList.contains(ClassNames.hidden);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function allVisibleEmojis(parent: NullableElement) {
|
|
126
|
+
if (!parent) {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return Array.from(
|
|
131
|
+
parent.querySelectorAll(VisibleEmojiSelector)
|
|
132
|
+
) as HTMLElement[];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function lastVisibleEmoji(element: NullableElement): NullableElement {
|
|
136
|
+
if (!element) return null;
|
|
137
|
+
|
|
138
|
+
const allEmojis = allVisibleEmojis(element);
|
|
139
|
+
const [last] = allEmojis.slice(-1);
|
|
140
|
+
if (!last) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!isVisibleEmoji(last)) {
|
|
145
|
+
return prevVisibleEmoji(last);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return last;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function nextVisibleEmoji(element: HTMLElement): NullableElement {
|
|
152
|
+
const next = element.nextElementSibling as HTMLElement;
|
|
153
|
+
|
|
154
|
+
if (!next) {
|
|
155
|
+
return firstVisibleEmoji(nextCategory(element));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!isVisibleEmoji(next)) {
|
|
159
|
+
return nextVisibleEmoji(next);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return next;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function prevVisibleEmoji(element: HTMLElement): NullableElement {
|
|
166
|
+
const prev = element.previousElementSibling as HTMLElement;
|
|
167
|
+
|
|
168
|
+
if (!prev) {
|
|
169
|
+
return lastVisibleEmoji(prevCategory(element));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!isVisibleEmoji(prev)) {
|
|
173
|
+
return prevVisibleEmoji(prev);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return prev;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function firstVisibleEmoji(parent: NullableElement) {
|
|
180
|
+
if (!parent) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const allEmojis = allVisibleEmojis(parent);
|
|
185
|
+
|
|
186
|
+
return firstVisibleElementInContainer(parent, allEmojis);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function prevCategory(element: NullableElement): NullableElement {
|
|
190
|
+
const category = closestCategory(element);
|
|
191
|
+
|
|
192
|
+
if (!category) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const prev = category.previousElementSibling as HTMLElement;
|
|
197
|
+
|
|
198
|
+
if (!prev) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (isHidden(prev)) {
|
|
203
|
+
return prevCategory(prev);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return prev;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function nextCategory(element: NullableElement): NullableElement {
|
|
210
|
+
const category = closestCategory(element);
|
|
211
|
+
|
|
212
|
+
if (!category) {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const next = category.nextElementSibling as HTMLElement;
|
|
217
|
+
|
|
218
|
+
if (!next) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (isHidden(next)) {
|
|
223
|
+
return nextCategory(next);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return next;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function closestCategory(element: NullableElement) {
|
|
230
|
+
if (!element) {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
return element.closest(asSelectors(ClassNames.category)) as HTMLElement;
|
|
234
|
+
}
|
package/src/EmojiPickerReact.css
CHANGED
|
@@ -1,50 +1,75 @@
|
|
|
1
1
|
.EmojiPickerReact {
|
|
2
2
|
--epr-highlight-color: #007aeb;
|
|
3
3
|
--epr-hover-bg-color: #f1f8ff;
|
|
4
|
+
--epr-focus-bg-color: #e0f0ff;
|
|
4
5
|
--epr-text-color: #858585;
|
|
5
|
-
--epr-search-
|
|
6
|
-
--epr-category-label-bg-color: #ffffffe6;
|
|
6
|
+
--epr-search-input-bg-color: #f6f6f6;
|
|
7
7
|
--epr-picker-border-color: #e7e7e7;
|
|
8
8
|
--epr-bg-color: #fff;
|
|
9
|
-
--epr-
|
|
10
|
-
--epr-emoji-variation-indicator-color-hover: var(--epr-text-color);
|
|
9
|
+
--epr-category-icon-active-color: #6aa8de;
|
|
11
10
|
|
|
11
|
+
--epr-horizontal-padding: 10px;
|
|
12
|
+
|
|
13
|
+
/* Picker Dimensions */
|
|
12
14
|
--epr-picker-width: 350px;
|
|
13
15
|
--epr-picker-height: 450px;
|
|
14
|
-
--epr-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
--epr-emoji-variation-picker-height: 55px;
|
|
18
|
-
--epr-preview-height: 70px;
|
|
19
|
-
--epr-preview-text-color: var(--epr-text-color);
|
|
20
|
-
--epr-preview-text-size: 14px;
|
|
21
|
-
--epr-preview-text-padding: 0 10px;
|
|
22
|
-
--epr-active-category-indicator-color: var(--epr-highlight-color);
|
|
23
|
-
--epr-active-skin-tone-indicator-border-color: var(--epr-highlight-color);
|
|
16
|
+
--epr-picker-border-radius: 8px;
|
|
17
|
+
|
|
18
|
+
/* Header */
|
|
24
19
|
--epr-search-border-color: var(--epr-highlight-color);
|
|
20
|
+
--epr-header-padding: 15px var(--epr-horizontal-padding);
|
|
21
|
+
|
|
22
|
+
/* Skin Tone Picker */
|
|
23
|
+
--epr-active-skin-tone-indicator-border-color: var(--epr-highlight-color);
|
|
25
24
|
--epr-active-skin-hover-color: var(--epr-hover-bg-color);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
--epr-
|
|
29
|
-
--epr-header-padding: 15px 10px;
|
|
30
|
-
--epr-emoji-gap: 2px;
|
|
31
|
-
--epr-category-padding: 0 10px;
|
|
25
|
+
|
|
26
|
+
/* Search */
|
|
27
|
+
--epr-search-input-bg-color-active: var(--epr-search-input-bg-color);
|
|
32
28
|
--epr-search-input-padding: 0 30px;
|
|
33
29
|
--epr-search-input-border-radius: 8px;
|
|
34
30
|
--epr-search-input-height: 40px;
|
|
35
|
-
--epr-
|
|
31
|
+
--epr-search-input-text-color: var(--epr-text-color);
|
|
32
|
+
--epr-search-input-placeholder-color: var(--epr-text-color);
|
|
33
|
+
--epr-search-bar-inner-padding: var(--epr-horizontal-padding);
|
|
34
|
+
|
|
35
|
+
/* Category Navigation */
|
|
36
|
+
--epr-category-navigation-button-size: 30px;
|
|
37
|
+
|
|
38
|
+
/* Variation Picker */
|
|
39
|
+
--epr-emoji-variation-picker-height: 55px;
|
|
40
|
+
--epr-emoji-variation-picker-bg-color: var(--epr-bg-color);
|
|
41
|
+
|
|
42
|
+
/* Preview */
|
|
43
|
+
--epr-preview-height: 70px;
|
|
44
|
+
--epr-preview-text-size: 14px;
|
|
45
|
+
--epr-preview-text-padding: 0 var(--epr-horizontal-padding);
|
|
46
|
+
--epr-preview-border-color: var(--epr-picker-border-color);
|
|
47
|
+
--epr-preview-text-color: var(--epr-text-color);
|
|
48
|
+
|
|
49
|
+
/* Category */
|
|
50
|
+
--epr-category-padding: 0 var(--epr-horizontal-padding);
|
|
51
|
+
|
|
52
|
+
/* Category Label */
|
|
53
|
+
--epr-category-label-bg-color: #ffffffe6;
|
|
54
|
+
--epr-category-label-text-color: var(--epr-text-color);
|
|
55
|
+
--epr-category-label-padding: 0 var(--epr-horizontal-padding);
|
|
36
56
|
--epr-category-label-height: 40px;
|
|
37
57
|
|
|
58
|
+
/* Emoji */
|
|
59
|
+
--epr-emoji-size: 30px;
|
|
60
|
+
--epr-emoji-padding: 5px;
|
|
61
|
+
--epr-emoji-fullsize: calc(
|
|
62
|
+
var(--epr-emoji-size) + var(--epr-emoji-padding) * 2
|
|
63
|
+
);
|
|
64
|
+
--epr-emoji-hover-color: var(--epr-hover-bg-color);
|
|
65
|
+
--epr-emoji-variation-indicator-color: var(--epr-picker-border-color);
|
|
66
|
+
--epr-emoji-variation-indicator-color-hover: var(--epr-text-color);
|
|
67
|
+
|
|
68
|
+
/* Z-Index */
|
|
38
69
|
--epr-header-overlay-z-index: 3;
|
|
39
70
|
--epr-emoji-variations-indictator-z-index: 1;
|
|
40
71
|
--epr-category-label-z-index: 2;
|
|
41
72
|
--epr-skin-variation-picker-z-index: 5;
|
|
42
|
-
|
|
43
|
-
--epr-picker-border-radius: 8px;
|
|
44
|
-
--epr-search-bar-text-color: var(--epr-text-color);
|
|
45
|
-
--epr-search-bar-placeholder-color: var(--epr-text-color);
|
|
46
|
-
--epr-preview-border-color: var(--epr-picker-border-color);
|
|
47
|
-
--epr-search-bar-bg-color-active: var(--epr-search-bar-bg-color);
|
|
48
73
|
}
|
|
49
74
|
|
|
50
75
|
.EmojiPickerReact.epr-dark-theme {
|
|
@@ -53,12 +78,14 @@
|
|
|
53
78
|
--epr-highlight-color: #c0c0c0;
|
|
54
79
|
--epr-text-color: var(--epr-highlight-color);
|
|
55
80
|
--epr-hover-bg-color: #363636f6;
|
|
56
|
-
--epr-
|
|
81
|
+
--epr-focus-bg-color: #474747;
|
|
82
|
+
--epr-search-input-bg-color: #333333;
|
|
57
83
|
--epr-category-label-bg-color: #222222e6;
|
|
58
84
|
--epr-picker-border-color: #151617;
|
|
59
85
|
--epr-bg-color: #222222;
|
|
60
|
-
--epr-search-
|
|
86
|
+
--epr-search-input-bg-color-active: var(--epr-dark);
|
|
61
87
|
--epr-emoji-variation-indicator-color: #444;
|
|
88
|
+
--epr-category-icon-active-color: #3271b7;
|
|
62
89
|
}
|
|
63
90
|
|
|
64
91
|
.EmojiPickerReact {
|
|
@@ -89,4 +116,5 @@
|
|
|
89
116
|
cursor: pointer;
|
|
90
117
|
border: 0;
|
|
91
118
|
background: none;
|
|
119
|
+
outline: none;
|
|
92
120
|
}
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
.EmojiPickerReact li.epr-emoji-category > .epr-emoji-category-content {
|
|
2
|
+
position: relative;
|
|
3
|
+
padding: var(--epr-category-padding);
|
|
4
|
+
|
|
5
|
+
display: grid;
|
|
6
|
+
grid-template-columns: repeat(auto-fill, var(--epr-emoji-fullsize));
|
|
7
|
+
grid-gap: 0;
|
|
8
|
+
justify-content: space-between;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.EmojiPickerReact li.epr-emoji-category:not(:has(.epr-visible)) {
|
|
12
|
+
display: none;
|
|
13
|
+
}
|
|
14
|
+
|
|
1
15
|
.EmojiPickerReact li.epr-emoji-category > .epr-emoji-category-label {
|
|
2
16
|
position: sticky;
|
|
3
17
|
top: 0;
|
|
@@ -20,23 +34,3 @@
|
|
|
20
34
|
position: relative;
|
|
21
35
|
opacity: 0.4;
|
|
22
36
|
}
|
|
23
|
-
|
|
24
|
-
.EmojiPickerReact li.epr-emoji-category {
|
|
25
|
-
position: relative;
|
|
26
|
-
display: flex;
|
|
27
|
-
flex-direction: row;
|
|
28
|
-
flex-wrap: wrap;
|
|
29
|
-
justify-content: space-around;
|
|
30
|
-
padding: var(--epr-category-padding);
|
|
31
|
-
gap: var(--epr-emoji-gap);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
.EmojiPickerReact li.epr-emoji-category:after {
|
|
35
|
-
content: '';
|
|
36
|
-
display: inline-block;
|
|
37
|
-
flex: 1;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.EmojiPickerReact li.epr-emoji-category:not(:has(.epr-visible)) {
|
|
41
|
-
display: none;
|
|
42
|
-
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
|
|
4
|
+
import { ClassNames } from '../../DomUtils/classNames';
|
|
4
5
|
import {
|
|
5
6
|
CategoryConfig,
|
|
6
7
|
categoryFromCategoryConfig,
|
|
7
8
|
categoryNameFromCategoryConfig
|
|
8
9
|
} from '../../config/categoryConfig';
|
|
9
|
-
import { ClassNames } from '../../DomUtils/classNames';
|
|
10
10
|
import './EmojiCategory.css';
|
|
11
11
|
|
|
12
12
|
type Props = Readonly<{
|
|
@@ -27,14 +27,14 @@ export function EmojiCategory({
|
|
|
27
27
|
|
|
28
28
|
return (
|
|
29
29
|
<li
|
|
30
|
-
className={clsx(
|
|
30
|
+
className={clsx(ClassNames.category, {
|
|
31
31
|
[ClassNames.hidden]: hidden,
|
|
32
32
|
[ClassNames.hiddenOnSearch]: hiddenOnSearch
|
|
33
33
|
})}
|
|
34
34
|
data-name={category}
|
|
35
35
|
>
|
|
36
36
|
<div className="epr-emoji-category-label">{categoryName}</div>
|
|
37
|
-
{children}
|
|
37
|
+
<div className="epr-emoji-category-content">{children}</div>
|
|
38
38
|
</li>
|
|
39
39
|
);
|
|
40
40
|
}
|
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
+
import { getEmojiRef } from '../../DomUtils/emojiElementRef';
|
|
3
4
|
import {
|
|
4
5
|
Categories,
|
|
5
6
|
CategoryConfig,
|
|
6
|
-
categoryFromCategoryConfig
|
|
7
|
+
categoryFromCategoryConfig
|
|
7
8
|
} from '../../config/categoryConfig';
|
|
8
9
|
import {
|
|
9
10
|
useCategoriesConfig,
|
|
10
11
|
useEmojiStyleConfig,
|
|
11
|
-
useLazyLoadEmojisConfig
|
|
12
|
+
useLazyLoadEmojisConfig
|
|
12
13
|
} from '../../config/useConfig';
|
|
13
14
|
import { emojisByCategory, emojiUnified } from '../../dataUtils/emojiSelectors';
|
|
14
15
|
import { useIsEmojiHidden } from '../../hooks/useIsEmojiHidden';
|
|
15
16
|
import {
|
|
16
17
|
useActiveSkinToneState,
|
|
17
|
-
useIsPastInitialLoad
|
|
18
|
+
useIsPastInitialLoad
|
|
18
19
|
} from '../context/PickerContext';
|
|
20
|
+
import { Emoji } from '../emoji/Emoji';
|
|
19
21
|
|
|
20
22
|
import { EmojiCategory } from './EmojiCategory';
|
|
21
23
|
import { Suggested } from './Suggested';
|
|
24
|
+
|
|
22
25
|
import './EmojiList.css';
|
|
23
|
-
import { getEmojiRef } from '../../DomUtils/emojiElementRef';
|
|
24
|
-
import { Emoji } from '../emoji/Emoji';
|
|
25
26
|
|
|
26
27
|
export function EmojiList() {
|
|
27
28
|
const categories = useCategoriesConfig();
|
|
@@ -51,7 +52,7 @@ export function EmojiList() {
|
|
|
51
52
|
function RenderCategory({
|
|
52
53
|
index,
|
|
53
54
|
category,
|
|
54
|
-
categoryConfig
|
|
55
|
+
categoryConfig
|
|
55
56
|
}: {
|
|
56
57
|
index: number;
|
|
57
58
|
category: Categories;
|
|
@@ -70,7 +71,7 @@ function RenderCategory({
|
|
|
70
71
|
|
|
71
72
|
let hiddenCounter = 0;
|
|
72
73
|
|
|
73
|
-
const emojis = emojisToPush.map(
|
|
74
|
+
const emojis = emojisToPush.map(emoji => {
|
|
74
75
|
const unified = emojiUnified(emoji, activeSkinTone);
|
|
75
76
|
const hidden = isEmojiHidden(emoji);
|
|
76
77
|
const emojiRef = getEmojiRef(emojiUnified(emoji));
|
|
@@ -2,7 +2,7 @@ import clsx from 'clsx';
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
buttonFromTarget,
|
|
6
6
|
elementHeight,
|
|
7
7
|
emojiTrueOffsetTop,
|
|
8
8
|
emojiTruOffsetLeft
|
|
@@ -91,7 +91,7 @@ function usePointerStyle(VariationPickerRef: React.RefObject<HTMLElement>) {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
if (AnchoredEmojiRef.current) {
|
|
94
|
-
const button =
|
|
94
|
+
const button = buttonFromTarget(AnchoredEmojiRef.current);
|
|
95
95
|
|
|
96
96
|
const offsetLeft = emojiTruOffsetLeft(button);
|
|
97
97
|
|
|
@@ -135,7 +135,7 @@ function useVariationPickerTop(
|
|
|
135
135
|
|
|
136
136
|
if (AnchoredEmojiRef.current) {
|
|
137
137
|
const bodyRef = BodyRef.current;
|
|
138
|
-
const button =
|
|
138
|
+
const button = buttonFromTarget(AnchoredEmojiRef.current);
|
|
139
139
|
|
|
140
140
|
const buttonHeight = elementHeight(button);
|
|
141
141
|
|
|
@@ -25,7 +25,8 @@ export function Suggested({ categoryConfig }: Props) {
|
|
|
25
25
|
const suggestedEmojisModeConfig = useSuggestedEmojisModeConfig();
|
|
26
26
|
const suggested = React.useMemo(
|
|
27
27
|
() => getsuggested(suggestedEmojisModeConfig) ?? [],
|
|
28
|
-
|
|
28
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
29
|
+
[suggestedUpdated, suggestedEmojisModeConfig]
|
|
29
30
|
);
|
|
30
31
|
const emojiStyle = useEmojiStyleConfig();
|
|
31
32
|
|
|
@@ -51,6 +52,7 @@ export function Suggested({ categoryConfig }: Props) {
|
|
|
51
52
|
emojiStyle={emojiStyle}
|
|
52
53
|
emoji={emoji}
|
|
53
54
|
key={suggestedItem.unified}
|
|
55
|
+
hidden={hidden}
|
|
54
56
|
/>
|
|
55
57
|
);
|
|
56
58
|
})}
|
|
@@ -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,80 @@ 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);
|
|
11
16
|
|
|
12
17
|
return (
|
|
13
18
|
<ElementRefContext.Provider
|
|
14
|
-
value={{
|
|
19
|
+
value={{
|
|
20
|
+
AnchoredEmojiRef,
|
|
21
|
+
BodyRef,
|
|
22
|
+
CategoryNavigationRef,
|
|
23
|
+
PickerMainRef,
|
|
24
|
+
SearchInputRef,
|
|
25
|
+
SkinTonePickerRef
|
|
26
|
+
}}
|
|
15
27
|
>
|
|
16
28
|
{children}
|
|
17
29
|
</ElementRefContext.Provider>
|
|
18
30
|
);
|
|
19
31
|
}
|
|
20
32
|
|
|
21
|
-
export type ElementRef
|
|
33
|
+
export type ElementRef<
|
|
34
|
+
E extends HTMLElement = HTMLElement
|
|
35
|
+
> = React.MutableRefObject<E | null>;
|
|
22
36
|
|
|
23
37
|
type ElementRefs = {
|
|
24
38
|
PickerMainRef: ElementRef;
|
|
25
39
|
AnchoredEmojiRef: ElementRef;
|
|
26
|
-
|
|
40
|
+
SkinTonePickerRef: ElementRef<HTMLDivElement>;
|
|
41
|
+
SearchInputRef: ElementRef<HTMLInputElement>;
|
|
42
|
+
BodyRef: ElementRef<HTMLDivElement>;
|
|
43
|
+
CategoryNavigationRef: ElementRef<HTMLDivElement>;
|
|
27
44
|
};
|
|
28
45
|
|
|
29
46
|
const ElementRefContext = React.createContext<ElementRefs>({
|
|
30
|
-
PickerMainRef: React.createRef(),
|
|
31
47
|
AnchoredEmojiRef: React.createRef(),
|
|
32
|
-
BodyRef: React.createRef()
|
|
48
|
+
BodyRef: React.createRef(),
|
|
49
|
+
CategoryNavigationRef: React.createRef(),
|
|
50
|
+
PickerMainRef: React.createRef(),
|
|
51
|
+
SearchInputRef: React.createRef(),
|
|
52
|
+
SkinTonePickerRef: React.createRef()
|
|
33
53
|
});
|
|
34
54
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
export function useElementRef(key: ElementRefKeys) {
|
|
38
|
-
const { [key]: elementRef } = React.useContext(ElementRefContext);
|
|
39
|
-
return elementRef;
|
|
55
|
+
function useElementRef() {
|
|
56
|
+
return React.useContext(ElementRefContext);
|
|
40
57
|
}
|
|
41
58
|
|
|
42
59
|
export function usePickerMainRef() {
|
|
43
|
-
return useElementRef('PickerMainRef'
|
|
60
|
+
return useElementRef()['PickerMainRef'];
|
|
44
61
|
}
|
|
45
62
|
|
|
46
63
|
export function useAnchoredEmojiRef() {
|
|
47
|
-
return useElementRef('AnchoredEmojiRef'
|
|
64
|
+
return useElementRef()['AnchoredEmojiRef'];
|
|
48
65
|
}
|
|
49
66
|
|
|
50
|
-
export function useSetAnchoredEmojiRef(): (target:
|
|
67
|
+
export function useSetAnchoredEmojiRef(): (target: NullableElement) => void {
|
|
51
68
|
const AnchoredEmojiRef = useAnchoredEmojiRef();
|
|
52
|
-
return (target:
|
|
69
|
+
return (target: NullableElement) => {
|
|
53
70
|
AnchoredEmojiRef.current = target;
|
|
54
71
|
};
|
|
55
72
|
}
|
|
56
73
|
|
|
57
|
-
export function useBodyRef()
|
|
58
|
-
return useElementRef(
|
|
59
|
-
|
|
60
|
-
|
|
74
|
+
export function useBodyRef() {
|
|
75
|
+
return useElementRef()['BodyRef'];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function useSearchInputRef() {
|
|
79
|
+
return useElementRef()['SearchInputRef'];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function useSkinTonePickerRef() {
|
|
83
|
+
return useElementRef()['SkinTonePickerRef'];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function useCategoryNavigationRef() {
|
|
87
|
+
return useElementRef()['CategoryNavigationRef'];
|
|
61
88
|
}
|