diff --git a/src/components/Input/index.tsx b/src/components/Input/index.tsx index 453c742..8be7e61 100644 --- a/src/components/Input/index.tsx +++ b/src/components/Input/index.tsx @@ -1,7 +1,8 @@ -import "./input.scss"; -import { fetchData } from "../../utils/fetch-data"; +import { useCallback, useRef, useState } from "react"; import { debounce } from "../../utils/deboucne"; +import { fetchData } from "../../utils/fetch-data"; import Loader from "../Loader"; +import "./input.scss"; export interface InputProps { /** Placeholder of the input */ @@ -10,12 +11,79 @@ export interface InputProps { onSelectItem: (item: string) => void; } +interface IResult { + id: number; + value: string; +} + const Input = ({ placeholder, onSelectItem }: InputProps) => { // DO NOT remove this log console.log('input re-render') // Your code start here - return + const [loading, setLoading] = useState(false); + const [results, setResults] = useState([]); + const inputRef = useRef(null); + const isInitial = !inputRef.current?.value; + + const debouncedFetch = debounce( + async (query: string) => { + if (!query.length) { + setResults([]); + return; + } + setLoading(true); + + try { + const results = await fetchData(query); + if (query !== inputRef.current?.value) { + setResults([]); + return; + } + if (!results.length) { + setResults([{ + id: 0, + value: 'No result' + }]) + return; + } + setResults(results.map((resultStr, index) => ({ + id: index + 1, + value: resultStr + }))); + } catch (e) { + setResults([{ id: -1, value: e as string }]) + throw e; + } finally { + setLoading(false) + } + }, + 100 + ) + + const onInputChanged = (changeEvent: React.ChangeEvent) => { + const { value } = changeEvent.target; + debouncedFetch(value); + } + + const getResultClass = useCallback((id: number): string => { + const classes = ['item']; + if (id < 0) classes.push('results-container__error'); + if (id === 0) classes.push('results-container__no-result'); + else classes.push('results-container__result'); + return classes.join(' '); + }, []) + + return <> + + {!isInitial &&
+ {loading ? : results.map((result) => { + return
+
onSelectItem(result.value)}>{result.value}
+
+ })} +
} + // Your code end here }; diff --git a/src/components/Input/input.scss b/src/components/Input/input.scss index 1dafbe7..2cc3399 100644 --- a/src/components/Input/input.scss +++ b/src/components/Input/input.scss @@ -1,6 +1,34 @@ * { box-sizing: border-box; } -html{ +html { font-size: 16px; } + +input { + border-radius: 4px; + line-height: 1.5em; + font-size: 16px; + padding: .5em 1em; + width: 100%; +} + +.results-container { + width: 100%; + position: relative; + border-radius: 4px; + min-height: 100px; + border: solid 1px #ddd; + margin-top: 4px; + font-style: italic; + .item { + padding: 1em 2em; + } + &__result:hover { + cursor: pointer; + background-color: #eee; + } + &__error { + color: red; + } +}