UNPKG

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
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;