emoji-picker-react 4.0.0-rc.20 → 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/dist/DomUtils/keyboardNavigation.d.ts +1 -0
- package/dist/emoji-picker-react.cjs.development.css +2 -2
- package/dist/emoji-picker-react.cjs.development.js +201 -115
- 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 +203 -117
- package/dist/emoji-picker-react.esm.js.map +1 -1
- package/dist/hooks/useFilter.d.ts +1 -0
- package/package.json +1 -1
- package/src/DomUtils/keyboardNavigation.ts +9 -3
- package/src/DomUtils/selectors.ts +5 -5
- package/src/EmojiPickerReact.css +3 -0
- package/src/components/body/EmojiCategory.css +14 -19
- package/src/components/body/EmojiCategory.tsx +1 -1
- package/src/hooks/useEmojiPreviewEvents.ts +19 -1
- package/src/hooks/useFilter.ts +14 -1
- package/src/hooks/useKeyboardNavigation.ts +59 -7
- package/dist/DomUtils/lastElementChild.d.ts +0 -2
- package/src/DomUtils/lastElementChild.ts +0 -7
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DataEmoji } from '../dataUtils/DataTypes';
|
|
2
2
|
export declare function useClearSearch(): () => void;
|
|
3
|
+
export declare function useAppendSearch(): (str: string) => void;
|
|
3
4
|
export declare function useFilter(): {
|
|
4
5
|
onChange: (nextValue: string) => void;
|
|
5
6
|
searchTerm: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
elementCountInRow,
|
|
3
3
|
elementIndexInRow,
|
|
4
|
-
firstVisibleElementInContainer,
|
|
5
4
|
getElementInNextRow,
|
|
6
5
|
getElementInPrevRow,
|
|
7
6
|
getElementInRow,
|
|
@@ -12,6 +11,7 @@ import { focusElement } from './focusElement';
|
|
|
12
11
|
import {
|
|
13
12
|
allVisibleEmojis,
|
|
14
13
|
closestCategory,
|
|
14
|
+
firstVisibleEmoji,
|
|
15
15
|
lastVisibleEmoji,
|
|
16
16
|
nextCategory,
|
|
17
17
|
nextVisibleEmoji,
|
|
@@ -21,8 +21,14 @@ import {
|
|
|
21
21
|
} from './selectors';
|
|
22
22
|
|
|
23
23
|
export function focusFirstVisibleEmoji(parent: NullableElement) {
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
focusElement(firstVisibleEmoji(parent));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function focusAndClickFirstVisibleEmoji(parent: NullableElement) {
|
|
28
|
+
const firstEmoji = firstVisibleEmoji(parent);
|
|
29
|
+
|
|
30
|
+
focusElement(firstEmoji);
|
|
31
|
+
firstEmoji?.click();
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
export function focusLastVisibleEmoji(parent: NullableElement) {
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from '../dataUtils/emojiSelectors';
|
|
6
6
|
|
|
7
7
|
import { asSelectors, ClassNames } from './classNames';
|
|
8
|
-
import {
|
|
8
|
+
import { firstVisibleElementInContainer } from './elementPositionInRow';
|
|
9
9
|
|
|
10
10
|
export type NullableElement = HTMLElement | null;
|
|
11
11
|
|
|
@@ -135,8 +135,8 @@ export function allVisibleEmojis(parent: NullableElement) {
|
|
|
135
135
|
export function lastVisibleEmoji(element: NullableElement): NullableElement {
|
|
136
136
|
if (!element) return null;
|
|
137
137
|
|
|
138
|
-
const
|
|
139
|
-
|
|
138
|
+
const allEmojis = allVisibleEmojis(element);
|
|
139
|
+
const [last] = allEmojis.slice(-1);
|
|
140
140
|
if (!last) {
|
|
141
141
|
return null;
|
|
142
142
|
}
|
|
@@ -181,9 +181,9 @@ export function firstVisibleEmoji(parent: NullableElement) {
|
|
|
181
181
|
return null;
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
const
|
|
184
|
+
const allEmojis = allVisibleEmojis(parent);
|
|
185
185
|
|
|
186
|
-
return
|
|
186
|
+
return firstVisibleElementInContainer(parent, allEmojis);
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
export function prevCategory(element: NullableElement): NullableElement {
|
package/src/EmojiPickerReact.css
CHANGED
|
@@ -58,6 +58,9 @@
|
|
|
58
58
|
/* Emoji */
|
|
59
59
|
--epr-emoji-size: 30px;
|
|
60
60
|
--epr-emoji-padding: 5px;
|
|
61
|
+
--epr-emoji-fullsize: calc(
|
|
62
|
+
var(--epr-emoji-size) + var(--epr-emoji-padding) * 2
|
|
63
|
+
);
|
|
61
64
|
--epr-emoji-hover-color: var(--epr-hover-bg-color);
|
|
62
65
|
--epr-emoji-variation-indicator-color: var(--epr-picker-border-color);
|
|
63
66
|
--epr-emoji-variation-indicator-color-hover: var(--epr-text-color);
|
|
@@ -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,22 +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
|
-
}
|
|
32
|
-
|
|
33
|
-
.EmojiPickerReact li.epr-emoji-category:after {
|
|
34
|
-
content: '';
|
|
35
|
-
display: inline-block;
|
|
36
|
-
flex: 1;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.EmojiPickerReact li.epr-emoji-category:not(:has(.epr-visible)) {
|
|
40
|
-
display: none;
|
|
41
|
-
}
|
|
@@ -22,6 +22,10 @@ export function useEmojiPreviewEvents(
|
|
|
22
22
|
}
|
|
23
23
|
const bodyRef = BodyRef.current;
|
|
24
24
|
|
|
25
|
+
bodyRef?.addEventListener('keydown', onEscape, {
|
|
26
|
+
passive: true
|
|
27
|
+
});
|
|
28
|
+
|
|
25
29
|
bodyRef?.addEventListener('mouseover', onMouseOver, {
|
|
26
30
|
passive: true
|
|
27
31
|
});
|
|
@@ -51,14 +55,28 @@ export function useEmojiPreviewEvents(
|
|
|
51
55
|
originalUnified
|
|
52
56
|
});
|
|
53
57
|
}
|
|
54
|
-
function onLeave() {
|
|
58
|
+
function onLeave(e?: FocusEvent | MouseEvent) {
|
|
59
|
+
if (e) {
|
|
60
|
+
const button = buttonFromTarget(e.target as HTMLElement);
|
|
61
|
+
|
|
62
|
+
if (button) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
55
67
|
setPreviewEmoji(null);
|
|
56
68
|
}
|
|
69
|
+
function onEscape(e: KeyboardEvent) {
|
|
70
|
+
if (e.key === 'Escape') {
|
|
71
|
+
setPreviewEmoji(null);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
57
74
|
return () => {
|
|
58
75
|
bodyRef?.removeEventListener('mouseover', onMouseOver);
|
|
59
76
|
bodyRef?.removeEventListener('mouseout', onLeave);
|
|
60
77
|
bodyRef?.removeEventListener('focus', onEnter, true);
|
|
61
78
|
bodyRef?.removeEventListener('blur', onLeave, true);
|
|
79
|
+
bodyRef?.removeEventListener('keydown', onEscape);
|
|
62
80
|
};
|
|
63
81
|
}, [BodyRef, allow, setPreviewEmoji]);
|
|
64
82
|
|
package/src/hooks/useFilter.ts
CHANGED
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
import { DataEmoji } from '../dataUtils/DataTypes';
|
|
19
19
|
import { emojiNames } from '../dataUtils/emojiSelectors';
|
|
20
20
|
|
|
21
|
-
|
|
22
21
|
function useSetFilterRef() {
|
|
23
22
|
const filterRef = useFilterRef();
|
|
24
23
|
|
|
@@ -45,6 +44,20 @@ export function useClearSearch() {
|
|
|
45
44
|
};
|
|
46
45
|
}
|
|
47
46
|
|
|
47
|
+
export function useAppendSearch() {
|
|
48
|
+
const SearchInputRef = useSearchInputRef();
|
|
49
|
+
const [, setSearchTerm] = useSearchTermState();
|
|
50
|
+
|
|
51
|
+
return function appendSearch(str: string) {
|
|
52
|
+
if (SearchInputRef.current) {
|
|
53
|
+
SearchInputRef.current.value = SearchInputRef.current.value + str;
|
|
54
|
+
setSearchTerm(SearchInputRef.current.value);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
setSearchTerm(str);
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
48
61
|
export function useFilter() {
|
|
49
62
|
const SearchInputRef = useSearchInputRef();
|
|
50
63
|
const filterRef = useFilterRef();
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from '../DomUtils/focusElement';
|
|
8
8
|
import { getActiveElement } from '../DomUtils/getActiveElement';
|
|
9
9
|
import {
|
|
10
|
+
focusAndClickFirstVisibleEmoji,
|
|
10
11
|
focusFirstVisibleEmoji,
|
|
11
12
|
focusNextVisibleEmoji,
|
|
12
13
|
focusPrevVisibleEmoji,
|
|
@@ -25,7 +26,7 @@ import {
|
|
|
25
26
|
import { useSkinToneFanOpenState } from '../components/context/PickerContext';
|
|
26
27
|
|
|
27
28
|
import { useCloseAllOpenToggles } from './useCloseAllOpenToggles';
|
|
28
|
-
import { useClearSearch } from './useFilter';
|
|
29
|
+
import { useAppendSearch, useClearSearch } from './useFilter';
|
|
29
30
|
import {
|
|
30
31
|
useFocusCategoryNavigation,
|
|
31
32
|
useFocusSearchInput,
|
|
@@ -38,7 +39,8 @@ enum KeyboardEvents {
|
|
|
38
39
|
ArrowUp = 'ArrowUp',
|
|
39
40
|
ArrowLeft = 'ArrowLeft',
|
|
40
41
|
ArrowRight = 'ArrowRight',
|
|
41
|
-
Escape = 'Escape'
|
|
42
|
+
Escape = 'Escape',
|
|
43
|
+
Enter = 'Enter'
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
export function useKeyboardNavigation() {
|
|
@@ -63,7 +65,7 @@ function usePickerMainKeyboardEvents() {
|
|
|
63
65
|
function onKeyDown(event: KeyboardEvent) {
|
|
64
66
|
const { key } = event;
|
|
65
67
|
|
|
66
|
-
if (key ===
|
|
68
|
+
if (key === KeyboardEvents.Escape) {
|
|
67
69
|
event.preventDefault();
|
|
68
70
|
clearSearch();
|
|
69
71
|
closeAllOpenToggles();
|
|
@@ -92,6 +94,7 @@ function usePickerMainKeyboardEvents() {
|
|
|
92
94
|
function useSearchInputKeyboardEvents() {
|
|
93
95
|
const focusSkinTonePicker = useFocusSkinTonePicker();
|
|
94
96
|
const PickerMainRef = usePickerMainRef();
|
|
97
|
+
const BodyRef = useBodyRef();
|
|
95
98
|
const SearchInputRef = useSearchInputRef();
|
|
96
99
|
const [, setSkinToneFanOpenState] = useSkinToneFanOpenState();
|
|
97
100
|
const goDownFromSearchInput = useGoDownFromSearchInput();
|
|
@@ -111,9 +114,18 @@ function useSearchInputKeyboardEvents() {
|
|
|
111
114
|
event.preventDefault();
|
|
112
115
|
goDownFromSearchInput();
|
|
113
116
|
break;
|
|
117
|
+
case KeyboardEvents.Enter:
|
|
118
|
+
event.preventDefault();
|
|
119
|
+
focusAndClickFirstVisibleEmoji(BodyRef.current);
|
|
120
|
+
break;
|
|
114
121
|
}
|
|
115
122
|
},
|
|
116
|
-
[
|
|
123
|
+
[
|
|
124
|
+
focusSkinTonePicker,
|
|
125
|
+
goDownFromSearchInput,
|
|
126
|
+
setSkinToneFanOpenState,
|
|
127
|
+
BodyRef
|
|
128
|
+
]
|
|
117
129
|
);
|
|
118
130
|
|
|
119
131
|
useEffect(() => {
|
|
@@ -137,6 +149,7 @@ function useSkinTonePickerKeyboardEvents() {
|
|
|
137
149
|
const SearchInputRef = useSearchInputRef();
|
|
138
150
|
const goDownFromSearchInput = useGoDownFromSearchInput();
|
|
139
151
|
const [isOpen, setIsOpen] = useSkinToneFanOpenState();
|
|
152
|
+
const onType = useOnType();
|
|
140
153
|
|
|
141
154
|
const onKeyDown = useMemo(
|
|
142
155
|
() =>
|
|
@@ -165,9 +178,12 @@ function useSkinTonePickerKeyboardEvents() {
|
|
|
165
178
|
}
|
|
166
179
|
goDownFromSearchInput();
|
|
167
180
|
break;
|
|
181
|
+
default:
|
|
182
|
+
onType(event);
|
|
183
|
+
break;
|
|
168
184
|
}
|
|
169
185
|
},
|
|
170
|
-
[isOpen, focusSearchInput, setIsOpen, goDownFromSearchInput]
|
|
186
|
+
[isOpen, focusSearchInput, setIsOpen, goDownFromSearchInput, onType]
|
|
171
187
|
);
|
|
172
188
|
|
|
173
189
|
useEffect(() => {
|
|
@@ -189,6 +205,7 @@ function useCategoryNavigationKeyboardEvents() {
|
|
|
189
205
|
const focusSearchInput = useFocusSearchInput();
|
|
190
206
|
const CategoryNavigationRef = useCategoryNavigationRef();
|
|
191
207
|
const BodyRef = useBodyRef();
|
|
208
|
+
const onType = useOnType();
|
|
192
209
|
|
|
193
210
|
const onKeyDown = useMemo(
|
|
194
211
|
() =>
|
|
@@ -212,9 +229,12 @@ function useCategoryNavigationKeyboardEvents() {
|
|
|
212
229
|
event.preventDefault();
|
|
213
230
|
focusFirstVisibleEmoji(BodyRef.current);
|
|
214
231
|
break;
|
|
232
|
+
default:
|
|
233
|
+
onType(event);
|
|
234
|
+
break;
|
|
215
235
|
}
|
|
216
236
|
},
|
|
217
|
-
[BodyRef, focusSearchInput]
|
|
237
|
+
[BodyRef, focusSearchInput, onType]
|
|
218
238
|
);
|
|
219
239
|
|
|
220
240
|
useEffect(() => {
|
|
@@ -236,6 +256,8 @@ function useBodyKeyboardEvents() {
|
|
|
236
256
|
const BodyRef = useBodyRef();
|
|
237
257
|
const goUpFromBody = useGoUpFromBody();
|
|
238
258
|
|
|
259
|
+
const onType = useOnType();
|
|
260
|
+
|
|
239
261
|
const onKeyDown = useMemo(
|
|
240
262
|
() =>
|
|
241
263
|
function onKeyDown(event: KeyboardEvent) {
|
|
@@ -260,9 +282,12 @@ function useBodyKeyboardEvents() {
|
|
|
260
282
|
event.preventDefault();
|
|
261
283
|
focusVisibleEmojiOneRowUp(activeElement, goUpFromBody);
|
|
262
284
|
break;
|
|
285
|
+
default:
|
|
286
|
+
onType(event);
|
|
287
|
+
break;
|
|
263
288
|
}
|
|
264
289
|
},
|
|
265
|
-
[goUpFromBody]
|
|
290
|
+
[goUpFromBody, onType]
|
|
266
291
|
);
|
|
267
292
|
|
|
268
293
|
useEffect(() => {
|
|
@@ -335,3 +360,30 @@ function focusPrevSkinTone() {
|
|
|
335
360
|
|
|
336
361
|
focusPrevElementSibling(currentSkinTone);
|
|
337
362
|
}
|
|
363
|
+
|
|
364
|
+
function useOnType() {
|
|
365
|
+
const appendSearch = useAppendSearch();
|
|
366
|
+
const focusSearchInput = useFocusSearchInput();
|
|
367
|
+
const closeAllOpenToggles = useCloseAllOpenToggles();
|
|
368
|
+
|
|
369
|
+
return function onType(event: KeyboardEvent) {
|
|
370
|
+
const { key } = event;
|
|
371
|
+
|
|
372
|
+
if (hasModifier(event)) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (key.match(/(^[a-zA-Z0-9]$){1}/)) {
|
|
377
|
+
event.preventDefault();
|
|
378
|
+
closeAllOpenToggles();
|
|
379
|
+
focusSearchInput();
|
|
380
|
+
appendSearch(key);
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function hasModifier(event: KeyboardEvent): boolean {
|
|
386
|
+
const { metaKey, ctrlKey, altKey } = event;
|
|
387
|
+
|
|
388
|
+
return metaKey || ctrlKey || altKey;
|
|
389
|
+
}
|