Add emoji category as menu in desktop, shown as offcanvas in mobile
This commit is contained in:
142
index.html
142
index.html
@@ -30,35 +30,153 @@
|
|||||||
<body class="flex flex-col h-full bg-gray-50 text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<body class="flex flex-col h-full bg-gray-50 text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
||||||
|
|
||||||
<header class="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
|
<header class="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
|
||||||
<div class="max-w-4xl mx-auto py-8 px-4 sm:px-6 lg:px-8 text-center">
|
<div class="mx-auto py-8 px-4 sm:px-6 lg:px-8 text-center">
|
||||||
<div class="relative">
|
<!-- Mobile Header Layout -->
|
||||||
<h1 class="text-4xl font-bold tracking-tight">Emoji Glossary</h1>
|
<div class="flex items-center justify-between mb-4 lg:hidden">
|
||||||
<button id="dark-mode-toggle" class="absolute top-1/2 right-0 -translate-y-1/2 p-2 rounded-full text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none">
|
<button id="offcanvas-toggle" class="p-2 rounded-full text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none">
|
||||||
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<h1 class="text-4xl font-bold tracking-tight flex-1 text-center">Emoji Glossary</h1>
|
||||||
|
<button id="dark-mode-toggle" class="p-2 rounded-full text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none">
|
||||||
<svg id="theme-toggle-dark-icon" class="hidden w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path></svg>
|
<svg id="theme-toggle-dark-icon" class="hidden w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path></svg>
|
||||||
<svg id="theme-toggle-light-icon" class="hidden w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 5.05A1 1 0 003.636 6.464l.707.707a1 1 0 001.414-1.414l-.707-.707zM3 11a1 1 0 100-2H2a1 1 0 100 2h1zM13.536 14.95a1 1 0 011.414 0l.707.707a1 1 0 01-1.414 1.414l-.707-.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
|
<svg id="theme-toggle-light-icon" class="hidden w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 5.05A1 1 0 003.636 6.464l.707.707a1 1 0 001.414-1.414l-.707-.707zM3 11a1 1 0 100-2H2a1 1 0 100 2h1zM13.536 14.95a1 1 0 011.414 0l.707.707a1 1 0 01-1.414 1.414l-.707-.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Desktop Header Layout -->
|
||||||
|
<div class="hidden lg:block relative">
|
||||||
|
<h1 class="text-4xl font-bold tracking-tight">Emoji Glossary</h1>
|
||||||
|
<button id="dark-mode-toggle-desktop" class="absolute top-1/2 right-0 -translate-y-1/2 p-2 rounded-full text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none">
|
||||||
|
<svg id="theme-toggle-dark-icon-desktop" class="hidden w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path></svg>
|
||||||
|
<svg id="theme-toggle-light-icon-desktop" class="hidden w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 5.05A1 1 0 003.636 6.464l.707.707a1 1 0 001.414-1.414l-.707-.707zM3 11a1 1 0 100-2H2a1 1 0 100 2h1zM13.536 14.95a1 1 0 011.414 0l.707.707a1 1 0 01-1.414 1.414l-.707-.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mt-4 max-w-md mx-auto">
|
<div class="mt-4 max-w-md mx-auto">
|
||||||
<input type="text" id="search-input" placeholder="Search for an emoji..." class="w-full px-4 py-2 border border-gray-300 rounded-full shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
<input type="text" id="search-input" placeholder="Search for an emoji..." class="w-full px-4 py-2 border border-gray-300 rounded-full shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main class="flex-grow w-full max-w-6xl mx-auto p-4 sm:p-6 lg:p-8">
|
<main class="flex-grow flex">
|
||||||
<div id="emoji-grid" class="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 lg:grid-cols-8 xl:grid-cols-10 gap-4">
|
<!-- Category Sidebar -->
|
||||||
<!-- Emojis will be loaded here -->
|
<aside class="w-64 bg-white dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 p-4 hidden lg:block">
|
||||||
|
<div class="sticky top-4">
|
||||||
|
<h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">Categories</h2>
|
||||||
|
<nav id="category-nav" class="space-y-2">
|
||||||
|
<button class="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 active" data-category="all">
|
||||||
|
<span class="flex items-center">
|
||||||
|
<span class="mr-2">🌟</span>
|
||||||
|
All Emojis
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button class="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="Smileys & Emotion">
|
||||||
|
<span class="flex items-center">
|
||||||
|
<span class="mr-2">😀</span>
|
||||||
|
Smileys & Emotion
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button class="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="People & Body">
|
||||||
|
<span class="flex items-center">
|
||||||
|
<span class="mr-2">👋</span>
|
||||||
|
People & Body
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button class="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="Animals & Nature">
|
||||||
|
<span class="flex items-center">
|
||||||
|
<span class="mr-2">🐶</span>
|
||||||
|
Animals & Nature
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button class="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="Food & Drink">
|
||||||
|
<span class="flex items-center">
|
||||||
|
<span class="mr-2">🍎</span>
|
||||||
|
Food & Drink
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button class="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="Travel & Places">
|
||||||
|
<span class="flex items-center">
|
||||||
|
<span class="mr-2">🌍</span>
|
||||||
|
Travel & Places
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button class="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="Activities">
|
||||||
|
<span class="flex items-center">
|
||||||
|
<span class="mr-2">⚽</span>
|
||||||
|
Activities
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button class="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="Objects">
|
||||||
|
<span class="flex items-center">
|
||||||
|
<span class="mr-2">💡</span>
|
||||||
|
Objects
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button class="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="Symbols">
|
||||||
|
<span class="flex items-center">
|
||||||
|
<span class="mr-2">❤️</span>
|
||||||
|
Symbols
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button class="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="Flags">
|
||||||
|
<span class="flex items-center">
|
||||||
|
<span class="mr-2">🏳️</span>
|
||||||
|
Flags
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- Mobile Offcanvas Overlay -->
|
||||||
|
<div id="offcanvas-overlay" class="lg:hidden fixed inset-0 z-50 hidden">
|
||||||
|
<div class="absolute inset-0 bg-black bg-opacity-50" id="offcanvas-backdrop"></div>
|
||||||
|
<!-- Offcanvas Sidebar -->
|
||||||
|
<div id="offcanvas-sidebar" class="absolute left-0 top-0 h-full w-64 bg-white dark:bg-gray-800 transform -translate-x-full transition-transform duration-300 ease-in-out">
|
||||||
|
<div class="p-4 border-b border-gray-200 dark:border-gray-700">
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100">Categories</h2>
|
||||||
|
<button id="offcanvas-close" class="p-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||||
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="p-4 overflow-y-auto h-full pb-20">
|
||||||
|
<nav id="offcanvas-nav" class="space-y-2">
|
||||||
|
<!-- Offcanvas category buttons will be populated by JavaScript -->
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="load-more-container" class="text-center mt-8">
|
<!-- Main Content Area -->
|
||||||
<button id="load-more-btn" class="hidden px-6 py-2 bg-blue-500 text-white font-semibold rounded-full hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50 transition">
|
<div class="flex-1 p-4 sm:p-6 lg:p-8">
|
||||||
Load More
|
<div class="max-w-6xl mx-auto">
|
||||||
</button>
|
<div class="mb-6">
|
||||||
|
<h2 id="current-category-title" class="text-2xl font-bold text-gray-900 dark:text-gray-100">All Emojis</h2>
|
||||||
|
<p id="current-category-count" class="text-sm text-gray-500 dark:text-gray-400 mt-1"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="emoji-grid" class="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 lg:grid-cols-8 xl:grid-cols-10 gap-4">
|
||||||
|
<!-- Emojis will be loaded here -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="load-more-container" class="text-center mt-8">
|
||||||
|
<button id="load-more-btn" class="hidden px-6 py-2 bg-blue-500 text-white font-semibold rounded-full hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50 transition">
|
||||||
|
Load More
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer class="bg-white dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 py-6">
|
<footer class="bg-white dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 py-6">
|
||||||
<p class="text-center text-sm text-gray-500">
|
<p class="text-center text-sm text-gray-500">
|
||||||
© 2025 Dewe Toolsites - Emoji Glossary.
|
© <span id="current-year">2025</span> Dewe Toolsites - Emoji Glossary.
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<div id="emoji-modal" class="fixed inset-0 z-50 hidden items-center justify-center bg-black bg-opacity-50 transition-opacity duration-300">
|
<div id="emoji-modal" class="fixed inset-0 z-50 hidden items-center justify-center bg-black bg-opacity-50 transition-opacity duration-300">
|
||||||
|
|||||||
205
script.js
205
script.js
@@ -3,8 +3,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const emojiGrid = document.getElementById('emoji-grid');
|
const emojiGrid = document.getElementById('emoji-grid');
|
||||||
const searchInput = document.getElementById('search-input');
|
const searchInput = document.getElementById('search-input');
|
||||||
const darkModeToggle = document.getElementById('dark-mode-toggle');
|
const darkModeToggle = document.getElementById('dark-mode-toggle');
|
||||||
|
const darkModeToggleDesktop = document.getElementById('dark-mode-toggle-desktop');
|
||||||
const lightIcon = document.getElementById('theme-toggle-light-icon');
|
const lightIcon = document.getElementById('theme-toggle-light-icon');
|
||||||
const darkIcon = document.getElementById('theme-toggle-dark-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 modal = document.getElementById('emoji-modal');
|
||||||
const modalContent = document.getElementById('modal-content');
|
const modalContent = document.getElementById('modal-content');
|
||||||
const modalCloseBtn = document.getElementById('modal-close-btn');
|
const modalCloseBtn = document.getElementById('modal-close-btn');
|
||||||
@@ -14,6 +17,15 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const modalKeywords = document.getElementById('modal-keywords');
|
const modalKeywords = document.getElementById('modal-keywords');
|
||||||
const modalCopyBtn = document.getElementById('modal-copy-btn');
|
const modalCopyBtn = document.getElementById('modal-copy-btn');
|
||||||
const loadMoreBtn = document.getElementById('load-more-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 ---
|
// --- State ---
|
||||||
let allEmojis = [];
|
let allEmojis = [];
|
||||||
@@ -21,26 +33,33 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
let currentPage = 1;
|
let currentPage = 1;
|
||||||
const EMOJIS_PER_PAGE = 100;
|
const EMOJIS_PER_PAGE = 100;
|
||||||
let indonesianKeywords = {};
|
let indonesianKeywords = {};
|
||||||
|
let currentCategory = 'all';
|
||||||
|
let categorizedEmojis = {};
|
||||||
|
|
||||||
// --- Dark Mode Logic ---
|
// --- Dark Mode Logic ---
|
||||||
const applyTheme = (isDark) => {
|
const applyTheme = (isDark) => {
|
||||||
document.documentElement.classList.toggle('dark', isDark);
|
document.documentElement.classList.toggle('dark', isDark);
|
||||||
|
// Mobile icons
|
||||||
lightIcon.classList.toggle('hidden', !isDark);
|
lightIcon.classList.toggle('hidden', !isDark);
|
||||||
darkIcon.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');
|
const isDark = !document.documentElement.classList.contains('dark');
|
||||||
localStorage.setItem('darkMode', isDark);
|
localStorage.setItem('darkMode', isDark);
|
||||||
applyTheme(isDark);
|
applyTheme(isDark);
|
||||||
});
|
};
|
||||||
|
|
||||||
|
darkModeToggle.addEventListener('click', toggleDarkMode);
|
||||||
|
darkModeToggleDesktop.addEventListener('click', toggleDarkMode);
|
||||||
|
|
||||||
// Set initial icon state on load
|
// Set initial icon state on load
|
||||||
const initialIsDark = document.documentElement.classList.contains('dark');
|
const initialIsDark = document.documentElement.classList.contains('dark');
|
||||||
applyTheme(initialIsDark);
|
applyTheme(initialIsDark);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// --- Data Fetching & Processing ---
|
// --- Data Fetching & Processing ---
|
||||||
const jsonFiles = [
|
const jsonFiles = [
|
||||||
'array.json',
|
'array.json',
|
||||||
@@ -92,7 +111,24 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
allEmojis = Array.from(uniqueEmojis.values());
|
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;
|
currentEmojiList = allEmojis;
|
||||||
|
initializeCategoryMenu();
|
||||||
|
updateCategoryDisplay();
|
||||||
displayPage(1);
|
displayPage(1);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@@ -143,21 +179,145 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
displayPage(currentPage + 1);
|
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 ---
|
// --- Search Logic ---
|
||||||
searchInput.addEventListener('input', (e) => {
|
searchInput.addEventListener('input', (e) => {
|
||||||
const searchTerm = e.target.value.toLowerCase().trim();
|
const searchTerm = e.target.value.toLowerCase().trim();
|
||||||
currentEmojiList = allEmojis.filter(emoji => {
|
const baseEmojis = categorizedEmojis[currentCategory] || [];
|
||||||
const nameMatch = emoji.name.toLowerCase().includes(searchTerm);
|
|
||||||
const keywordMatch = emoji.keywords && Array.isArray(emoji.keywords) && emoji.keywords.some(k => k.toLowerCase().includes(searchTerm));
|
if (searchTerm) {
|
||||||
const categoryMatch = emoji.category && emoji.category.toLowerCase().includes(searchTerm);
|
currentEmojiList = baseEmojis.filter(emoji => {
|
||||||
const subcategoryMatch = emoji.subcategory && emoji.subcategory.toLowerCase().includes(searchTerm);
|
const nameMatch = emoji.name.toLowerCase().includes(searchTerm);
|
||||||
|
const keywordMatch = emoji.keywords && Array.isArray(emoji.keywords) && emoji.keywords.some(k => k.toLowerCase().includes(searchTerm));
|
||||||
// Indonesian keywords search
|
const categoryMatch = emoji.category && emoji.category.toLowerCase().includes(searchTerm);
|
||||||
const indonesianMatch = emoji.indonesianKeywords && Array.isArray(emoji.indonesianKeywords) &&
|
const subcategoryMatch = emoji.subcategory && emoji.subcategory.toLowerCase().includes(searchTerm);
|
||||||
emoji.indonesianKeywords.some(k => k.toLowerCase().includes(searchTerm));
|
|
||||||
|
// Indonesian keywords search
|
||||||
return nameMatch || keywordMatch || categoryMatch || subcategoryMatch || indonesianMatch;
|
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);
|
displayPage(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -233,4 +393,17 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
setTimeout(() => { modalCopyBtn.textContent = originalText; }, 2000);
|
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