Add emoji category as menu in desktop, shown as offcanvas in mobile
This commit is contained in:
205
script.js
205
script.js
@@ -3,8 +3,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const emojiGrid = document.getElementById('emoji-grid');
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const darkModeToggle = document.getElementById('dark-mode-toggle');
|
||||
const darkModeToggleDesktop = document.getElementById('dark-mode-toggle-desktop');
|
||||
const lightIcon = document.getElementById('theme-toggle-light-icon');
|
||||
const darkIcon = document.getElementById('theme-toggle-dark-icon');
|
||||
const lightIconDesktop = document.getElementById('theme-toggle-light-icon-desktop');
|
||||
const darkIconDesktop = document.getElementById('theme-toggle-dark-icon-desktop');
|
||||
const modal = document.getElementById('emoji-modal');
|
||||
const modalContent = document.getElementById('modal-content');
|
||||
const modalCloseBtn = document.getElementById('modal-close-btn');
|
||||
@@ -14,6 +17,15 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const modalKeywords = document.getElementById('modal-keywords');
|
||||
const modalCopyBtn = document.getElementById('modal-copy-btn');
|
||||
const loadMoreBtn = document.getElementById('load-more-btn');
|
||||
const categoryButtons = document.querySelectorAll('.category-btn');
|
||||
const currentCategoryTitle = document.getElementById('current-category-title');
|
||||
const currentCategoryCount = document.getElementById('current-category-count');
|
||||
const offcanvasToggle = document.getElementById('offcanvas-toggle');
|
||||
const offcanvasOverlay = document.getElementById('offcanvas-overlay');
|
||||
const offcanvasClose = document.getElementById('offcanvas-close');
|
||||
const offcanvasBackdrop = document.getElementById('offcanvas-backdrop');
|
||||
const offcanvasSidebar = document.getElementById('offcanvas-sidebar');
|
||||
const offcanvasNav = document.getElementById('offcanvas-nav');
|
||||
|
||||
// --- State ---
|
||||
let allEmojis = [];
|
||||
@@ -21,26 +33,33 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
let currentPage = 1;
|
||||
const EMOJIS_PER_PAGE = 100;
|
||||
let indonesianKeywords = {};
|
||||
let currentCategory = 'all';
|
||||
let categorizedEmojis = {};
|
||||
|
||||
// --- Dark Mode Logic ---
|
||||
const applyTheme = (isDark) => {
|
||||
document.documentElement.classList.toggle('dark', isDark);
|
||||
// Mobile icons
|
||||
lightIcon.classList.toggle('hidden', !isDark);
|
||||
darkIcon.classList.toggle('hidden', isDark);
|
||||
// Desktop icons
|
||||
lightIconDesktop.classList.toggle('hidden', !isDark);
|
||||
darkIconDesktop.classList.toggle('hidden', isDark);
|
||||
};
|
||||
|
||||
darkModeToggle.addEventListener('click', () => {
|
||||
const toggleDarkMode = () => {
|
||||
const isDark = !document.documentElement.classList.contains('dark');
|
||||
localStorage.setItem('darkMode', isDark);
|
||||
applyTheme(isDark);
|
||||
});
|
||||
};
|
||||
|
||||
darkModeToggle.addEventListener('click', toggleDarkMode);
|
||||
darkModeToggleDesktop.addEventListener('click', toggleDarkMode);
|
||||
|
||||
// Set initial icon state on load
|
||||
const initialIsDark = document.documentElement.classList.contains('dark');
|
||||
applyTheme(initialIsDark);
|
||||
|
||||
|
||||
|
||||
// --- Data Fetching & Processing ---
|
||||
const jsonFiles = [
|
||||
'array.json',
|
||||
@@ -92,7 +111,24 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
});
|
||||
allEmojis = Array.from(uniqueEmojis.values());
|
||||
|
||||
// Organize emojis by category
|
||||
categorizedEmojis = {
|
||||
'all': allEmojis,
|
||||
'Smileys & Emotion': allEmojis.filter(e => e.category === 'Smileys & Emotion'),
|
||||
'People & Body': allEmojis.filter(e => e.category === 'People & Body'),
|
||||
'Animals & Nature': allEmojis.filter(e => e.category === 'Animals & Nature'),
|
||||
'Food & Drink': allEmojis.filter(e => e.category === 'Food & Drink'),
|
||||
'Travel & Places': allEmojis.filter(e => e.category === 'Travel & Places'),
|
||||
'Activities': allEmojis.filter(e => e.category === 'Activities'),
|
||||
'Objects': allEmojis.filter(e => e.category === 'Objects'),
|
||||
'Symbols': allEmojis.filter(e => e.category === 'Symbols'),
|
||||
'Flags': allEmojis.filter(e => e.category === 'Flags')
|
||||
};
|
||||
|
||||
currentEmojiList = allEmojis;
|
||||
initializeCategoryMenu();
|
||||
updateCategoryDisplay();
|
||||
displayPage(1);
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -143,21 +179,145 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
displayPage(currentPage + 1);
|
||||
});
|
||||
|
||||
// --- Category Logic ---
|
||||
function initializeCategoryMenu() {
|
||||
// Desktop category buttons
|
||||
categoryButtons.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const category = btn.dataset.category;
|
||||
setActiveCategory(category);
|
||||
});
|
||||
});
|
||||
|
||||
// Offcanvas category menu
|
||||
const categories = [
|
||||
{ key: 'all', name: 'All Emojis', icon: '🌟' },
|
||||
{ key: 'Smileys & Emotion', name: 'Smileys & Emotion', icon: '😀' },
|
||||
{ key: 'People & Body', name: 'People & Body', icon: '👋' },
|
||||
{ key: 'Animals & Nature', name: 'Animals & Nature', icon: '🐶' },
|
||||
{ key: 'Food & Drink', name: 'Food & Drink', icon: '🍎' },
|
||||
{ key: 'Travel & Places', name: 'Travel & Places', icon: '🌍' },
|
||||
{ key: 'Activities', name: 'Activities', icon: '⚽' },
|
||||
{ key: 'Objects', name: 'Objects', icon: '💡' },
|
||||
{ key: 'Symbols', name: 'Symbols', icon: '❤️' },
|
||||
{ key: 'Flags', name: 'Flags', icon: '🏳️' }
|
||||
];
|
||||
|
||||
offcanvasNav.innerHTML = categories.map(cat => `
|
||||
<button class="offcanvas-category-btn w-full text-left px-3 py-2 rounded-lg text-sm font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors" data-category="${cat.key}">
|
||||
<span class="flex items-center">
|
||||
<span class="mr-2">${cat.icon}</span>
|
||||
${cat.name}
|
||||
</span>
|
||||
</button>
|
||||
`).join('');
|
||||
|
||||
// Offcanvas category button events
|
||||
document.querySelectorAll('.offcanvas-category-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const category = btn.dataset.category;
|
||||
setActiveCategory(category);
|
||||
closeOffcanvas();
|
||||
});
|
||||
});
|
||||
|
||||
// Offcanvas menu controls
|
||||
offcanvasToggle.addEventListener('click', openOffcanvas);
|
||||
offcanvasClose.addEventListener('click', closeOffcanvas);
|
||||
offcanvasBackdrop.addEventListener('click', closeOffcanvas);
|
||||
}
|
||||
|
||||
function setActiveCategory(category) {
|
||||
currentCategory = category;
|
||||
|
||||
// Update active state for desktop buttons
|
||||
categoryButtons.forEach(btn => {
|
||||
btn.classList.toggle('active', btn.dataset.category === category);
|
||||
if (btn.dataset.category === category) {
|
||||
btn.classList.add('bg-blue-100', 'dark:bg-blue-900', 'text-blue-700', 'dark:text-blue-300');
|
||||
} else {
|
||||
btn.classList.remove('bg-blue-100', 'dark:bg-blue-900', 'text-blue-700', 'dark:text-blue-300');
|
||||
}
|
||||
});
|
||||
|
||||
// Update active state for offcanvas buttons
|
||||
document.querySelectorAll('.offcanvas-category-btn').forEach(btn => {
|
||||
if (btn.dataset.category === category) {
|
||||
btn.classList.add('bg-blue-100', 'dark:bg-blue-900', 'text-blue-700', 'dark:text-blue-300');
|
||||
} else {
|
||||
btn.classList.remove('bg-blue-100', 'dark:bg-blue-900', 'text-blue-700', 'dark:text-blue-300');
|
||||
}
|
||||
});
|
||||
|
||||
// Filter emojis by category
|
||||
const baseEmojis = categorizedEmojis[category] || [];
|
||||
|
||||
// Apply search filter if there's a search term
|
||||
const searchTerm = searchInput.value.toLowerCase().trim();
|
||||
if (searchTerm) {
|
||||
currentEmojiList = baseEmojis.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);
|
||||
|
||||
// Indonesian keywords search
|
||||
const indonesianMatch = emoji.indonesianKeywords && Array.isArray(emoji.indonesianKeywords) &&
|
||||
emoji.indonesianKeywords.some(k => k.toLowerCase().includes(searchTerm));
|
||||
|
||||
return nameMatch || keywordMatch || categoryMatch || subcategoryMatch || indonesianMatch;
|
||||
});
|
||||
} else {
|
||||
currentEmojiList = baseEmojis;
|
||||
}
|
||||
|
||||
updateCategoryDisplay();
|
||||
displayPage(1);
|
||||
}
|
||||
|
||||
function updateCategoryDisplay() {
|
||||
const categoryName = currentCategory === 'all' ? 'All Emojis' : currentCategory;
|
||||
currentCategoryTitle.textContent = categoryName;
|
||||
currentCategoryCount.textContent = `${currentEmojiList.length} emojis`;
|
||||
}
|
||||
|
||||
function openOffcanvas() {
|
||||
offcanvasOverlay.classList.remove('hidden');
|
||||
setTimeout(() => {
|
||||
offcanvasSidebar.classList.remove('-translate-x-full');
|
||||
}, 10);
|
||||
}
|
||||
|
||||
function closeOffcanvas() {
|
||||
offcanvasSidebar.classList.add('-translate-x-full');
|
||||
setTimeout(() => {
|
||||
offcanvasOverlay.classList.add('hidden');
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// --- 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);
|
||||
|
||||
// Indonesian keywords search
|
||||
const indonesianMatch = emoji.indonesianKeywords && Array.isArray(emoji.indonesianKeywords) &&
|
||||
emoji.indonesianKeywords.some(k => k.toLowerCase().includes(searchTerm));
|
||||
|
||||
return nameMatch || keywordMatch || categoryMatch || subcategoryMatch || indonesianMatch;
|
||||
});
|
||||
const baseEmojis = categorizedEmojis[currentCategory] || [];
|
||||
|
||||
if (searchTerm) {
|
||||
currentEmojiList = baseEmojis.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);
|
||||
|
||||
// Indonesian keywords search
|
||||
const indonesianMatch = emoji.indonesianKeywords && Array.isArray(emoji.indonesianKeywords) &&
|
||||
emoji.indonesianKeywords.some(k => k.toLowerCase().includes(searchTerm));
|
||||
|
||||
return nameMatch || keywordMatch || categoryMatch || subcategoryMatch || indonesianMatch;
|
||||
});
|
||||
} else {
|
||||
currentEmojiList = baseEmojis;
|
||||
}
|
||||
|
||||
updateCategoryDisplay();
|
||||
displayPage(1);
|
||||
});
|
||||
|
||||
@@ -233,4 +393,17 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
setTimeout(() => { modalCopyBtn.textContent = originalText; }, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize footer with dynamic year
|
||||
function initFooter() {
|
||||
const yearElement = document.getElementById('current-year');
|
||||
if (yearElement) {
|
||||
const currentYear = new Date().getFullYear();
|
||||
yearElement.textContent = currentYear;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize footer
|
||||
initFooter();
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user