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.
@@ -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,5 +1,5 @@
1
1
  {
2
- "version": "4.0.0-rc.20",
2
+ "version": "4.0.0-rc.21",
3
3
  "license": "MIT",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
@@ -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
- const allEmojis = allVisibleEmojis(parent);
25
- focusElement(firstVisibleElementInContainer(parent, allEmojis));
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 { lastElementChild } from './lastElementChild';
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 last = lastElementChild(element);
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 emoji = parent.querySelector(VisibleEmojiSelector);
184
+ const allEmojis = allVisibleEmojis(parent);
185
185
 
186
- return emoji as HTMLElement;
186
+ return firstVisibleElementInContainer(parent, allEmojis);
187
187
  }
188
188
 
189
189
  export function prevCategory(element: NullableElement): NullableElement {
@@ -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
- }
@@ -34,7 +34,7 @@ export function EmojiCategory({
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
  }
@@ -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
 
@@ -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 === 'Escape') {
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
- [focusSkinTonePicker, goDownFromSearchInput, setSkinToneFanOpenState]
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
+ }
@@ -1,2 +0,0 @@
1
- import { NullableElement } from './selectors';
2
- export declare function lastElementChild(element: NullableElement): NullableElement;
@@ -1,7 +0,0 @@
1
- import { NullableElement } from './selectors';
2
-
3
- export function lastElementChild(element: NullableElement) {
4
- if (!element) return null;
5
-
6
- return element.lastElementChild as HTMLElement;
7
- }