emoji-picker-react 4.0.0-rc.9 → 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 +3771 -3068
- 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 +3781 -3078
- 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 +24 -4
- 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
|
@@ -1,93 +1,252 @@
|
|
|
1
1
|
import { DataEmoji } from '../dataUtils/DataTypes';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
emojiByUnified,
|
|
4
|
+
unifiedWithoutSkinTone
|
|
5
|
+
} from '../dataUtils/emojiSelectors';
|
|
3
6
|
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
6
21
|
): HTMLButtonElement | null {
|
|
7
|
-
return emojiElement?.closest(
|
|
22
|
+
return emojiElement?.closest(EmojiButtonSelector) ?? null;
|
|
8
23
|
}
|
|
9
24
|
|
|
10
25
|
export function emojiFromElement(
|
|
11
|
-
element:
|
|
12
|
-
): DataEmoji |
|
|
13
|
-
const
|
|
26
|
+
element: NullableElement
|
|
27
|
+
): [DataEmoji, string] | [] {
|
|
28
|
+
const originalUnified = originalUnifiedFromEmojiElement(element);
|
|
29
|
+
const unified = unifiedFromEmojiElement(element);
|
|
30
|
+
|
|
31
|
+
if (!originalUnified) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
14
34
|
|
|
15
|
-
|
|
16
|
-
|
|
35
|
+
const emoji = emojiByUnified(originalUnified);
|
|
36
|
+
|
|
37
|
+
if (!emoji) {
|
|
38
|
+
return [];
|
|
17
39
|
}
|
|
18
|
-
|
|
40
|
+
|
|
41
|
+
return [emoji, unified as string];
|
|
19
42
|
}
|
|
20
43
|
|
|
21
|
-
export function isEmojiElement(element:
|
|
44
|
+
export function isEmojiElement(element: NullableElement): boolean {
|
|
22
45
|
return Boolean(
|
|
23
|
-
element?.matches(
|
|
24
|
-
element?.parentElement?.matches(
|
|
46
|
+
element?.matches(EmojiButtonSelector) ||
|
|
47
|
+
element?.parentElement?.matches(EmojiButtonSelector)
|
|
25
48
|
);
|
|
26
49
|
}
|
|
27
50
|
|
|
28
|
-
export function closestCategory(
|
|
29
|
-
element: HTMLElement | null
|
|
30
|
-
): HTMLElement | null {
|
|
31
|
-
return element?.closest('.epr-emoji-category') ?? null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
51
|
export function categoryLabelFromCategory(
|
|
35
|
-
category:
|
|
36
|
-
):
|
|
37
|
-
return category?.querySelector(
|
|
52
|
+
category: NullableElement
|
|
53
|
+
): NullableElement {
|
|
54
|
+
return category?.querySelector(asSelectors(ClassNames.label)) ?? null;
|
|
38
55
|
}
|
|
39
56
|
|
|
40
57
|
export function closestCategoryLabel(
|
|
41
|
-
element:
|
|
42
|
-
):
|
|
58
|
+
element: NullableElement
|
|
59
|
+
): NullableElement {
|
|
43
60
|
const category = closestCategory(element);
|
|
44
61
|
return categoryLabelFromCategory(category);
|
|
45
62
|
}
|
|
46
63
|
|
|
47
|
-
export function elementHeight(element:
|
|
64
|
+
export function elementHeight(element: NullableElement): number {
|
|
48
65
|
return element?.clientHeight ?? 0;
|
|
49
66
|
}
|
|
50
67
|
|
|
51
|
-
export function emojiTrueOffsetTop(element:
|
|
52
|
-
|
|
53
|
-
|
|
68
|
+
export function emojiTrueOffsetTop(element: NullableElement): number {
|
|
69
|
+
if (!element) {
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const button = buttonFromTarget(element);
|
|
74
|
+
const categoryWithoutLabel = closestCategoryContent(button);
|
|
75
|
+
const category = closestCategory(categoryWithoutLabel);
|
|
76
|
+
|
|
77
|
+
// compensate for the label height
|
|
78
|
+
const labelHeight =
|
|
79
|
+
(category?.clientHeight ?? 0) - (categoryWithoutLabel?.clientHeight ?? 0);
|
|
54
80
|
|
|
55
|
-
return elementOffsetTop(button) + elementOffsetTop(category);
|
|
81
|
+
return elementOffsetTop(button) + elementOffsetTop(category) + labelHeight;
|
|
56
82
|
}
|
|
57
|
-
export function emojiTruOffsetLeft(element:
|
|
58
|
-
const button =
|
|
83
|
+
export function emojiTruOffsetLeft(element: NullableElement): number {
|
|
84
|
+
const button = buttonFromTarget(element);
|
|
59
85
|
const category = closestCategory(button);
|
|
60
86
|
|
|
61
87
|
return elementOffsetLeft(button) + elementOffsetLeft(category);
|
|
62
88
|
}
|
|
63
89
|
|
|
64
|
-
function elementOffsetTop(element:
|
|
90
|
+
function elementOffsetTop(element: NullableElement): number {
|
|
65
91
|
return element?.offsetTop ?? 0;
|
|
66
92
|
}
|
|
67
93
|
|
|
68
|
-
function elementOffsetLeft(element:
|
|
94
|
+
function elementOffsetLeft(element: NullableElement): number {
|
|
69
95
|
return element?.offsetLeft ?? 0;
|
|
70
96
|
}
|
|
71
97
|
|
|
72
|
-
export function unifiedFromEmojiElement(
|
|
73
|
-
emoji
|
|
74
|
-
): string | null {
|
|
75
|
-
return elementDataSetKey(buttonFromEmoji(emoji), 'unified') ?? null;
|
|
98
|
+
export function unifiedFromEmojiElement(emoji: NullableElement): string | null {
|
|
99
|
+
return elementDataSetKey(buttonFromTarget(emoji), 'unified') ?? null;
|
|
76
100
|
}
|
|
77
101
|
|
|
78
102
|
export function originalUnifiedFromEmojiElement(
|
|
79
|
-
emoji:
|
|
103
|
+
emoji: NullableElement
|
|
80
104
|
): string | null {
|
|
81
|
-
|
|
105
|
+
const unified = unifiedFromEmojiElement(emoji);
|
|
106
|
+
|
|
107
|
+
if (unified) {
|
|
108
|
+
return unifiedWithoutSkinTone(unified);
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
82
111
|
}
|
|
83
112
|
|
|
84
113
|
function elementDataSetKey(
|
|
85
|
-
element:
|
|
114
|
+
element: NullableElement,
|
|
86
115
|
key: string
|
|
87
116
|
): string | null {
|
|
88
117
|
return elementDataSet(element)[key] ?? null;
|
|
89
118
|
}
|
|
90
119
|
|
|
91
|
-
function elementDataSet(element:
|
|
120
|
+
function elementDataSet(element: NullableElement): DOMStringMap {
|
|
92
121
|
return element?.dataset ?? {};
|
|
93
122
|
}
|
|
123
|
+
|
|
124
|
+
export function isVisibleEmoji(element: HTMLElement) {
|
|
125
|
+
return element.classList.contains(ClassNames.visible);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function isHidden(element: NullableElement) {
|
|
129
|
+
if (!element) return true;
|
|
130
|
+
|
|
131
|
+
return element.classList.contains(ClassNames.hidden);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function allVisibleEmojis(parent: NullableElement) {
|
|
135
|
+
if (!parent) {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return Array.from(
|
|
140
|
+
parent.querySelectorAll(VisibleEmojiSelector)
|
|
141
|
+
) as HTMLElement[];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function lastVisibleEmoji(element: NullableElement): NullableElement {
|
|
145
|
+
if (!element) return null;
|
|
146
|
+
|
|
147
|
+
const allEmojis = allVisibleEmojis(element);
|
|
148
|
+
const [last] = allEmojis.slice(-1);
|
|
149
|
+
if (!last) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!isVisibleEmoji(last)) {
|
|
154
|
+
return prevVisibleEmoji(last);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return last;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function nextVisibleEmoji(element: HTMLElement): NullableElement {
|
|
161
|
+
const next = element.nextElementSibling as HTMLElement;
|
|
162
|
+
|
|
163
|
+
if (!next) {
|
|
164
|
+
return firstVisibleEmoji(nextCategory(element));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!isVisibleEmoji(next)) {
|
|
168
|
+
return nextVisibleEmoji(next);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return next;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function prevVisibleEmoji(element: HTMLElement): NullableElement {
|
|
175
|
+
const prev = element.previousElementSibling as HTMLElement;
|
|
176
|
+
|
|
177
|
+
if (!prev) {
|
|
178
|
+
return lastVisibleEmoji(prevCategory(element));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (!isVisibleEmoji(prev)) {
|
|
182
|
+
return prevVisibleEmoji(prev);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return prev;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function firstVisibleEmoji(parent: NullableElement) {
|
|
189
|
+
if (!parent) {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const allEmojis = allVisibleEmojis(parent);
|
|
194
|
+
|
|
195
|
+
return firstVisibleElementInContainer(parent, allEmojis);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function prevCategory(element: NullableElement): NullableElement {
|
|
199
|
+
const category = closestCategory(element);
|
|
200
|
+
|
|
201
|
+
if (!category) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const prev = category.previousElementSibling as HTMLElement;
|
|
206
|
+
|
|
207
|
+
if (!prev) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (isHidden(prev)) {
|
|
212
|
+
return prevCategory(prev);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return prev;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function nextCategory(element: NullableElement): NullableElement {
|
|
219
|
+
const category = closestCategory(element);
|
|
220
|
+
|
|
221
|
+
if (!category) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const next = category.nextElementSibling as HTMLElement;
|
|
226
|
+
|
|
227
|
+
if (!next) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (isHidden(next)) {
|
|
232
|
+
return nextCategory(next);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return next;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function closestCategory(element: NullableElement) {
|
|
239
|
+
if (!element) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
return element.closest(asSelectors(ClassNames.category)) as HTMLElement;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function closestCategoryContent(element: NullableElement) {
|
|
246
|
+
if (!element) {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
return element.closest(
|
|
250
|
+
asSelectors(ClassNames.categoryContent)
|
|
251
|
+
) as HTMLElement;
|
|
252
|
+
}
|
package/src/EmojiPickerReact.css
CHANGED
|
@@ -1,68 +1,92 @@
|
|
|
1
1
|
.EmojiPickerReact {
|
|
2
|
-
--highlight-color: #007aeb;
|
|
3
|
-
--hover-
|
|
4
|
-
--
|
|
5
|
-
--epr-
|
|
6
|
-
--epr-
|
|
7
|
-
--epr-picker-
|
|
8
|
-
--epr-
|
|
9
|
-
--epr-category-
|
|
10
|
-
|
|
11
|
-
--epr-
|
|
12
|
-
|
|
13
|
-
--epr-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
--epr-search-
|
|
17
|
-
--epr-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
--epr-active-skin-
|
|
21
|
-
--epr-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
--epr-
|
|
25
|
-
--epr-header-padding: 15px 10px;
|
|
26
|
-
--epr-emoji-gap: 4px;
|
|
27
|
-
--epr-category-padding: 0 10px;
|
|
2
|
+
--epr-highlight-color: #007aeb;
|
|
3
|
+
--epr-hover-bg-color: #f1f8ff;
|
|
4
|
+
--epr-focus-bg-color: #e0f0ff;
|
|
5
|
+
--epr-text-color: #858585;
|
|
6
|
+
--epr-search-input-bg-color: #f6f6f6;
|
|
7
|
+
--epr-picker-border-color: #e7e7e7;
|
|
8
|
+
--epr-bg-color: #fff;
|
|
9
|
+
--epr-category-icon-active-color: #6aa8de;
|
|
10
|
+
|
|
11
|
+
--epr-horizontal-padding: 10px;
|
|
12
|
+
|
|
13
|
+
--epr-picker-border-radius: 8px;
|
|
14
|
+
|
|
15
|
+
/* Header */
|
|
16
|
+
--epr-search-border-color: var(--epr-highlight-color);
|
|
17
|
+
--epr-header-padding: 15px var(--epr-horizontal-padding);
|
|
18
|
+
|
|
19
|
+
/* Skin Tone Picker */
|
|
20
|
+
--epr-active-skin-tone-indicator-border-color: var(--epr-highlight-color);
|
|
21
|
+
--epr-active-skin-hover-color: var(--epr-hover-bg-color);
|
|
22
|
+
|
|
23
|
+
/* Search */
|
|
24
|
+
--epr-search-input-bg-color-active: var(--epr-search-input-bg-color);
|
|
28
25
|
--epr-search-input-padding: 0 30px;
|
|
29
26
|
--epr-search-input-border-radius: 8px;
|
|
30
27
|
--epr-search-input-height: 40px;
|
|
31
|
-
--epr-
|
|
28
|
+
--epr-search-input-text-color: var(--epr-text-color);
|
|
29
|
+
--epr-search-input-placeholder-color: var(--epr-text-color);
|
|
30
|
+
--epr-search-bar-inner-padding: var(--epr-horizontal-padding);
|
|
31
|
+
|
|
32
|
+
/* Category Navigation */
|
|
33
|
+
--epr-category-navigation-button-size: 30px;
|
|
34
|
+
|
|
35
|
+
/* Variation Picker */
|
|
36
|
+
--epr-emoji-variation-picker-height: 45px;
|
|
37
|
+
--epr-emoji-variation-picker-bg-color: var(--epr-bg-color);
|
|
38
|
+
|
|
39
|
+
/* Preview */
|
|
40
|
+
--epr-preview-height: 70px;
|
|
41
|
+
--epr-preview-text-size: 14px;
|
|
42
|
+
--epr-preview-text-padding: 0 var(--epr-horizontal-padding);
|
|
43
|
+
--epr-preview-border-color: var(--epr-picker-border-color);
|
|
44
|
+
--epr-preview-text-color: var(--epr-text-color);
|
|
45
|
+
|
|
46
|
+
/* Category */
|
|
47
|
+
--epr-category-padding: 0 var(--epr-horizontal-padding);
|
|
48
|
+
|
|
49
|
+
/* Category Label */
|
|
50
|
+
--epr-category-label-bg-color: #ffffffe6;
|
|
51
|
+
--epr-category-label-text-color: var(--epr-text-color);
|
|
52
|
+
--epr-category-label-padding: 0 var(--epr-horizontal-padding);
|
|
32
53
|
--epr-category-label-height: 40px;
|
|
33
54
|
|
|
55
|
+
/* Emoji */
|
|
56
|
+
--epr-emoji-size: 30px;
|
|
57
|
+
--epr-emoji-padding: 5px;
|
|
58
|
+
--epr-emoji-fullsize: calc(
|
|
59
|
+
var(--epr-emoji-size) + var(--epr-emoji-padding) * 2
|
|
60
|
+
);
|
|
61
|
+
--epr-emoji-hover-color: var(--epr-hover-bg-color);
|
|
62
|
+
--epr-emoji-variation-indicator-color: var(--epr-picker-border-color);
|
|
63
|
+
--epr-emoji-variation-indicator-color-hover: var(--epr-text-color);
|
|
64
|
+
|
|
65
|
+
/* Z-Index */
|
|
34
66
|
--epr-header-overlay-z-index: 3;
|
|
35
67
|
--epr-emoji-variations-indictator-z-index: 1;
|
|
36
68
|
--epr-category-label-z-index: 2;
|
|
37
69
|
--epr-skin-variation-picker-z-index: 5;
|
|
38
|
-
|
|
39
|
-
--epr-picker-border-radius: 8px;
|
|
40
|
-
--epr-search-bar-text-color: var(--text-color-base);
|
|
41
|
-
--epr-search-bar-placeholder-color: var(--text-color-base);
|
|
42
|
-
--epr-picker-border-color: #e7e7e7;
|
|
43
|
-
--epr-background-color: #fff;
|
|
44
|
-
--epr-preview-border-color: var(--epr-picker-border-color);
|
|
45
|
-
--epr-search-bar-bg-color-active: var(--epr-search-bar-bg-color);
|
|
46
|
-
--epr-emoji-variation-indicator-color: rgba(0, 0, 0, 0.1);
|
|
47
|
-
--epr-emoji-variation-indicator-color-hover: rgba(0, 0, 0, 0.4);
|
|
48
70
|
}
|
|
49
71
|
|
|
50
72
|
.EmojiPickerReact.epr-dark-theme {
|
|
51
|
-
--epr-
|
|
52
|
-
--
|
|
53
|
-
--
|
|
54
|
-
--
|
|
55
|
-
--epr-
|
|
56
|
-
--epr-
|
|
73
|
+
--epr-dark: #000;
|
|
74
|
+
--epr-emoji-variation-picker-bg-color: var(--epr-dark);
|
|
75
|
+
--epr-highlight-color: #c0c0c0;
|
|
76
|
+
--epr-text-color: var(--epr-highlight-color);
|
|
77
|
+
--epr-hover-bg-color: #363636f6;
|
|
78
|
+
--epr-focus-bg-color: #474747;
|
|
79
|
+
--epr-search-input-bg-color: #333333;
|
|
80
|
+
--epr-category-label-bg-color: #222222e6;
|
|
57
81
|
--epr-picker-border-color: #151617;
|
|
58
|
-
--epr-
|
|
59
|
-
--epr-search-
|
|
60
|
-
--epr-emoji-variation-indicator-color:
|
|
61
|
-
--epr-
|
|
82
|
+
--epr-bg-color: #222222;
|
|
83
|
+
--epr-search-input-bg-color-active: var(--epr-dark);
|
|
84
|
+
--epr-emoji-variation-indicator-color: #444;
|
|
85
|
+
--epr-category-icon-active-color: #3271b7;
|
|
62
86
|
}
|
|
63
87
|
|
|
64
88
|
.EmojiPickerReact {
|
|
65
|
-
background-color: var(--epr-
|
|
89
|
+
background-color: var(--epr-bg-color);
|
|
66
90
|
overflow: hidden;
|
|
67
91
|
}
|
|
68
92
|
|
|
@@ -80,6 +104,32 @@
|
|
|
80
104
|
visibility: hidden !important;
|
|
81
105
|
}
|
|
82
106
|
|
|
107
|
+
.EmojiPickerReact:not(.epr-search-active) .epr-visible-on-search-only {
|
|
108
|
+
display: none !important;
|
|
109
|
+
opacity: 0 !important;
|
|
110
|
+
pointer-events: none !important;
|
|
111
|
+
visibility: hidden !important;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* This is the same as .search-active, only without any js behind it
|
|
115
|
+
Should switch to it full time when :has() gets better adoption
|
|
116
|
+
*/
|
|
117
|
+
aside.EmojiPickerReact.epr-main:has(input:not(:placeholder-shown))
|
|
118
|
+
.epr-hidden-on-search {
|
|
119
|
+
display: none !important;
|
|
120
|
+
opacity: 0 !important;
|
|
121
|
+
pointer-events: none !important;
|
|
122
|
+
visibility: hidden !important;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
aside.EmojiPickerReact.epr-main:has(input:placeholder-shown)
|
|
126
|
+
.epr-visible-on-search-only {
|
|
127
|
+
display: none !important;
|
|
128
|
+
opacity: 0 !important;
|
|
129
|
+
pointer-events: none !important;
|
|
130
|
+
visibility: hidden !important;
|
|
131
|
+
}
|
|
132
|
+
|
|
83
133
|
.EmojiPickerReact * {
|
|
84
134
|
box-sizing: border-box;
|
|
85
135
|
font-family: sans-serif;
|
|
@@ -89,4 +139,5 @@
|
|
|
89
139
|
cursor: pointer;
|
|
90
140
|
border: 0;
|
|
91
141
|
background: none;
|
|
142
|
+
outline: none;
|
|
92
143
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { Body } from './components/body/Body';
|
|
4
|
+
import { ElementRefContextProvider } from './components/context/ElementRefContext';
|
|
5
|
+
import { PickerConfigProvider } from './components/context/PickerConfigContext';
|
|
6
|
+
import { Preview } from './components/footer/Preview';
|
|
7
|
+
import { Header } from './components/header/Header';
|
|
8
|
+
import PickerMain from './components/main/PickerMain';
|
|
9
|
+
import { PickerConfig } from './config/config';
|
|
10
|
+
import './EmojiPickerReact.css';
|
|
11
|
+
|
|
12
|
+
export interface Props extends PickerConfig {}
|
|
13
|
+
|
|
14
|
+
export default function EmojiPicker(props: Props) {
|
|
15
|
+
return (
|
|
16
|
+
<ElementRefContextProvider>
|
|
17
|
+
<PickerConfigProvider {...props}>
|
|
18
|
+
<PickerMain>
|
|
19
|
+
<Header />
|
|
20
|
+
<Body />
|
|
21
|
+
<Preview />
|
|
22
|
+
</PickerMain>
|
|
23
|
+
</PickerConfigProvider>
|
|
24
|
+
</ElementRefContextProvider>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
export default class ErrorBoundary extends React.Component<
|
|
4
|
+
{ children: React.ReactNode },
|
|
5
|
+
{ hasError: boolean }
|
|
6
|
+
> {
|
|
7
|
+
constructor(props: { children: React.ReactNode }) {
|
|
8
|
+
super(props);
|
|
9
|
+
this.state = { hasError: false };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static getDerivedStateFromError() {
|
|
13
|
+
return { hasError: true };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
componentDidCatch(error: Error, errorInfo: any) {
|
|
17
|
+
// eslint-disable-next-line no-console
|
|
18
|
+
console.error('Emoji Picker React failed to render:', error, errorInfo);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
render() {
|
|
22
|
+
if (this.state.hasError) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return this.props.children;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { useOnMouseMove } from '../../hooks/useDisallowMouseMove';
|
|
4
4
|
import { useMouseDownHandlers } from '../../hooks/useMouseDownHandlers';
|
|
5
5
|
import { useOnScroll } from '../../hooks/useOnScroll';
|
|
6
6
|
import { useBodyRef } from '../context/ElementRefContext';
|
|
7
7
|
|
|
8
8
|
import { EmojiList } from './EmojiList';
|
|
9
9
|
import { EmojiVariationPicker } from './EmojiVariationPicker';
|
|
10
|
+
|
|
10
11
|
import './Body.css';
|
|
11
12
|
|
|
12
13
|
export function Body() {
|
|
13
14
|
const BodyRef = useBodyRef();
|
|
14
|
-
useActiveCategoryScrollDetection(BodyRef);
|
|
15
15
|
useOnScroll(BodyRef);
|
|
16
16
|
useMouseDownHandlers(BodyRef);
|
|
17
|
+
useOnMouseMove();
|
|
17
18
|
|
|
18
19
|
return (
|
|
19
20
|
<div className="epr-body" ref={BodyRef}>
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
.EmojiPickerReact li.epr-emoji-category > .epr-emoji-category-content {
|
|
2
|
+
position: relative;
|
|
3
|
+
margin: var(--epr-category-padding);
|
|
4
|
+
display: grid;
|
|
5
|
+
grid-template-columns: repeat(auto-fill, var(--epr-emoji-fullsize));
|
|
6
|
+
grid-gap: 0;
|
|
7
|
+
justify-content: space-between;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.EmojiPickerReact li.epr-emoji-category:not(:has(.epr-visible)) {
|
|
11
|
+
display: none;
|
|
12
|
+
}
|
|
13
|
+
|
|
1
14
|
.EmojiPickerReact li.epr-emoji-category > .epr-emoji-category-label {
|
|
2
15
|
position: sticky;
|
|
3
16
|
top: 0;
|
|
@@ -8,35 +21,8 @@
|
|
|
8
21
|
text-transform: capitalize;
|
|
9
22
|
backdrop-filter: blur(3px);
|
|
10
23
|
padding: var(--epr-category-label-padding);
|
|
11
|
-
background-color: var(--epr-category-label-
|
|
24
|
+
background-color: var(--epr-category-label-bg-color);
|
|
12
25
|
color: var(--epr-category-label-text-color);
|
|
13
26
|
z-index: var(--epr-category-label-z-index);
|
|
14
27
|
height: var(--epr-category-label-height);
|
|
15
28
|
}
|
|
16
|
-
|
|
17
|
-
.EmojiPickerReact.epr-search-active
|
|
18
|
-
li.epr-emoji-category
|
|
19
|
-
> .epr-emoji-category-label {
|
|
20
|
-
position: relative;
|
|
21
|
-
opacity: 0.4;
|
|
22
|
-
}
|
|
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,15 @@ 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
|
+
aria-label={categoryName}
|
|
35
36
|
>
|
|
36
|
-
<div className=
|
|
37
|
-
{children}
|
|
37
|
+
<div className={ClassNames.label}>{categoryName}</div>
|
|
38
|
+
<div className={ClassNames.categoryContent}>{children}</div>
|
|
38
39
|
</li>
|
|
39
40
|
);
|
|
40
41
|
}
|