4 changed files with 92 additions and 3 deletions
@ -0,0 +1,46 @@ |
|||||
|
import { SearchIcon } from "lucide-react"; |
||||
|
import { Input } from "@components/UI/Input"; |
||||
|
import { getDeepField } from "@app/core/utils/getDeepField"; |
||||
|
|
||||
|
export interface SearchProps<T extends object = object> { |
||||
|
data: T[]; |
||||
|
filterBy: string; |
||||
|
onFilter: (results: T[]) => void; |
||||
|
} |
||||
|
|
||||
|
export function Search<T extends object = object>({ |
||||
|
data, |
||||
|
filterBy, |
||||
|
onFilter, |
||||
|
}: SearchProps<T>): JSX.Element { |
||||
|
let timeout: number | string | NodeJS.Timeout; |
||||
|
const onInputChange = (query: string) => { |
||||
|
if (timeout) { |
||||
|
clearTimeout(timeout); |
||||
|
} |
||||
|
|
||||
|
timeout = setTimeout(() => { |
||||
|
const regex = new RegExp(query, "gi"); |
||||
|
const results = data.filter((item) => { |
||||
|
const value = getDeepField(item, filterBy); |
||||
|
|
||||
|
return value && regex.test(`${value}`); |
||||
|
}); |
||||
|
|
||||
|
onFilter(results); |
||||
|
}, 250); |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<Input |
||||
|
placeholder="Search" |
||||
|
action={{ |
||||
|
icon: SearchIcon, |
||||
|
onClick: () => false, |
||||
|
}} |
||||
|
onChange={(e) => onInputChange(e.target.value)} |
||||
|
/> |
||||
|
</> |
||||
|
); |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
export function getDeepField<O extends object = object, D = any>( |
||||
|
object: O, |
||||
|
path: string, |
||||
|
defaultValue?: D, |
||||
|
): D | undefined { |
||||
|
const keys = path.split("."); |
||||
|
let value: any = object; |
||||
|
|
||||
|
for (const key of keys) { |
||||
|
if (!Object.hasOwn(value, key)) { |
||||
|
return defaultValue; |
||||
|
} |
||||
|
|
||||
|
value = value[key]; |
||||
|
} |
||||
|
|
||||
|
return value ?? defaultValue; |
||||
|
} |
||||
Loading…
Reference in new issue