document.addEventListener('DOMContentLoaded', () => { // --- DOM Elements --- const emojiGrid = document.getElementById('emoji-grid'); const searchInput = document.getElementById('search-input'); const darkModeToggle = document.getElementById('dark-mode-toggle'); const lightIcon = document.getElementById('theme-toggle-light-icon'); const darkIcon = document.getElementById('theme-toggle-dark-icon'); const modal = document.getElementById('emoji-modal'); const modalContent = document.getElementById('modal-content'); const modalCloseBtn = document.getElementById('modal-close-btn'); const modalEmoji = document.getElementById('modal-emoji'); const modalName = document.getElementById('modal-name'); const modalCategory = document.getElementById('modal-category'); const modalKeywords = document.getElementById('modal-keywords'); const modalCopyBtn = document.getElementById('modal-copy-btn'); const loadMoreBtn = document.getElementById('load-more-btn'); // --- State --- let allEmojis = []; let currentEmojiList = []; let currentPage = 1; const EMOJIS_PER_PAGE = 100; // --- Dark Mode Logic --- const applyTheme = (isDark) => { document.documentElement.classList.toggle('dark', isDark); lightIcon.classList.toggle('hidden', !isDark); darkIcon.classList.toggle('hidden', isDark); }; darkModeToggle.addEventListener('click', () => { const isDark = !document.documentElement.classList.contains('dark'); localStorage.setItem('darkMode', isDark); applyTheme(isDark); }); // Set initial icon state on load const initialIsDark = document.documentElement.classList.contains('dark'); applyTheme(initialIsDark); // --- Data Fetching & Processing --- const jsonFiles = [ 'array.json', 'list.json', 'categories.json', // Add other files if they should be loaded, keeping the initial set small ].map(file => `src/${file}`); function parseEmojiData(data) { let results = []; if (Array.isArray(data)) { data.forEach(item => results.push(...parseEmojiData(item))); } else if (typeof data === 'object' && data !== null) { if (data.hasOwnProperty('emoji') && data.hasOwnProperty('name')) { results.push(data); } Object.values(data).forEach(value => results.push(...parseEmojiData(value))); } return results; } Promise.all(jsonFiles.map(file => fetch(file) .then(response => { if (!response.ok) throw new Error(`Failed to load ${file}`); return response.json(); }) .catch(error => { console.error(error); return []; }) )) .then(allData => { const uniqueEmojis = new Map(); const parsedEmojis = allData.flat().flatMap(parseEmojiData); parsedEmojis.forEach(emoji => { if (emoji && emoji.name && !uniqueEmojis.has(emoji.name)) { uniqueEmojis.set(emoji.name, emoji); } }); allEmojis = Array.from(uniqueEmojis.values()); currentEmojiList = allEmojis; displayPage(1); }) .catch(error => { console.error('Error processing emoji data:', error); emojiGrid.innerHTML = '

Failed to load emoji data.

'; }); // --- UI Rendering --- function updateLoadMoreButton() { const end = currentPage * EMOJIS_PER_PAGE; if (currentEmojiList.length > end) { loadMoreBtn.classList.remove('hidden'); } else { loadMoreBtn.classList.add('hidden'); } } function displayPage(page) { currentPage = page; const start = (page - 1) * EMOJIS_PER_PAGE; const end = start + EMOJIS_PER_PAGE; const emojisToDisplay = currentEmojiList.slice(start, end); if (page === 1) { emojiGrid.innerHTML = ''; // Clear the grid only for the first page } emojisToDisplay.forEach(emoji => { const emojiCard = document.createElement('div'); emojiCard.className = 'flex flex-col items-center justify-center p-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm cursor-pointer transition-transform duration-200 hover:scale-105 hover:shadow-md'; emojiCard.innerHTML = `
${emoji.emoji}
${emoji.name}
`; emojiCard.addEventListener('click', () => openModal(emoji)); emojiGrid.appendChild(emojiCard); }); // Show 'No emojis found' only if the grid is still empty after rendering if (emojiGrid.innerHTML === '') { emojiGrid.innerHTML = '

No emojis found.

'; } updateLoadMoreButton(); } loadMoreBtn.addEventListener('click', () => { displayPage(currentPage + 1); }); // --- Search Logic --- searchInput.addEventListener('input', (e) => { const searchTerm = e.target.value.toLowerCase().trim(); currentEmojiList = allEmojis.filter(emoji => { const nameMatch = emoji.name.toLowerCase().includes(searchTerm); const keywordMatch = emoji.keywords && Array.isArray(emoji.keywords) && emoji.keywords.some(k => k.toLowerCase().includes(searchTerm)); const categoryMatch = emoji.category && emoji.category.toLowerCase().includes(searchTerm); const subcategoryMatch = emoji.subcategory && emoji.subcategory.toLowerCase().includes(searchTerm); return nameMatch || keywordMatch || categoryMatch || subcategoryMatch; }); displayPage(1); }); // --- Modal Logic --- function openModal(emoji) { modalEmoji.textContent = emoji.emoji; modalName.textContent = emoji.name; modalCategory.textContent = [emoji.category, emoji.subcategory].filter(Boolean).join(' / '); modalKeywords.innerHTML = ''; const tags = new Set(); if (emoji.keywords && Array.isArray(emoji.keywords)) { emoji.keywords.forEach(k => tags.add(k)); } if (emoji.category) tags.add(emoji.category); if (emoji.subcategory) tags.add(emoji.subcategory); const tagArray = Array.from(tags); if (tagArray.length > 0) { tagArray.forEach(tagText => { const tag = document.createElement('button'); tag.className = 'px-2 py-1 bg-gray-200 text-gray-700 dark:bg-gray-700 dark:text-gray-200 rounded-md text-sm hover:bg-gray-300 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500'; tag.textContent = tagText; tag.addEventListener('click', () => { searchInput.value = tagText; searchInput.dispatchEvent(new Event('input', { bubbles: true })); closeModal(); }); modalKeywords.appendChild(tag); }); } else { modalKeywords.innerHTML = '

No tags available.

'; } modalCopyBtn.onclick = () => copyToClipboard(emoji.emoji); modal.classList.remove('hidden'); modal.classList.add('flex'); setTimeout(() => { modalContent.classList.remove('scale-95', 'opacity-0'); }, 10); } function closeModal() { modalContent.classList.add('scale-95', 'opacity-0'); setTimeout(() => { modal.classList.add('hidden'); modal.classList.remove('flex'); }, 300); } modalCloseBtn.addEventListener('click', closeModal); modal.addEventListener('click', (e) => { if (e.target === modal) { closeModal(); } }); // --- Clipboard Logic --- function copyToClipboard(text) { navigator.clipboard.writeText(text).then(() => { const originalText = modalCopyBtn.textContent; modalCopyBtn.textContent = 'Copied!'; setTimeout(() => { modalCopyBtn.textContent = originalText; }, 2000); }).catch(err => { console.error('Failed to copy: ', err); const originalText = modalCopyBtn.textContent; modalCopyBtn.textContent = 'Failed!'; setTimeout(() => { modalCopyBtn.textContent = originalText; }, 2000); }); } });