From 75d75a9255ecf016c13db3f4e85ce481223f16a1 Mon Sep 17 00:00:00 2001 From: Vrezh Fedora Date: Sun, 15 Dec 2024 15:22:26 +0100 Subject: [PATCH] feat(modlist): added filtering and search bar for modlist --- src/components/ModsList.tsx | 161 ++++++++++++++++++++++++++++++++++++ src/mods.ts | 76 +++++++++++------ src/pages/mods/index.astro | 44 +++------- 3 files changed, 227 insertions(+), 54 deletions(-) create mode 100644 src/components/ModsList.tsx diff --git a/src/components/ModsList.tsx b/src/components/ModsList.tsx new file mode 100644 index 0000000..56b5761 --- /dev/null +++ b/src/components/ModsList.tsx @@ -0,0 +1,161 @@ +import React, { useState, useMemo } from 'react' +import type { ZenTheme } from '../mods' +import { library, icon } from '@fortawesome/fontawesome-svg-core' +import { faSort, faSortUp, faSortDown } from '@fortawesome/free-solid-svg-icons' + +// Add icons to the library +library.add(faSort, faSortUp, faSortDown) + +// Create icon objects +const defaultSortIcon = icon({ prefix: 'fas', iconName: 'sort' }) +const ascSortIcon = icon({ prefix: 'fas', iconName: 'sort-up' }) +const descSortIcon = icon({ prefix: 'fas', iconName: 'sort-down' }) + +interface ModsListProps { + mods: ZenTheme[] +} + +export default function ModsList({ mods }: ModsListProps) { + const [search, setSearch] = useState('') + const [createdSort, setCreatedSort] = useState<'default' | 'asc' | 'desc'>( + 'default', + ) + const [updatedSort, setUpdatedSort] = useState<'default' | 'asc' | 'desc'>( + 'default', + ) + + const toggleCreatedSort = () => { + setCreatedSort((prev) => { + if (prev === 'default') return 'asc' + if (prev === 'asc') return 'desc' + return 'default' + }) + } + + const toggleUpdatedSort = () => { + setUpdatedSort((prev) => { + if (prev === 'default') return 'asc' + if (prev === 'asc') return 'desc' + return 'default' + }) + } + + function getSortIcon(state: 'default' | 'asc' | 'desc') { + if (state === 'asc') return ascSortIcon + if (state === 'desc') return descSortIcon + return defaultSortIcon + } + + const filteredAndSortedMods = useMemo(() => { + let filtered = [...mods] + + // Filter by search + const searchTerm = search.toLowerCase() + if (searchTerm) { + filtered = filtered.filter( + (mod) => + mod.name.toLowerCase().includes(searchTerm) || + mod.description.toLowerCase().includes(searchTerm) || + mod.author.toLowerCase().includes(searchTerm) || + (mod.tags?.some((tag) => tag.toLowerCase().includes(searchTerm)) ?? + false), + ) + } + + // Sort by createdAt if chosen + if (createdSort !== 'default') { + filtered.sort((a, b) => { + const diff = + new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime() + return createdSort === 'asc' ? diff : -diff + }) + } + + // Sort by updatedAt if chosen + if (updatedSort !== 'default') { + filtered.sort((a, b) => { + const diff = + new Date(a.updatedAt).getTime() - new Date(b.updatedAt).getTime() + return updatedSort === 'asc' ? diff : -diff + }) + } + + return filtered + }, [mods, search, createdSort, updatedSort]) + + return ( +
+
+
+ setSearch(e.target.value)} + /> +
+ +
+
+ +
+ +
+ +
+
+
+ +
+ {filteredAndSortedMods.map((mod) => ( + +
+ {mod.name} +
+
+

+ {mod.name}{' '} + + by @{mod.author} + +

+

{mod.description}

+
+
+ ))} +
+
+ ) +} diff --git a/src/mods.ts b/src/mods.ts index 4dec622..4cb95b6 100644 --- a/src/mods.ts +++ b/src/mods.ts @@ -1,35 +1,65 @@ export interface ZenTheme { - name: string; - description: string; - image: string; - downloadUrl: string; - id: string; - homepage?: string; - readme: string; - preferences?: string; - isColorTheme: boolean; - author: string; - version: string; - tags: string[]; - createdAt: Date; - updatedAt: Date; + name: string + description: string + image: string + downloadUrl: string + id: string + homepage?: string + readme: string + preferences?: string + isColorTheme: boolean + author: string + version: string + tags: string[] + createdAt: Date + updatedAt: Date } -const THEME_API = "https://zen-browser.github.io/theme-store/themes.json"; +const THEME_API = 'https://zen-browser.github.io/theme-store/themes.json' -export async function getAllMods(): Promise { +interface FilterOptions { + createdAt?: Date + updatedAt?: Date + search?: string +} + +export async function getAllMods(filters?: FilterOptions): Promise { try { - const res = await fetch(THEME_API); - const json = await res.json(); + const res = await fetch(THEME_API) + const json = await res.json() // convert dict to array - const mods = Object.keys(json).map((key) => json[key]); - return mods; + let mods: ZenTheme[] = Object.keys(json).map((key) => json[key]) + + if (filters) { + if (filters.createdAt) { + mods = mods.filter( + (mod) => new Date(mod.createdAt) >= filters.createdAt!, + ) + } + if (filters.updatedAt) { + mods = mods.filter( + (mod) => new Date(mod.updatedAt) >= filters.updatedAt!, + ) + } + if (filters.search) { + const searchLower = filters.search.toLowerCase() + mods = mods.filter( + (mod) => + mod.name.toLowerCase().includes(searchLower) || + mod.description.toLowerCase().includes(searchLower) || + mod.author.toLowerCase().includes(searchLower) || + mod.tags.some((tag) => tag.toLowerCase().includes(searchLower)), + ) + } + } + + return mods } catch (error) { - console.error(error); - return []; + console.error(error) + return [] } } export function getAuthorLink(author: string): string { - return `https://github.com/${author}`; + return `https://github.com/${author}` } diff --git a/src/pages/mods/index.astro b/src/pages/mods/index.astro index 63b160c..ab7476f 100644 --- a/src/pages/mods/index.astro +++ b/src/pages/mods/index.astro @@ -1,46 +1,28 @@ --- -import Description from '../../components/Description.astro'; -import Title from '../../components/Title.astro'; -import Layout from '../../layouts/Layout.astro'; -import { getAllMods, type ZenTheme } from '../../mods'; +import Description from '../../components/Description.astro' +import Title from '../../components/Title.astro' +import Layout from '../../layouts/Layout.astro' +import ModsList from '../../components/ModsList' +import { getAllMods } from '../../mods' -const mods = await getAllMods() || []; +const mods = (await getAllMods({})) || [] ---
-
-
+
+
Zen Mods - Zen Mods is a collection of themes and plugins for Zen Browser. You can - find a theme for every mood and a plugin for every need. + Zen Mods is a collection of themes and plugins for Zen Browser. You + can find a theme for every mood and a plugin for every need.
-
- + + +