mirror of
https://github.com/ld3z/new-manga.git
synced 2026-11-09 19:53:45 -07:00
add genre filter
This commit is contained in:
parent
cd52b53cf2
commit
c81866e53b
3 changed files with 182 additions and 1 deletions
|
|
@ -17,7 +17,10 @@ const coverUrl = comic.md_comics.md_covers[0]
|
|||
const isNSFW = comic.md_comics.content_rating === 'erotica';
|
||||
---
|
||||
|
||||
<div class="bg-secondary rounded-xl overflow-hidden shadow-lg h-full flex flex-col max-w-[280px] mx-auto border border-hover/30 hover:border-accent/50 transition-all duration-300">
|
||||
<div
|
||||
class="bg-secondary rounded-xl overflow-hidden shadow-lg h-full flex flex-col max-w-[280px] mx-auto border border-hover/30 hover:border-accent/50 transition-all duration-300"
|
||||
data-genres={JSON.stringify(comic.md_comics.genres)}
|
||||
>
|
||||
<a href={`https://comick.io/comic/${comic.md_comics.slug}`} class="flex flex-col h-full group relative">
|
||||
<div class="relative overflow-hidden aspect-[2/3] rounded-t-xl">
|
||||
{!isAvif ? (
|
||||
|
|
@ -220,4 +223,23 @@ const isNSFW = comic.md_comics.content_rating === 'erotica';
|
|||
setInterval(() => updateTimer(timer), 1000);
|
||||
});
|
||||
});
|
||||
|
||||
function filterComics() {
|
||||
const excludedGenres = new Set(JSON.parse(localStorage.getItem('excluded-genres') || '[]'));
|
||||
|
||||
document.querySelectorAll('[data-genres]').forEach(card => {
|
||||
const cardGenres = JSON.parse((card as HTMLElement).dataset.genres || '[]');
|
||||
const shouldHide = cardGenres.some((genreId: number) => excludedGenres.has(genreId));
|
||||
|
||||
card.classList.toggle('hidden', shouldHide);
|
||||
});
|
||||
}
|
||||
|
||||
// Listen for genre updates
|
||||
window.addEventListener('genresUpdated', () => {
|
||||
filterComics();
|
||||
});
|
||||
|
||||
// Initial filter
|
||||
filterComics();
|
||||
</script>
|
||||
157
src/components/GenreFilter.astro
Normal file
157
src/components/GenreFilter.astro
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
---
|
||||
import { fetchGenres } from '../lib/api';
|
||||
|
||||
const genres = await fetchGenres();
|
||||
---
|
||||
|
||||
<div class="relative inline-block">
|
||||
<button
|
||||
id="genre-filter-btn"
|
||||
class="bg-secondary text-primary px-4 py-2 rounded-lg hover:bg-hover transition-colors flex items-center gap-2"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-5 h-5"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path d="M3 6h18M6 12h12m-9 6h6"/>
|
||||
</svg>
|
||||
<span>Filter Genres</span>
|
||||
<span id="excluded-count" class="bg-accent/20 text-accent px-2 rounded-full text-sm hidden">
|
||||
0
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div
|
||||
id="genre-dropdown"
|
||||
class="absolute z-50 mt-2 w-64 bg-secondary rounded-lg shadow-lg border border-hover/30 hidden"
|
||||
>
|
||||
<div class="p-3">
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<h3 class="font-medium">Exclude Genres</h3>
|
||||
<button
|
||||
id="clear-filters"
|
||||
class="text-sm text-accent hover:text-accent-light transition-colors"
|
||||
>
|
||||
Clear All
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search genres..."
|
||||
class="w-full px-3 py-2 bg-primary rounded border border-hover/30 text-sm mb-2"
|
||||
id="genre-search"
|
||||
/>
|
||||
<div class="max-h-64 overflow-y-auto space-y-1" id="genre-list">
|
||||
{genres.map(genre => (
|
||||
<label class="flex items-center gap-2 px-2 py-1.5 hover:bg-hover/50 rounded cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
value={genre.id}
|
||||
class="genre-checkbox"
|
||||
/>
|
||||
<span class="text-sm">{genre.name}</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const button = document.getElementById('genre-filter-btn');
|
||||
const dropdown = document.getElementById('genre-dropdown');
|
||||
const genreList = document.getElementById('genre-list');
|
||||
const clearButton = document.getElementById('clear-filters');
|
||||
const searchInput = document.getElementById('genre-search') as HTMLInputElement;
|
||||
const excludedCount = document.getElementById('excluded-count');
|
||||
|
||||
// Load excluded genres from localStorage
|
||||
let excludedGenres = new Set(JSON.parse(localStorage.getItem('excluded-genres') || '[]'));
|
||||
|
||||
// Initialize checkboxes
|
||||
function initCheckboxes() {
|
||||
document.querySelectorAll('.genre-checkbox').forEach((checkbox: HTMLInputElement) => {
|
||||
checkbox.checked = excludedGenres.has(Number(checkbox.value));
|
||||
});
|
||||
updateExcludedCount();
|
||||
}
|
||||
|
||||
function updateExcludedCount() {
|
||||
if (excludedCount) {
|
||||
if (excludedGenres.size > 0) {
|
||||
excludedCount.textContent = excludedGenres.size.toString();
|
||||
excludedCount.classList.remove('hidden');
|
||||
} else {
|
||||
excludedCount.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle checkbox changes
|
||||
function handleCheckboxChange(e: Event) {
|
||||
const checkbox = e.target as HTMLInputElement;
|
||||
const genreId = Number(checkbox.value);
|
||||
|
||||
if (checkbox.checked) {
|
||||
excludedGenres.add(genreId);
|
||||
} else {
|
||||
excludedGenres.delete(genreId);
|
||||
}
|
||||
|
||||
localStorage.setItem('excluded-genres', JSON.stringify([...excludedGenres]));
|
||||
updateExcludedCount();
|
||||
|
||||
// Dispatch event for comic filtering
|
||||
window.dispatchEvent(new CustomEvent('genresUpdated', {
|
||||
detail: { excludedGenres: [...excludedGenres] }
|
||||
}));
|
||||
}
|
||||
|
||||
// Search functionality
|
||||
function handleSearch() {
|
||||
const query = searchInput.value.toLowerCase();
|
||||
const labels = genreList?.querySelectorAll('label');
|
||||
|
||||
labels?.forEach(label => {
|
||||
const text = label.textContent?.toLowerCase() || '';
|
||||
label.style.display = text.includes(query) ? '' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
// Event listeners
|
||||
button?.addEventListener('click', () => {
|
||||
dropdown?.classList.toggle('hidden');
|
||||
button.setAttribute('aria-expanded', dropdown?.classList.contains('hidden') ? 'false' : 'true');
|
||||
});
|
||||
|
||||
clearButton?.addEventListener('click', () => {
|
||||
excludedGenres.clear();
|
||||
localStorage.setItem('excluded-genres', '[]');
|
||||
initCheckboxes();
|
||||
window.dispatchEvent(new CustomEvent('genresUpdated', {
|
||||
detail: { excludedGenres: [] }
|
||||
}));
|
||||
});
|
||||
|
||||
document.querySelectorAll('.genre-checkbox').forEach(checkbox => {
|
||||
checkbox.addEventListener('change', handleCheckboxChange);
|
||||
});
|
||||
|
||||
searchInput?.addEventListener('input', handleSearch);
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!button?.contains(e.target as Node) && !dropdown?.contains(e.target as Node)) {
|
||||
dropdown?.classList.add('hidden');
|
||||
button?.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize
|
||||
initCheckboxes();
|
||||
</script>
|
||||
|
|
@ -5,6 +5,7 @@ import LoadingCard from '../../../components/LoadingCard.astro';
|
|||
import RSSLink from '../../../components/RSSLink.astro';
|
||||
import TypeFilter from '../../../components/TypeFilter.astro';
|
||||
import LanguageSelector from '../../../components/LanguageSelector.astro';
|
||||
import GenreFilter from '../../../components/GenreFilter.astro';
|
||||
import { fetchComics, fetchGenres, isValidLanguage, isValidContentType, isValidComicType, getLanguageName } from '../../../lib/api';
|
||||
import type { Comic, Genre } from '../../../lib/types';
|
||||
|
||||
|
|
@ -39,6 +40,7 @@ const title = `ComicK - ${getLanguageName(lang!)} ${contentType?.toUpperCase()}
|
|||
<div class="flex flex-col sm:flex-row gap-4">
|
||||
<div class="flex items-center gap-4">
|
||||
<LanguageSelector currentLang={lang!} currentType={type!} contentType={contentType!} />
|
||||
<GenreFilter />
|
||||
<div class="flex gap-2">
|
||||
<a
|
||||
href={`/${lang}/sfw/${type}`}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue