feat: poisson user page with fuzzy search
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
import { useComputed, useSignal, type ReadonlySignal } from '@preact/signals'
|
||||
import type { JSX } from 'preact/jsx-runtime'
|
||||
|
||||
export const ShowMore = <T extends any>({
|
||||
items,
|
||||
pageSize,
|
||||
children,
|
||||
}: {
|
||||
items: ReadonlySignal<T[]>
|
||||
pageSize: number
|
||||
children: (item: T) => JSX.Element
|
||||
}) => {
|
||||
const $shownItems = useSignal(pageSize)
|
||||
|
||||
const $paginatedItems = useComputed(() => {
|
||||
return items.value.slice(0, $shownItems.value)
|
||||
})
|
||||
|
||||
console.log($paginatedItems.value)
|
||||
|
||||
return (
|
||||
<>
|
||||
{$paginatedItems.value.map(children)}
|
||||
<div class="show-more">
|
||||
{$shownItems.value < items.value.length && (
|
||||
<button onClick={() => ($shownItems.value += pageSize)}>Mostra altri</button>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
+46
-17
@@ -1,5 +1,8 @@
|
||||
import { useSignal } from '@preact/signals'
|
||||
import { useComputed, useSignal } from '@preact/signals'
|
||||
import Fuse from 'fuse.js'
|
||||
import { useEffect } from 'preact/hooks'
|
||||
import { trottleDebounce } from './lib/util'
|
||||
import { ShowMore } from './Paginate'
|
||||
|
||||
type User = {
|
||||
uid: string
|
||||
@@ -20,37 +23,63 @@ function applyPatches(users: User[]) {
|
||||
}
|
||||
|
||||
export const UtentiPage = () => {
|
||||
const utentiSignal = useSignal<User[]>([])
|
||||
const $utentiData = useSignal<User[]>([])
|
||||
const $fuse = useSignal<Fuse<User> | null>(null)
|
||||
|
||||
const $searchText = useSignal('')
|
||||
const $searchResults = useComputed(() =>
|
||||
$searchText.value.trim().length > 0
|
||||
? ($fuse.value?.search($searchText.value).map(result => result.item) ?? [])
|
||||
: $utentiData.value,
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
fetch('https://poisson.phc.dm.unipi.it/users.json')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
applyPatches(data)
|
||||
utentiSignal.value = data
|
||||
|
||||
$utentiData.value = data
|
||||
$fuse.value = new Fuse(data, {
|
||||
keys: ['gecos'],
|
||||
})
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="search">
|
||||
<input type="text" placeholder="Cerca un utente Poisson..." />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Cerca un utente Poisson..."
|
||||
onInput={e => ($searchText.value = e.currentTarget.value)}
|
||||
value={$searchText.value}
|
||||
/>
|
||||
<span class="material-symbols-outlined">search</span>
|
||||
</div>
|
||||
<div class="search-results">
|
||||
{utentiSignal.value.map(user => (
|
||||
<div class="search-result">
|
||||
<div class="icon">
|
||||
<span class="material-symbols-outlined">person</span>
|
||||
</div>
|
||||
<div class="text">{user.gecos}</div>
|
||||
<div class="right">
|
||||
<a href={`https://poisson.phc.dm.unipi.it/~${user.uid}`}>
|
||||
<span class="material-symbols-outlined">globe</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{$searchResults.value ? (
|
||||
<ShowMore items={$searchResults} pageSize={10}>
|
||||
{poissonUser => (
|
||||
console.log(poissonUser),
|
||||
(
|
||||
<div class="search-result">
|
||||
<div class="icon">
|
||||
<span class="material-symbols-outlined">person</span>
|
||||
</div>
|
||||
<div class="text">{poissonUser.gecos}</div>
|
||||
<div class="right">
|
||||
<a href={`https://poisson.phc.dm.unipi.it/~${poissonUser.uid}`}>
|
||||
<span class="material-symbols-outlined">globe</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</ShowMore>
|
||||
) : (
|
||||
<>Nessun risultato</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
export const trottleDebounce = <T extends any[], R>(
|
||||
fn: (...args: T) => R,
|
||||
delay: number,
|
||||
options: { leading?: boolean; trailing?: boolean } = {},
|
||||
): ((...args: T) => R | undefined) => {
|
||||
let lastCall = 0
|
||||
let lastResult: R | undefined
|
||||
let lastArgs: T | undefined
|
||||
let timeout: NodeJS.Timeout | undefined
|
||||
|
||||
const leading = options.leading ?? true
|
||||
const trailing = options.trailing ?? true
|
||||
|
||||
return (...args: T): R | undefined => {
|
||||
lastArgs = args
|
||||
if (leading && Date.now() - lastCall >= delay) {
|
||||
lastCall = Date.now()
|
||||
lastResult = fn(...args)
|
||||
} else {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
timeout = setTimeout(() => {
|
||||
if (trailing && lastArgs) {
|
||||
lastCall = Date.now()
|
||||
lastResult = fn(...lastArgs)
|
||||
}
|
||||
}, delay)
|
||||
}
|
||||
|
||||
console.log(lastResult)
|
||||
return lastResult
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user