diff --git a/src/App.css b/src/App.css index b9d355d..74b17e3 100644 --- a/src/App.css +++ b/src/App.css @@ -2,7 +2,6 @@ max-width: 1280px; margin: 0 auto; padding: 2rem; - text-align: center; } .logo { diff --git a/src/App.tsx b/src/App.tsx index afe48ac..0492646 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,34 +1,15 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' -import './App.css' +import './App.css'; +import Input from './components/Input'; function App() { - const [count, setCount] = useState(0) + const placeholder = "Type something to search..."; + + const handleSelectItem = (value: any) => { + alert(`Search result: ${value}`); + }; return ( - <> -
- - Vite logo - - - React logo - -
-

Vite + React

-
- -

- Edit src/App.tsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- + ) } diff --git a/src/components/Input/index.tsx b/src/components/Input/index.tsx index 453c742..6c8785a 100644 --- a/src/components/Input/index.tsx +++ b/src/components/Input/index.tsx @@ -1,21 +1,72 @@ import "./input.scss"; import { fetchData } from "../../utils/fetch-data"; -import { debounce } from "../../utils/deboucne"; +import { debounce } from "../../utils/debounce"; import Loader from "../Loader"; +import { useEffect, useState } from "react"; export interface InputProps { - /** Placeholder of the input */ placeholder?: string; - /** On click item handler */ onSelectItem: (item: string) => void; } const Input = ({ placeholder, onSelectItem }: InputProps) => { // DO NOT remove this log - console.log('input re-render') + // console.log('input re-render', placeholder); // Your code start here - return + const DEBOUNCE_PERIOD = 100; + + const [result, setResult] = useState([]); + const [errMessage, setErrMessage] = useState(''); + const [searchText, setSearchText] = useState(''); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + handleFetchingData(); + console.log(result); + }, [searchText]); + + const handleFetchingData = async () => { + setIsLoading(true); + + try { + const response = await fetchData(searchText); + setSearchText(searchText); + setResult(response); + setErrMessage(''); + } catch (error) { + setErrMessage(error as string); + setResult([]); + } finally { + setIsLoading(false); + } + }; + + const handleChangeInput = debounce((e: any) => { + setSearchText(e.target.value) + }, DEBOUNCE_PERIOD); + + return ( +
+ + {isLoading && } + { + result + ? searchText &&
    + {result.map((item, index) => ( +
  • onSelectItem(item)}>{item}
  • + ))} +
+ :
No results
+ + } + {errMessage &&
{errMessage}
} +
+ ) // Your code end here }; diff --git a/src/components/Input/input.scss b/src/components/Input/input.scss index 1dafbe7..e8920e0 100644 --- a/src/components/Input/input.scss +++ b/src/components/Input/input.scss @@ -1,6 +1,45 @@ * { box-sizing: border-box; -} -html{ font-size: 16px; } + +.search { + &__panel { + max-width: 456px; + margin: 60px auto; + } + + &__box { + width: 100%; + padding: 12px 20px; + } + + &__results { + margin: 0; + padding: 0; + list-style-type: none; + max-height: 480px; + overflow: auto; + } + + &__item { + padding: 12px 20px; + background-color: #fafafa; + cursor: pointer; + transition: all .3s; + + &:hover { + background-color: #efefef; + } + } + + &__message, &__error { + padding: 12px 20px; + background-color: #fafafa; + } + + &__error { + color: rgba(255, 0, 0, 1); + background: rgba(255, 0, 0, 0.1); + } +} diff --git a/src/index.css b/src/index.css index 6119ad9..52810fd 100644 --- a/src/index.css +++ b/src/index.css @@ -24,10 +24,6 @@ a:hover { body { margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; } h1 { diff --git a/src/utils/deboucne.ts b/src/utils/debounce.ts similarity index 100% rename from src/utils/deboucne.ts rename to src/utils/debounce.ts