From 5ec0a0824ed401239cf192625d97e6671cd62beb Mon Sep 17 00:00:00 2001 From: "NITECO\\huy.pham" Date: Fri, 4 Oct 2024 17:57:18 +0700 Subject: [PATCH] Reactjs assignment 1 --- src/components/Input/index.tsx | 113 ++++++++++++++++++++++++++++++-- src/components/Input/input.scss | 78 +++++++++++++++++++++- 2 files changed, 181 insertions(+), 10 deletions(-) diff --git a/src/components/Input/index.tsx b/src/components/Input/index.tsx index 453c742..ffd7caf 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 { debounce } from "../../utils/deboucne"; -import Loader from "../Loader"; +import './input.scss'; +import { fetchData } from '../../utils/fetch-data'; +import { debounce } from '../../utils/deboucne'; +import Loader from '../Loader'; +import { useRef, useState } from 'react'; export interface InputProps { /** Placeholder of the input */ @@ -10,14 +11,112 @@ export interface InputProps { onSelectItem: (item: string) => void; } +const Item = ({ + name, + className, + onSelectItem +}: { + name: string; + className: string; + onSelectItem: (item: string) => void; +}) => ( +
  • onSelectItem(name)}> + {name} +
  • +); + const Input = ({ placeholder, onSelectItem }: InputProps) => { // DO NOT remove this log - console.log('input re-render') + console.log('input re-render'); // Your code start here - return + + const [result, setResult] = useState<{ + items: string[]; + error: string | null; + }>({ + items: [], + error: null + }); + const [isLoading, setIsLoading] = useState(false); + const [isOpen, setIsOpen] = useState(false); + const latestQuery = useRef(''); + + const handleChange = debounce( + async (e: React.ChangeEvent) => { + const query = e.target.value; + latestQuery.current = query; + + if (query.length === 0) { + setIsOpen(false); + return; + } + + try { + setIsOpen(true); + setIsLoading(true); + const names = await fetchData(query); + + if (latestQuery.current !== query) return; + + setResult({ + items: names, + error: null + }); + } catch (error: unknown) { + if (latestQuery.current !== query) return; + setResult({ + items: [], + error: String(error).toString() + }); + } finally { + if (latestQuery.current == query) { + setIsLoading(false); + } + } + }, + 100 + ); + const { items, error } = result; + + return ( +
    + + {isOpen && ( +
    + {isLoading && } + + {!isLoading && !error && items.length > 0 && ( +
      + {items.map((name) => ( + + ))} +
    + )} + {!isLoading && items.length === 0 && !error && ( +
    + No results +
    + )} + {!isLoading && error && ( +
    + {error} +
    + )} +
    + )} +
    + ); // Your code end here }; export default Input; - diff --git a/src/components/Input/input.scss b/src/components/Input/input.scss index 1dafbe7..c2ec58a 100644 --- a/src/components/Input/input.scss +++ b/src/components/Input/input.scss @@ -1,6 +1,78 @@ * { - box-sizing: border-box; + box-sizing: border-box; } -html{ - font-size: 16px; +html { + font-size: 16px; +} + +@mixin text-styles { + font-family: 'Roboto', sans-serif; + font-size: 16px; + color: #03183f; +} +@mixin header-styles { + font-family: 'Roboto', sans-serif; + font-size: 16px; + color: #03183f; +} + +.container { + position: relative; + @include text-styles; +} + +.input { + width: 500px; + height: 50px; + padding: 16px 24px; + border-radius: 5px; + color: #03183f; + border-color: #03183f6b; + + font: 'Roboto', sans-serif; +} + +.list-container { + position: absolute; + top: 100%; + left: 0; + + margin-top: 8px; + border-radius: 5px; + border: 1px solid #03183f6b; + + padding: 16px 0; + width: 100%; + min-height: 150px; + max-height: 400px; + overflow-y: auto; +} + +.list { + @include text-styles; + padding: 0; + margin: 0; + list-style: none; +} + +.item { + line-height: 1.6; + padding: 4px 24px; + &:hover { + background-color: rgb(158, 156, 156); + color: white; + cursor: pointer; + } +} + +.no-result { + @include header-styles; + text-align: center; + padding: 4px 24px; +} + +.error { + @include text-styles; + color: #c92c25; + padding: 4px 24px; }