claritykit-svelte
Version:
A comprehensive Svelte component library focused on accessibility, ADHD-optimized design, developer experience, and full SSR compatibility
162 lines (161 loc) • 8.27 kB
JavaScript
import { Extension } from '@tiptap/core';
import { PluginKey } from '@tiptap/pm/state';
import { Plugin } from '@tiptap/pm/state';
export const EmojiPickerExtension = Extension.create({
name: 'emojiPicker',
addOptions() {
return {
categories: defaultEmojiCategories,
customEmojis: [],
recentEmojis: [],
maxRecent: 20,
showCategories: true,
showSearch: true,
showSkinTones: true,
onEmojiSelect: (emoji) => {
console.log('Emoji selected:', emoji);
},
};
},
addCommands() {
return {
insertEmoji: (emoji) => ({ commands }) => {
return commands.insertContent(emoji);
},
insertCustomEmoji: (customEmoji) => ({ commands }) => {
const emojiHtml = customEmoji.animated
? `<img src="${customEmoji.url}" alt="${customEmoji.name}" class="custom-emoji animated" data-emoji-id="${customEmoji.id}" />`
: `<img src="${customEmoji.url}" alt="${customEmoji.name}" class="custom-emoji" data-emoji-id="${customEmoji.id}" />`;
return commands.insertContent(emojiHtml);
},
openEmojiPicker: () => ({ editor }) => {
// This would trigger the emoji picker UI
console.log('Opening emoji picker');
return true;
},
};
},
addKeyboardShortcuts() {
return {
'Mod-Shift-e': () => this.editor.commands.openEmojiPicker(),
};
},
addProseMirrorPlugins() {
return [
new Plugin({
key: new PluginKey('emojiPicker'),
props: {
handleKeyDown: (view, event) => {
// Handle emoji shortcuts like :smile: -> 😀
if (event.key === ':') {
const { state } = view;
const { selection } = state;
const { $from } = selection;
// Look for emoji shortcode pattern
const textBefore = $from.parent.textBetween(Math.max(0, $from.parentOffset - 20), $from.parentOffset);
const emojiMatch = textBefore.match(/:([a-z_]+)$/);
if (emojiMatch) {
const shortcode = emojiMatch[1];
const emoji = findEmojiByShortcode(shortcode);
if (emoji) {
const tr = state.tr;
const start = $from.pos - emojiMatch[0].length;
const end = $from.pos;
tr.replaceWith(start, end, state.schema.text(emoji.emoji));
view.dispatch(tr);
// Add to recent emojis
this.options.onEmojiSelect(emoji);
return true;
}
}
}
return false;
},
},
}),
];
},
});
// Default emoji categories with common emojis
export const defaultEmojiCategories = [
{
id: 'recent',
name: 'Recently Used',
icon: '🕒',
emojis: [], // This would be populated dynamically
},
{
id: 'smileys',
name: 'Smileys & Emotion',
icon: '😀',
emojis: [
{ id: 'grinning', emoji: '😀', name: 'Grinning Face', keywords: ['happy', 'smile', 'grin'], category: 'smileys' },
{ id: 'grinning_eyes', emoji: '😁', name: 'Beaming Face', keywords: ['happy', 'smile', 'joy'], category: 'smileys' },
{ id: 'joy', emoji: '😂', name: 'Face with Tears of Joy', keywords: ['laugh', 'happy', 'cry'], category: 'smileys' },
{ id: 'rofl', emoji: '🤣', name: 'Rolling on Floor Laughing', keywords: ['laugh', 'lol', 'funny'], category: 'smileys' },
{ id: 'smile', emoji: '😊', name: 'Smiling Face', keywords: ['happy', 'smile', 'blush'], category: 'smileys' },
{ id: 'wink', emoji: '😉', name: 'Winking Face', keywords: ['wink', 'flirt', 'playful'], category: 'smileys' },
{ id: 'heart_eyes', emoji: '😍', name: 'Heart Eyes', keywords: ['love', 'heart', 'adore'], category: 'smileys' },
{ id: 'thinking', emoji: '🤔', name: 'Thinking Face', keywords: ['think', 'consider', 'hmm'], category: 'smileys' },
{ id: 'thumbsup', emoji: '👍', name: 'Thumbs Up', keywords: ['good', 'yes', 'approve'], category: 'smileys' },
{ id: 'thumbsdown', emoji: '👎', name: 'Thumbs Down', keywords: ['bad', 'no', 'disapprove'], category: 'smileys' },
],
},
{
id: 'people',
name: 'People & Body',
icon: '👋',
emojis: [
{ id: 'wave', emoji: '👋', name: 'Waving Hand', keywords: ['hello', 'hi', 'goodbye'], category: 'people' },
{ id: 'clap', emoji: '👏', name: 'Clapping Hands', keywords: ['applause', 'congratulations'], category: 'people' },
{ id: 'pray', emoji: '🙏', name: 'Folded Hands', keywords: ['pray', 'thanks', 'please'], category: 'people' },
{ id: 'muscle', emoji: '💪', name: 'Flexed Biceps', keywords: ['strong', 'strength', 'power'], category: 'people' },
{ id: 'point_right', emoji: '👉', name: 'Pointing Right', keywords: ['point', 'direction', 'right'], category: 'people' },
{ id: 'point_left', emoji: '👈', name: 'Pointing Left', keywords: ['point', 'direction', 'left'], category: 'people' },
{ id: 'point_up', emoji: '☝️', name: 'Pointing Up', keywords: ['point', 'direction', 'up'], category: 'people' },
{ id: 'point_down', emoji: '👇', name: 'Pointing Down', keywords: ['point', 'direction', 'down'], category: 'people' },
],
},
{
id: 'objects',
name: 'Objects',
icon: '⚽',
emojis: [
{ id: 'fire', emoji: '🔥', name: 'Fire', keywords: ['hot', 'flame', 'burn'], category: 'objects' },
{ id: 'star', emoji: '⭐', name: 'Star', keywords: ['star', 'favorite', 'good'], category: 'objects' },
{ id: 'heart', emoji: '❤️', name: 'Red Heart', keywords: ['love', 'heart', 'romance'], category: 'objects' },
{ id: 'broken_heart', emoji: '💔', name: 'Broken Heart', keywords: ['sad', 'heartbreak', 'love'], category: 'objects' },
{ id: 'check', emoji: '✅', name: 'Check Mark', keywords: ['done', 'complete', 'yes'], category: 'objects' },
{ id: 'x', emoji: '❌', name: 'Cross Mark', keywords: ['no', 'wrong', 'error'], category: 'objects' },
{ id: 'warning', emoji: '⚠️', name: 'Warning', keywords: ['warning', 'caution', 'alert'], category: 'objects' },
{ id: 'question', emoji: '❓', name: 'Question Mark', keywords: ['question', 'help', 'confused'], category: 'objects' },
],
},
];
// Helper function to find emoji by shortcode
function findEmojiByShortcode(shortcode) {
for (const category of defaultEmojiCategories) {
const emoji = category.emojis.find(e => e.id === shortcode ||
e.keywords.includes(shortcode) ||
e.name.toLowerCase().replace(/\s+/g, '_') === shortcode);
if (emoji)
return emoji;
}
return null;
}
// Helper function to search emojis
export function searchEmojis(query, categories) {
const searchTerm = query.toLowerCase();
const results = [];
for (const category of categories) {
for (const emoji of category.emojis) {
if (emoji.name.toLowerCase().includes(searchTerm) ||
emoji.keywords.some(keyword => keyword.toLowerCase().includes(searchTerm)) ||
emoji.id.includes(searchTerm)) {
results.push(emoji);
}
}
}
return results.slice(0, 50); // Limit results
}
export default EmojiPickerExtension;