emoji-picker-react 5.0.1 → 5.0.2
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/data/index.js +1 -1
- package/dist/data-core/prepare.d.ts +6 -0
- package/dist/esm/chunks/{chunk-F2KS7DCA.mjs → chunk-56D5WCOZ.mjs} +26 -32
- package/dist/esm/chunks/{chunk-L5W4PFRJ.mjs → chunk-IODYR4CR.mjs} +1 -1
- package/dist/esm/chunks/{chunk-ACXAQEKG.mjs → chunk-RU7G34WK.mjs} +10 -3
- package/dist/esm/data.mjs +2 -2
- package/dist/esm/index.mjs +3 -3
- package/dist/esm/primitives/index.mjs +2 -2
- package/dist/hooks/preloadEmoji.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/primitives/index.js +1 -1
- package/llms-full.txt +3 -0
- package/package.json +2 -2
- package/src/components/context/PickerDataContext.tsx +2 -1
- package/src/components/header/SkinTonePicker/BtnSkinToneVariation.tsx +7 -6
- package/src/config/config.ts +6 -3
- package/src/data-core/pickerData.ts +7 -9
- package/src/data-core/prepare.ts +12 -4
- package/src/data-core/search.ts +10 -2
- package/src/dataUtils/suggestedEmojis.ts +25 -39
- package/src/hooks/preloadEmoji.ts +12 -6
- package/src/hooks/useDisallowedEmojis.ts +6 -1
- package/src/hooks/useEmojiVirtualization.tsx +11 -1
package/llms-full.txt
CHANGED
|
@@ -1633,6 +1633,7 @@ Existing `reactionsDefaultOpen`, `allowExpandReactions`, `reactions`, `onReactio
|
|
|
1633
1633
|
|
|
1634
1634
|
For standard emoji IDs:
|
|
1635
1635
|
- matching is case-insensitive;
|
|
1636
|
+
- matching ignores where U+FE0F appears: `'2764'` and `'2764-fe0f'` both resolve to the dataset's own spelling, the same rule `reactions` and `hiddenEmojis` use;
|
|
1636
1637
|
- a valid variation remains that exact variation for rendering rather than collapsing to the neutral base emoji;
|
|
1637
1638
|
- duplicates are removed by canonical normalized ID, first occurrence wins.
|
|
1638
1639
|
|
|
@@ -2370,6 +2371,7 @@ The following variables are already documented public customization surface and
|
|
|
2370
2371
|
### Skin tone
|
|
2371
2372
|
- `--epr-skin-tone-picker-menu-color`
|
|
2372
2373
|
- `--epr-skin-tone-size`
|
|
2374
|
+
- `--epr-skin-tone-neutral-color`, `--epr-skin-tone-light-color`, `--epr-skin-tone-medium-light-color`, `--epr-skin-tone-medium-color`, `--epr-skin-tone-medium-dark-color`, `--epr-skin-tone-dark-color` — the tone swatch colors (defaults `#ffd225`, `#ffdfbd`, `#e9c197`, `#c88e62`, `#a86637`, `#60463a`).
|
|
2373
2375
|
|
|
2374
2376
|
### Dark mode
|
|
2375
2377
|
- `--epr-dark-bg-color`
|
|
@@ -3028,6 +3030,7 @@ No other data helpers are part of v5.
|
|
|
3028
3030
|
- trims surrounding whitespace;
|
|
3029
3031
|
- normalizes hexadecimal unified code to lowercase;
|
|
3030
3032
|
- accepts base or variation unified code;
|
|
3033
|
+
- accepts either U+FE0F spelling (`2764` or `2764-fe0f`); an exact match wins;
|
|
3031
3034
|
- variation lookup returns the canonical/base EmojiInfo whose `variations` includes the requested variation;
|
|
3032
3035
|
- returns `undefined` for unknown/empty input;
|
|
3033
3036
|
- does not mutate the dataset;
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "5.0.
|
|
2
|
+
"version": "5.0.2",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"typings": "dist/index.d.ts",
|
|
@@ -183,7 +183,7 @@
|
|
|
183
183
|
},
|
|
184
184
|
{
|
|
185
185
|
"path": "dist/esm/index.mjs",
|
|
186
|
-
"limit": "
|
|
186
|
+
"limit": "76 KB"
|
|
187
187
|
},
|
|
188
188
|
{
|
|
189
189
|
"path": "dist/esm/primitives/index.mjs",
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
useSuggestedEmojisModeConfig,
|
|
6
6
|
} from '../../config/useConfig';
|
|
7
7
|
import { getPickerDataSnapshot } from '../../data-core/pickerData';
|
|
8
|
-
import { normalizeQuery } from '../../data-core/prepare';
|
|
8
|
+
import { normalizeQuery, withoutFe0f } from '../../data-core/prepare';
|
|
9
9
|
import { searchEmojis } from '../../data-core/search';
|
|
10
10
|
import {
|
|
11
11
|
DataEmoji,
|
|
@@ -79,6 +79,7 @@ export function PickerDataProvider({
|
|
|
79
79
|
|
|
80
80
|
const result =
|
|
81
81
|
data.allEmojisByUnified[unified] ??
|
|
82
|
+
data.allEmojisByUnified[withoutFe0f(unified)] ??
|
|
82
83
|
data.allEmojisByUnified[unifiedWithoutSkinTone(unified)];
|
|
83
84
|
return result;
|
|
84
85
|
},
|
|
@@ -108,23 +108,24 @@ const styles = /* @__PURE__ */ (() =>
|
|
|
108
108
|
':focus': {
|
|
109
109
|
boxShadow: '0 0 0 3px var(--epr-focus-bg-color)',
|
|
110
110
|
},
|
|
111
|
+
// Swatch colors are themeable; the fallbacks are the defaults.
|
|
111
112
|
'&.epr-tone-neutral': {
|
|
112
|
-
backgroundColor: '#ffd225',
|
|
113
|
+
backgroundColor: 'var(--epr-skin-tone-neutral-color, #ffd225)',
|
|
113
114
|
},
|
|
114
115
|
'&.epr-tone-1f3fb': {
|
|
115
|
-
backgroundColor: '#ffdfbd',
|
|
116
|
+
backgroundColor: 'var(--epr-skin-tone-light-color, #ffdfbd)',
|
|
116
117
|
},
|
|
117
118
|
'&.epr-tone-1f3fc': {
|
|
118
|
-
backgroundColor: '#e9c197',
|
|
119
|
+
backgroundColor: 'var(--epr-skin-tone-medium-light-color, #e9c197)',
|
|
119
120
|
},
|
|
120
121
|
'&.epr-tone-1f3fd': {
|
|
121
|
-
backgroundColor: '#c88e62',
|
|
122
|
+
backgroundColor: 'var(--epr-skin-tone-medium-color, #c88e62)',
|
|
122
123
|
},
|
|
123
124
|
'&.epr-tone-1f3fe': {
|
|
124
|
-
backgroundColor: '#a86637',
|
|
125
|
+
backgroundColor: 'var(--epr-skin-tone-medium-dark-color, #a86637)',
|
|
125
126
|
},
|
|
126
127
|
'&.epr-tone-1f3ff': {
|
|
127
|
-
backgroundColor: '#60463a',
|
|
128
|
+
backgroundColor: 'var(--epr-skin-tone-dark-color, #60463a)',
|
|
128
129
|
},
|
|
129
130
|
},
|
|
130
131
|
}))();
|
package/src/config/config.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { DEFAULT_REACTIONS } from '../components/Reactions/DEFAULT_REACTIONS';
|
|
4
4
|
import { GetEmojiUrl } from '../components/emoji/BaseEmojiProps';
|
|
5
|
+
import { withoutFe0f } from '../data-core/prepare';
|
|
5
6
|
import { emojiUrlByUnified } from '../dataUtils/emojiUtils';
|
|
6
7
|
import type { EmojiDataInput } from '../hooks/useResolvedEmojiData';
|
|
7
8
|
import {
|
|
@@ -27,7 +28,8 @@ import {
|
|
|
27
28
|
} from './categoryConfig';
|
|
28
29
|
import { CustomEmoji } from './customEmojiConfig';
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
// Stored without U+FE0F, like every hiddenEmojis entry.
|
|
32
|
+
const KNOWN_FAILING_EMOJIS = ['2640', '2642', '2695'];
|
|
31
33
|
|
|
32
34
|
export const DEFAULT_SEARCH_PLACEHOLDER = 'Search';
|
|
33
35
|
export const DEFAULT_PICKER_WIDTH = 350;
|
|
@@ -171,9 +173,10 @@ export function mergeConfig(
|
|
|
171
173
|
|
|
172
174
|
// Dataset ids are lowercase; accept the uppercase spelling consumers
|
|
173
175
|
// copy from docs and tools (and stray whitespace), like `reactions` and
|
|
174
|
-
// `suggestedEmojis` already do.
|
|
176
|
+
// `suggestedEmojis` already do. Kept without U+FE0F, so either spelling
|
|
177
|
+
// of an ID hides the emoji (see useIsEmojiDisallowed).
|
|
175
178
|
config.hiddenEmojis.forEach((emoji) => {
|
|
176
|
-
const id = String(emoji).trim().toLowerCase();
|
|
179
|
+
const id = withoutFe0f(String(emoji).trim().toLowerCase());
|
|
177
180
|
if (id) config.unicodeToHide.add(id);
|
|
178
181
|
});
|
|
179
182
|
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
} from '../dataUtils/DataTypes';
|
|
22
22
|
import { Categories, type EmojiData } from '../types/exposedTypes';
|
|
23
23
|
|
|
24
|
-
import { getPreparedCore } from './prepare';
|
|
24
|
+
import { getPreparedCore, withoutFe0f } from './prepare';
|
|
25
25
|
|
|
26
26
|
export interface PickerDataSnapshot {
|
|
27
27
|
emojiData: EmojiData;
|
|
@@ -134,15 +134,13 @@ export function getPickerDataSnapshot(
|
|
|
134
134
|
|
|
135
135
|
const allEmojisByUnified: Record<string, DataEmoji> = Object.create(null);
|
|
136
136
|
|
|
137
|
+
// The base unified and each variation, and either U+FE0F spelling of
|
|
138
|
+
// them; an exact ID always wins over another record's spelling.
|
|
137
139
|
allEmojis.forEach((emoji) => {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
emoji[Keys.variations]?.forEach((variation) => {
|
|
143
|
-
allEmojisByUnified[variation] = emoji;
|
|
144
|
-
});
|
|
145
|
-
}
|
|
140
|
+
[emoji[Keys.unified], ...(emoji[Keys.variations] || [])].forEach((id) => {
|
|
141
|
+
allEmojisByUnified[id] = emoji;
|
|
142
|
+
allEmojisByUnified[withoutFe0f(id)] ||= emoji;
|
|
143
|
+
});
|
|
146
144
|
});
|
|
147
145
|
|
|
148
146
|
const snapshot: PickerDataSnapshot = {
|
package/src/data-core/prepare.ts
CHANGED
|
@@ -55,6 +55,15 @@ export function normalizeUnified(input: string): string {
|
|
|
55
55
|
return input.trim().toLowerCase();
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Datasets disagree on where U+FE0F (emoji presentation) appears in an ID:
|
|
60
|
+
* `2764` and `2764-fe0f` are the same heart. IDs are also indexed under
|
|
61
|
+
* this form, so either spelling finds the dataset's own record.
|
|
62
|
+
*/
|
|
63
|
+
export function withoutFe0f(unified: string): string {
|
|
64
|
+
return unified.replace(/-fe0f/g, '');
|
|
65
|
+
}
|
|
66
|
+
|
|
58
67
|
export function normalizeQuery(input: string): string {
|
|
59
68
|
if (!input || typeof input !== 'string') {
|
|
60
69
|
return '';
|
|
@@ -102,10 +111,9 @@ function appendEntry(
|
|
|
102
111
|
});
|
|
103
112
|
records.push(info);
|
|
104
113
|
byUnified.set(unified, info);
|
|
105
|
-
for (const
|
|
106
|
-
if (!byUnified.has(
|
|
107
|
-
|
|
108
|
-
}
|
|
114
|
+
for (const id of [unified, ...info.variations]) {
|
|
115
|
+
if (!byUnified.has(id)) byUnified.set(id, info);
|
|
116
|
+
if (!byUnified.has(withoutFe0f(id))) byUnified.set(withoutFe0f(id), info);
|
|
109
117
|
}
|
|
110
118
|
indexCharBucket(byCharMutable, names, info);
|
|
111
119
|
}
|
package/src/data-core/search.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
// Pure v5 data operations shared by the picker and `emoji-picker-react/data`.
|
|
2
2
|
// No React / ShipStyles imports allowed in this module graph.
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
getPreparedCore,
|
|
6
|
+
normalizeQuery,
|
|
7
|
+
normalizeUnified,
|
|
8
|
+
withoutFe0f,
|
|
9
|
+
} from './prepare';
|
|
5
10
|
import type { EmojiDataOptions, EmojiInfo } from './types';
|
|
6
11
|
|
|
7
12
|
export type { EmojiDataOptions, EmojiInfo };
|
|
@@ -74,7 +79,10 @@ export function getEmojiByUnified(
|
|
|
74
79
|
return undefined;
|
|
75
80
|
}
|
|
76
81
|
const core = getPreparedCore(options?.emojiData);
|
|
77
|
-
return
|
|
82
|
+
return (
|
|
83
|
+
core.byUnified.get(normalized) ??
|
|
84
|
+
core.byUnified.get(withoutFe0f(normalized))
|
|
85
|
+
);
|
|
78
86
|
}
|
|
79
87
|
|
|
80
88
|
/**
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { withoutFe0f } from '../data-core/prepare';
|
|
2
|
+
|
|
1
3
|
import { DataEmoji, EmojiProperties } from './DataTypes';
|
|
2
4
|
|
|
3
5
|
// Caller-defined Suggested contents.
|
|
@@ -47,46 +49,30 @@ export function resolveEntryId(
|
|
|
47
49
|
if (!id) {
|
|
48
50
|
return undefined;
|
|
49
51
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
let unified = id;
|
|
53
|
+
let record = lookup(id);
|
|
54
|
+
// Unified IDs and custom IDs are ASCII; anything else may be the emoji
|
|
55
|
+
// character itself, read as its code points.
|
|
56
|
+
if (!record && /[^\0-\x7f]/.test(id)) {
|
|
57
|
+
unified = Array.from(id)
|
|
58
|
+
.map((char) =>
|
|
59
|
+
(char.codePointAt(0) as number).toString(16).padStart(4, '0'),
|
|
60
|
+
)
|
|
61
|
+
.join('-');
|
|
62
|
+
record = lookup(unified);
|
|
57
63
|
}
|
|
58
|
-
return nativeToUnifiedCandidates(id).find((candidate) =>
|
|
59
|
-
isKnownRenderId(candidate, lookup),
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Dataset IDs are zero-padded code points joined by "-", and carry U+FE0F
|
|
64
|
-
// where the emoji presentation needs it; typed or stored characters often
|
|
65
|
-
// omit or add it. Try the exact sequence, then without FE0F, then with
|
|
66
|
-
// FE0F after the first code point (e.g. "©" -> "00a9-fe0f").
|
|
67
|
-
function nativeToUnifiedCandidates(native: string): string[] {
|
|
68
|
-
const codePoints = Array.from(native).map((char) =>
|
|
69
|
-
(char.codePointAt(0) as number).toString(16).padStart(4, '0'),
|
|
70
|
-
);
|
|
71
|
-
const exact = codePoints.join('-');
|
|
72
|
-
const bare = codePoints.filter((codePoint) => codePoint !== 'fe0f');
|
|
73
|
-
const withPresentation = [bare[0], 'fe0f', ...bare.slice(1)];
|
|
74
|
-
return [exact, bare.join('-'), withPresentation.join('-')];
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// A render identity is valid only as the base unified or a listed
|
|
78
|
-
// variation. Anything else — e.g. a nonexistent variation that fell back
|
|
79
|
-
// to the base record — is unknown and ignored.
|
|
80
|
-
function isKnownRenderId(
|
|
81
|
-
id: string,
|
|
82
|
-
lookup: (normalizedId: string) => DataEmoji | undefined,
|
|
83
|
-
): boolean {
|
|
84
|
-
const record = lookup(id);
|
|
85
64
|
if (!record) {
|
|
86
|
-
return
|
|
65
|
+
return undefined;
|
|
87
66
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
67
|
+
// A render identity is the base unified or a listed variation; a
|
|
68
|
+
// nonexistent variation that fell back to the base record is unknown.
|
|
69
|
+
// Typed, stored or other datasets' IDs often omit or add U+FE0F: resolve
|
|
70
|
+
// to the record's own spelling of that identity.
|
|
71
|
+
const known = [
|
|
72
|
+
record[EmojiProperties.unified],
|
|
73
|
+
...(record[EmojiProperties.variations] ?? []),
|
|
74
|
+
];
|
|
75
|
+
return known.includes(unified)
|
|
76
|
+
? unified
|
|
77
|
+
: known.find((each) => withoutFe0f(each) === withoutFe0f(unified));
|
|
92
78
|
}
|
|
@@ -7,24 +7,30 @@ export function preloadEmoji(
|
|
|
7
7
|
getEmojiUrl: GetEmojiUrl,
|
|
8
8
|
emoji: undefined | DataEmoji,
|
|
9
9
|
emojiStyle: EmojiStyleValue,
|
|
10
|
+
rendered?: string,
|
|
10
11
|
): void {
|
|
11
12
|
if (!emoji) {
|
|
12
13
|
return;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
const unified = emojiUnified(emoji);
|
|
17
|
+
// With `rendered`, only the image the cell renders (the active tone's, or
|
|
18
|
+
// the base) is fetched. It is marked apart from a full preload, so a
|
|
19
|
+
// later full preload (on focus) still fetches every tone; a full one
|
|
20
|
+
// covers it.
|
|
21
|
+
const key = rendered ? rendered + '/only' : unified;
|
|
16
22
|
|
|
17
|
-
if (preloadedEmojs.has(unified)) {
|
|
23
|
+
if (preloadedEmojs.has(unified) || preloadedEmojs.has(key)) {
|
|
18
24
|
return;
|
|
19
25
|
}
|
|
20
|
-
preloadedEmojs.add(
|
|
26
|
+
preloadedEmojs.add(key);
|
|
21
27
|
|
|
22
|
-
emojiVariations(emoji)
|
|
23
|
-
|
|
24
|
-
.forEach((variation) => {
|
|
28
|
+
(rendered ? [rendered] : emojiVariations(emoji).concat(unified)).forEach(
|
|
29
|
+
(variation) => {
|
|
25
30
|
const emojiUrl = getEmojiUrl(variation, emojiStyle);
|
|
26
31
|
preloadImage(emojiUrl);
|
|
27
|
-
}
|
|
32
|
+
},
|
|
33
|
+
);
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
export const preloadedEmojs: Set<string> = new Set();
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
useNativeEmojiSupport,
|
|
6
6
|
} from '../components/context/PickerContext';
|
|
7
7
|
import { useEmojiVersionConfig, useUnicodeToHide } from '../config/useConfig';
|
|
8
|
+
import { withoutFe0f } from '../data-core/prepare';
|
|
8
9
|
import { DataEmoji } from '../dataUtils/DataTypes';
|
|
9
10
|
import {
|
|
10
11
|
addedIn,
|
|
@@ -27,7 +28,11 @@ export function useIsEmojiDisallowed() {
|
|
|
27
28
|
emoji: DataEmoji,
|
|
28
29
|
renderedUnified = emojiUnified(emoji, activeSkinTone),
|
|
29
30
|
) {
|
|
30
|
-
if (
|
|
31
|
+
if (
|
|
32
|
+
unicodeToHide.has(
|
|
33
|
+
withoutFe0f(unifiedWithoutSkinTone(emojiUnified(emoji))),
|
|
34
|
+
)
|
|
35
|
+
)
|
|
31
36
|
return true;
|
|
32
37
|
if (isCustomEmoji(emoji)) return false;
|
|
33
38
|
const version = addedIn(emoji);
|
|
@@ -169,8 +169,18 @@ export function useEmojiVirtualization({
|
|
|
169
169
|
useEffect(() => requestNativeProbe(probe));
|
|
170
170
|
// Image requests belong to committed work, never an abandoned render.
|
|
171
171
|
// The proximity and cache checks run synchronously, without per-cell timers.
|
|
172
|
+
// Only the image a nearby cell will render is fetched: every skin tone of
|
|
173
|
+
// every nearby cell is hundreds of requests per scroll and long frames on
|
|
174
|
+
// slow devices. Focusing a cell (useOnFocus) fetches its variations.
|
|
172
175
|
useEffect(() => {
|
|
173
|
-
preloads.forEach((emoji) =>
|
|
176
|
+
preloads.forEach((emoji) =>
|
|
177
|
+
preloadEmoji(
|
|
178
|
+
getEmojiUrl,
|
|
179
|
+
emoji,
|
|
180
|
+
emojiStyle,
|
|
181
|
+
emojiUnified(emoji, activeSkinTone),
|
|
182
|
+
),
|
|
183
|
+
);
|
|
174
184
|
});
|
|
175
185
|
|
|
176
186
|
return {
|