-
Notifications
You must be signed in to change notification settings - Fork 835
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How can I add other utf8 characters as custom emojis? #886
Comments
Did you ever find a solution to this? I am looking for exactly the same thing. |
@booni3 Yes, here is a screenshot of the cross-platform Electron app I made for myself. You can see that I got the section identifier icons working (up top) but that I didn't find a way to add a section label above each section, so you can see that the final section label is "Flags" (which is built in to emoji-mart), and there are no labels for my extra custom sections afterward. Also, I think I've been having trouble with the fonts. You can see that the left and right arrows don't look quite right. I can look for the code later. |
Would be awesome to see how you did it. Was it within the standard configuration, or did you have to extend the package? |
It doesn't look like I forked emoji-mart. "@emoji-mart/data": "^1.1.2", import emojiMartData, {
type EmojiMartData as EmojiMartDataOriginal,
type Emoji as EmojiOriginal,
} from '@emoji-mart/data';
import unicodeMap from '../UnicodeMap.json';
export type EmojiId = string;
export type Emoji = EmojiOriginal & {
id: EmojiId;
native: string; // Why is this not in the offical type?
};
export type EmojiMartData = EmojiMartDataOriginal;
type EmojiMartSkin = {
src: string;
};
type IndexedEmojis = {
[key: string]: emojiMartData.Emoji;
};
export type EmojiMartCustomEmojis = {
id: string;
name: string;
emojis: {
id: string;
name: string;
keywords: string[];
skins: EmojiMartSkin[];
}[];
}[];
type CharactersMap = {
[category: string]: string;
};
const recent = 'recent';
const frequent = 'frequent';
const arrows = 'arrows';
const math = 'math';
const brands = 'twitter';
const vegan = 'vegan';
const customCharactersMap: CharactersMap = {
// TODO: For these, change font-family to "Segoe UI Symbol" instead of `EmojiMart, "Segoe UI Emoji", "Segoe UI Symbol", "Segoe UI", "Apple Color Emoji", "Twemoji Mozilla", "Noto Color Emoji", "Android Emoji"`
[arrows]: '►◄▲▼🡸🡹🡺🡻🡼🡽🡾🡿',
[math]: '≈±≡≠∞',
[brands]: '𝕏Ⓝ⋈',
[vegan]: 'ⓥⓋ🅥🌱',
};
const customCharactersCategories = Object.keys(customCharactersMap);
function getCustomEmoji(category: string, character: string): Emoji {
const id = `${character}`;
const charCode = character.codePointAt(0); // Use `codePointAt` instead of `charCodeAt` because some characters in Unicode are represented by a pair of code units known as a surrogate pair.
const zeroPaddedHexCode = charCode
?.toString(16)
.toUpperCase()
.padStart(4, '0');
if (!zeroPaddedHexCode) {
console.error({ character, charCode, zeroPaddedHexCode });
throw new Error('unicode not found');
}
let name = (unicodeMap as CharactersMap)[zeroPaddedHexCode];
if (!name) {
console.error('unicode name not found', {
character,
charCode,
zeroPaddedHexCode,
});
name = '?';
}
console.log({ charCode, zeroPaddedHexCode, name });
return {
id,
native: id,
name,
keywords: [category, id, name],
skins: [
{
unified: id,
native: id,
},
], // TODO
version: 1, // TODO
};
}
function getCustomEmojis(
category: string,
characters: string[],
): IndexedEmojis {
const emojis: IndexedEmojis = {};
// eslint-disable-next-line no-restricted-syntax
for (const character of characters) {
console.log({ character });
const customEmoji = getCustomEmoji(category, character);
emojis[customEmoji.id] = customEmoji;
}
return emojis;
}
function insertCustomCharacters(
emojiMartDataInner: EmojiMartData,
): EmojiMartData {
const result = { ...emojiMartDataInner };
// eslint-disable-next-line no-restricted-syntax
for (const category of customCharactersCategories) {
console.log({ category });
const customCharacters = Array.from(customCharactersMap[category]);
console.log({ customCharacters });
const charactersAsEmojis = getCustomEmojis(category, customCharacters);
const customCharacterEmojiIds = Object.keys(charactersAsEmojis);
console.log({ charactersAsEmojis, customCharacterEmojiIds });
result.emojis = { ...charactersAsEmojis, ...result.emojis };
result.categories.push({
id: category,
emojis: customCharacterEmojiIds,
});
}
return result;
}
/**
* https://missiveapp.com/open/emoji-mart/example-categories.html
*/
export const categoryIcons = {
[recent]: {
// https://react-icons.github.io/react-icons/search/#q=clock: BsFillClockFill
svg: '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 16 16" height="200px" width="200px" xmlns="http://www.w3.org/2000/svg"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .252.434l3.5 2a.5.5 0 0 0 .496-.868L8 8.71V3.5z"></path></svg>',
},
[frequent]: {
// https://react-icons.github.io/react-icons/search/#q=heart: BsFillSuitHeartFill
svg: '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 16 16" height="200px" width="200px" xmlns="http://www.w3.org/2000/svg"><path d="M4 1c2.21 0 4 1.755 4 3.92C8 2.755 9.79 1 12 1s4 1.755 4 3.92c0 3.263-3.234 4.414-7.608 9.608a.513.513 0 0 1-.784 0C3.234 9.334 0 8.183 0 4.92 0 2.755 1.79 1 4 1z"></path></svg>',
},
[arrows]: {
// https://react-icons.github.io/react-icons/search/#q=FaRightLeft
svg: '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 512 512" height="200px" width="200px" xmlns="http://www.w3.org/2000/svg"><path d="M32 96l320 0V32c0-12.9 7.8-24.6 19.8-29.6s25.7-2.2 34.9 6.9l96 96c6 6 9.4 14.1 9.4 22.6s-3.4 16.6-9.4 22.6l-96 96c-9.2 9.2-22.9 11.9-34.9 6.9s-19.8-16.6-19.8-29.6V160L32 160c-17.7 0-32-14.3-32-32s14.3-32 32-32zM480 352c17.7 0 32 14.3 32 32s-14.3 32-32 32H160v64c0 12.9-7.8 24.6-19.8 29.6s-25.7 2.2-34.9-6.9l-96-96c-6-6-9.4-14.1-9.4-22.6s3.4-16.6 9.4-22.6l96-96c9.2-9.2 22.9-11.9 34.9-6.9s19.8 16.6 19.8 29.6l0 64H480z"></path></svg>',
},
[math]: {
// https://react-icons.github.io/react-icons/search/#q=math
svg: '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" height="200px" width="200px" xmlns="http://www.w3.org/2000/svg"><path d="M7 2H5v3H2v2h3v3h2V7h3V5H7V2zm7 3h8v2h-8zm0 10h8v2h-8zm0 4h8v2h-8zm-5.71-4.71L6 16.59l-2.29-2.3-1.42 1.42L4.59 18l-2.3 2.29 1.42 1.42L6 19.41l2.29 2.3 1.42-1.42L7.41 18l2.3-2.29-1.42-1.42z"></path></svg>',
},
[brands]: {
// https://react-icons.github.io/react-icons/search/#q=twitter
svg: '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 512 512" height="200px" width="200px" xmlns="http://www.w3.org/2000/svg"><path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"></path></svg>',
},
[vegan]: {
// https://react-icons.github.io/react-icons/search/#q=plant
svg: '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 256 256" height="200px" width="200px" xmlns="http://www.w3.org/2000/svg"><path d="M205.41,159.07a60.9,60.9,0,0,1-31.83,8.86,71.71,71.71,0,0,1-27.36-5.66A55.55,55.55,0,0,0,136,194.51V224a8,8,0,0,1-8.53,8,8.18,8.18,0,0,1-7.47-8.25V211.31L81.38,172.69A52.5,52.5,0,0,1,63.44,176a45.82,45.82,0,0,1-23.92-6.67C17.73,156.09,6,125.62,8.27,87.79a8,8,0,0,1,7.52-7.52c37.83-2.23,68.3,9.46,81.5,31.25A46,46,0,0,1,103.74,140a4,4,0,0,1-6.89,2.43l-19.2-20.1a8,8,0,0,0-11.31,11.31l53.88,55.25c.06-.78.13-1.56.21-2.33a68.56,68.56,0,0,1,18.64-39.46l50.59-53.46a8,8,0,0,0-11.31-11.32l-49,51.82a4,4,0,0,1-6.78-1.74c-4.74-17.48-2.65-34.88,6.4-49.82,17.86-29.48,59.42-45.26,111.18-42.22a8,8,0,0,1,7.52,7.52C250.67,99.65,234.89,141.21,205.41,159.07Z"></path></svg>',
// https://react-icons.github.io/react-icons/search/#q=vegan
// '<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="200px" width="200px" xmlns="http://www.w3.org/2000/svg"><path d="M2 2a26.6 26.6 0 0 1 10 20c.9-6.82 1.5-9.5 4-14"></path><path d="M16 8c4 0 6-2 6-6-4 0-6 2-6 6"></path><path d="M17.41 3.6a10 10 0 1 0 3 3"></path></svg>',
},
// people: {
// src: document.querySelector('#img-indeed').src,
// },
};
/**
* See also data.originalCategories.
*/
export const categories = [
recent,
frequent,
// ...custom.map((item) => item.id),
'people',
'nature',
'foods',
'activity',
'places',
'objects',
'symbols',
'flags',
...customCharactersCategories,
];
const customRecentCategory = {
// TODO: How to override `node_modules/@emoji-mart/data/i18n/en.json` to define its label?
id: recent,
emojis: [],
};
export function ensureDataHasRecentCategoryPrepended(
data: EmojiMartData,
): EmojiMartData {
console.log({ data });
const customData: EmojiMartData = { ...data } as EmojiMartData;
if (!customData.categories.find((category) => category.id === recent)) {
const newCategories = [customRecentCategory, ...data.categories];
customData.categories = newCategories;
console.log({ customData });
}
return customData;
}
function getCustomData(
emojiMartDataInner: EmojiMartData,
): emojiMartData.EmojiMartData {
const withExtraCharacters = insertCustomCharacters(emojiMartDataInner);
return ensureDataHasRecentCategoryPrepended(withExtraCharacters);
}
export const customData = getCustomData(emojiMartData as EmojiMartData);
export function getTruncatedRecentEmojis(
emojiId: EmojiId,
recentEmojis: EmojiId[],
max: number,
): EmojiId[] {
const newRecentlyUsedEmojisSet = new Set([emojiId, ...recentEmojis]);
const newRecentlyUsedEmojisArray = Array.from(newRecentlyUsedEmojisSet);
newRecentlyUsedEmojisArray.length = Math.min(
newRecentlyUsedEmojisArray.length,
max,
);
console.log({ newRecentlyUsedEmojisArray });
return newRecentlyUsedEmojisArray;
}
export function overwriteDataWithRecentlyUsedEmoji(
previousData: EmojiMartData,
recentEmojis: EmojiId[],
): EmojiMartData {
console.log('overwriteDataWithRecentlyUsedEmoji', {
previousData,
recentEmojis,
});
const indexOfRecentlyUsedEmojis = previousData.categories.findIndex(
(item) => item.id === recent,
);
console.log('overwriteDataWithRecentlyUsedEmoji', {
indexOfRecentlyUsedEmojis,
});
const newData = { ...previousData };
newData.categories[indexOfRecentlyUsedEmojis].emojis = recentEmojis;
return newData;
} |
I love emoji-mart. Thanks for offering it. ❤️
I was wondering if there could be a way for me to add sections of "custom" emojis that are really just other utf8 characters and therefore do not have an image (and so I can't populate the "src" property, as https://github.com/missive/emoji-mart/tree/main?tab=readme-ov-file#custom-emojis seems to require).
For example, it would be great if I could create:
And emoji-mart would make them findable via the official utf8 names for each character.
E.g. 🡺 is called "wide-headed rightwards heavy barb arrow". https://graphemica.com/%F0%9F%A1%BA
So, 🡺 should appear in my emoji-mart results whenever I search "right arrow".
What do you think?
I would use this feature every day!
Thanks!
The text was updated successfully, but these errors were encountered: