Initial skeleton: React/TS frontend + Go backend structure
This commit is contained in:
53
client/src/hooks/useSpotlight.ts
Normal file
53
client/src/hooks/useSpotlight.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { useMemo } from 'react'
|
||||
|
||||
export const useSpotlight = () => {
|
||||
const fuzzy = (query: string, text: string): number => {
|
||||
if (!query) return 0
|
||||
|
||||
const queryLower = query.toLowerCase()
|
||||
const textLower = text.toLowerCase()
|
||||
|
||||
let score = 0
|
||||
let queryIndex = 0
|
||||
|
||||
for (let i = 0; i < textLower.length && queryIndex < queryLower.length; i++) {
|
||||
if (textLower[i] === queryLower[queryIndex]) {
|
||||
score++
|
||||
queryIndex++
|
||||
}
|
||||
}
|
||||
|
||||
return score / Math.max(query.length, text.length)
|
||||
}
|
||||
|
||||
const filterItems = <T extends { name: string; type: string }>(
|
||||
items: T[],
|
||||
query: string,
|
||||
filterType: string | null
|
||||
): T[] => {
|
||||
return items.filter((item) => {
|
||||
const matchesQuery = fuzzy(query, item.name) > 0.3
|
||||
const matchesType = !filterType || filterType === 'all' || item.type === filterType
|
||||
return matchesQuery && matchesType
|
||||
})
|
||||
}
|
||||
|
||||
const debounce = <T extends (...args: any[]) => any>(fn: T, delay: number) => {
|
||||
let timeoutId: NodeJS.Timeout | null = null
|
||||
return (...args: Parameters<T>): Promise<ReturnType<T>> => {
|
||||
return new Promise((resolve) => {
|
||||
if (timeoutId) clearTimeout(timeoutId)
|
||||
timeoutId = setTimeout(() => resolve(fn(...args)), delay)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
fuzzy,
|
||||
filterItems,
|
||||
debounce,
|
||||
}),
|
||||
[]
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user