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