Skip to content

Commit bfeb541

Browse files
committed
overhaul the replaceEmojis function, fix #6
uses "Extended_Pictographic" now instead of "Emoji" then checks for components (skin color, hair) then looks for joined characters (zero width joiner) doesn't replace Emojis ending with a \ufe0e character ("text style" marker)
1 parent a1e01a8 commit bfeb541

2 files changed

Lines changed: 40 additions & 27 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
node_modules
44
lib
55
src
6+
*.tgz

build/main.js

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -35,49 +35,61 @@ function reactReplaceEmojis(reactChild, options) {
3535
}
3636

3737
function replaceEmojis(string, options) {
38+
if (!string) return;
39+
let array = [string]
40+
3841
options = {
3942
size: typeof options?.size === 'string' ? options.size : undefined,
4043
outline: typeof options?.outline === 'boolean' ? options.outline : undefined
41-
}
42-
if (!string) return;
43-
const emojis = string.match(/[\p{Emoji}\u200d\ufe0f]+/gu);
44-
if (!emojis) return string;
44+
};
4545

46-
string = string.split(/([\p{Emoji}\u200d\ufe0f]+)/gu);
46+
// matches all emojis matches all attached components matches that is joined (zwj)
47+
// \p{Extended_Pictographic}[\u{1f3fb}-\u{1f3ff}\u{1f9b0}-\u{1f9b3}]?(\u200d.)?\ufe0f?(?!\ufe0e)
48+
// matches only non-"text style" emojis
4749

48-
// replace emojis with SVGs
49-
emojis.forEach((emoji, i) => {
50-
// get the char codes of the emojis
51-
let unicode = "";
50+
const regex = /\p{Extended_Pictographic}[\u{1f3fb}-\u{1f3ff}\u{1f9b0}-\u{1f9b3}]?(\u200d.)?\ufe0f?(?!\ufe0e)/gu;
51+
let m;
5252

53-
function getNextChar(pointer) {
54-
const subUnicode = emoji.codePointAt(pointer);
55-
if (!subUnicode) return;
56-
if (!(subUnicode >= 56320 && subUnicode <= 57343)) { // 56320-57343: Low Surrogates Character
57-
unicode += '-' + subUnicode.toString(16);
58-
}
59-
getNextChar(++pointer);
53+
while ((m = regex.exec(string)) !== null) {
54+
// This is necessary to avoid infinite loops with zero-width matches
55+
if (m.index === regex.lastIndex) {
56+
regex.lastIndex++;
6057
}
6158

62-
getNextChar(0);
63-
unicode = unicode.substr(1);
59+
// find the code for the emoji
60+
let emojiName = 'U', done = false
61+
for (let i = 0; !done; i++) {
62+
let subUnicode = m[0].codePointAt(i)
63+
// dismiss low surrogates characters (56320-57343)
64+
if ((subUnicode >= 56320 && subUnicode <= 57343)) continue
65+
emojiName += '_' + subUnicode?.toString(16).toUpperCase()
6466

65-
const emojiName = `U_${unicode.toUpperCase().replace(/-/g, '_')}`;
67+
// check if is done: if this hexcode is longer than 4, check the next but one codepoint
68+
done = m[0].codePointAt(i)?.toString(16).length > 4
69+
? !m[0].codePointAt(i + 2)
70+
: !m[0].codePointAt(i + 1)
71+
}
6672

67-
const emojiIndex = string.indexOf(emoji);
6873
let emojiSvg = Emoji[emojiName];
69-
70-
options.key = i
71-
7274
if (emojiSvg) {
73-
string[emojiIndex] = React.createElement(emojiSvg, options);
75+
// gets last string ['String with {Emoji} and {Emoji} in it'] -> 'String with {Emoji} and {Emoji} in it'
76+
let workingString = array.pop()
77+
// 'String with {Emoji} and {Emoji} in it' -> ['String with ', ' and ', ' in it']
78+
workingString = workingString.split(m[0])
79+
// [] -> ['String with ']
80+
// ['String with ', ' and ', ' in it'] -> [' and ', ' in it']
81+
array.push(workingString.shift())
82+
// ['String with '] -> ['String with ', <Emoji>]
83+
array.push(React.createElement(emojiSvg, options))
84+
// [' and ', ' in it'] -> ' and {Emoji} in it'
85+
// ['String with ', <Emoji>] -> ['String with ', <Emoji>, ' and {Emoji} in it']
86+
array.push(workingString.join(m[0]))
7487
} else {
7588
console.warn('SVG not found: ' + emojiName);
7689
}
77-
})
90+
}
7891

79-
// return converted react HTML
80-
return string;
92+
return array;
8193
}
8294

8395
reactReplaceEmojis.replaceEmojis = replaceEmojis

0 commit comments

Comments
 (0)