mirror of
https://github.com/zen-browser/www.git
synced 2025-07-13 03:35:32 +02:00
184 lines
4.7 KiB
TypeScript
184 lines
4.7 KiB
TypeScript
import { useEffect, useState } from 'preact/hooks'
|
|
import type { ZenTheme } from '../mods'
|
|
|
|
type SortOrder = 'default' | 'asc' | 'desc'
|
|
|
|
interface ModsSearchState {
|
|
search: string
|
|
createdSort: SortOrder
|
|
updatedSort: SortOrder
|
|
page: number
|
|
limit: number
|
|
}
|
|
|
|
const DEFAULT_LIMIT = 12
|
|
|
|
export function useModsSearch(mods: ZenTheme[]) {
|
|
const [searchParams, setSearchParams] = useState<URLSearchParams>()
|
|
const [state, setState] = useState<ModsSearchState>({
|
|
search: '',
|
|
createdSort: 'desc',
|
|
updatedSort: 'default',
|
|
page: 1,
|
|
limit: DEFAULT_LIMIT,
|
|
})
|
|
|
|
// Initialize search params
|
|
useEffect(() => {
|
|
const params = new URLSearchParams(window.location.search)
|
|
setSearchParams(params)
|
|
setState({
|
|
search: params.get('q') || '',
|
|
createdSort: (params.get('created') as SortOrder) || 'default',
|
|
updatedSort: (params.get('updated') as SortOrder) || 'default',
|
|
page: Number.parseInt(params.get('page') || '1', 10),
|
|
limit: Number.parseInt(params.get('limit') || String(DEFAULT_LIMIT), 10),
|
|
})
|
|
}, [])
|
|
|
|
// Update URL when state changes
|
|
useEffect(() => {
|
|
if (!searchParams) return
|
|
|
|
if (state.search) {
|
|
searchParams.set('q', state.search)
|
|
} else {
|
|
searchParams.delete('q')
|
|
}
|
|
|
|
if (state.createdSort !== 'default') {
|
|
searchParams.set('created', state.createdSort)
|
|
} else {
|
|
searchParams.delete('created')
|
|
}
|
|
|
|
if (state.updatedSort !== 'default') {
|
|
searchParams.set('updated', state.updatedSort)
|
|
} else {
|
|
searchParams.delete('updated')
|
|
}
|
|
|
|
if (state.page > 1) {
|
|
searchParams.set('page', state.page.toString())
|
|
} else {
|
|
searchParams.delete('page')
|
|
}
|
|
|
|
if (state.limit !== DEFAULT_LIMIT) {
|
|
searchParams.set('limit', state.limit.toString())
|
|
} else {
|
|
searchParams.delete('limit')
|
|
}
|
|
|
|
const newUrl = `${window.location.pathname}${
|
|
searchParams.toString() ? `?${searchParams.toString()}` : ''
|
|
}`
|
|
|
|
if (state.page > 1) {
|
|
window.history.pushState({}, '', newUrl)
|
|
} else {
|
|
window.history.replaceState({}, '', newUrl)
|
|
}
|
|
}, [state, searchParams])
|
|
|
|
const filteredMods = (() => {
|
|
let filtered = [...mods]
|
|
|
|
// Filter by search
|
|
const searchTerm = state.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 (state.createdSort !== 'default') {
|
|
filtered.sort((a, b) => {
|
|
const diff =
|
|
new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
|
|
return state.createdSort === 'asc' ? diff : -diff
|
|
})
|
|
}
|
|
|
|
// Sort by updatedAt if chosen
|
|
if (state.updatedSort !== 'default') {
|
|
filtered.sort((a, b) => {
|
|
const diff =
|
|
new Date(a.updatedAt).getTime() - new Date(b.updatedAt).getTime()
|
|
return state.updatedSort === 'asc' ? diff : -diff
|
|
})
|
|
}
|
|
|
|
return filtered
|
|
})()
|
|
|
|
// Calculate pagination
|
|
const totalPages = Math.ceil(filteredMods.length / state.limit)
|
|
const startIndex = (state.page - 1) * state.limit
|
|
const endIndex = startIndex + state.limit
|
|
const paginatedMods = filteredMods.slice(startIndex, endIndex)
|
|
|
|
const setSearch = (search: string) => {
|
|
setState((prev) => ({ ...prev, search, page: 1 })) // Reset page when search changes
|
|
}
|
|
|
|
const toggleCreatedSort = () => {
|
|
setState((prev) => ({
|
|
...prev,
|
|
createdSort:
|
|
prev.createdSort === 'default'
|
|
? 'asc'
|
|
: prev.createdSort === 'asc'
|
|
? 'desc'
|
|
: 'default',
|
|
page: 1, // Reset page when sort changes
|
|
}))
|
|
}
|
|
|
|
const toggleUpdatedSort = () => {
|
|
setState((prev) => ({
|
|
...prev,
|
|
updatedSort:
|
|
prev.updatedSort === 'default'
|
|
? 'asc'
|
|
: prev.updatedSort === 'asc'
|
|
? 'desc'
|
|
: 'default',
|
|
page: 1, // Reset page when sort changes
|
|
}))
|
|
}
|
|
|
|
const setPage = (page: number) => {
|
|
setState((prev) => ({
|
|
...prev,
|
|
page: Math.max(1, Math.min(page, totalPages)),
|
|
}))
|
|
}
|
|
|
|
const setLimit = (limit: number) => {
|
|
setState((prev) => ({ ...prev, limit, page: 1 })) // Reset page when limit changes
|
|
}
|
|
|
|
return {
|
|
search: state.search,
|
|
createdSort: state.createdSort,
|
|
updatedSort: state.updatedSort,
|
|
page: state.page,
|
|
limit: state.limit,
|
|
totalPages,
|
|
totalItems: filteredMods.length,
|
|
setSearch,
|
|
toggleCreatedSort,
|
|
toggleUpdatedSort,
|
|
setPage,
|
|
setLimit,
|
|
mods: paginatedMods,
|
|
searchParams,
|
|
}
|
|
}
|