<div id="root"></div>
body {
height: 100vh;
width: 100vw;
margin: 0;
background: #cd5d7d;
}
.flex {
display: flex;
}
.justify-center {
justify-content: center;
}
.justify-between {
justify-content: space-between;
}
.justify-around {
justify-content: space-around;
}
.align-center {
align-items: center;
}
.w-full {
width: 100vw;
}
.w-max {
width: 100%;
}
.w-threefourth {
width: 75%;
}
.w-half {
width: 50%;
}
.w-third {
width: 33%;
}
.h-100 {
height: 100px;
}
.h-400 {
height: 400px;
}
.h-full {
height: 100vh;
}
.h-max {
height: 100%;
}
.flex-row {
display: flex;
flex-direction: row;
}
.flex-col {
display: flex;
flex-direction: column;
}
.conditional-row-to-col {
display: flex;
@media (max-width: 480px) {
flex-direction: column;
}
}
.conditional-text-center {
@media (max-width: 480px) {
text-align: center;
}
}
.border {
border: 0.05rem solid;
}
.my-10 {
margin: 100px, 10px;
}
.input {
height: 1rem;
min-width: 50px;
}
.bg-white {
background: white;
}
.bg-red {
background: red;
}
.rounded-m {
border-radius: 8px;
}
/* dropdown button */
.dropdown {
position: relative;
display: inline-block;
margin: 0 0 0 30px;
@media (max-width: 480px) {
margin: 0;
}
}
.dropbtn {
background-color: #4caf50;
padding: 16px;
font-size: 16px;
border: none;
cursor: pointer;
padding: 0 15px;
}
.dropdown-content {
display: block;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 1;
list-style: none;
margin: 0;
padding: 0 0 0 10px;
cursor: pointer;
}
.dropdown:hover .dropbtn {
background-color: #3e8e41;
}
.dropdown-content li {
transition: background-color 0.3s ease;
}
.dropdown-content li:hover {
color: #3e8e41;
}
/* dropdown-button-end */
View Compiled
import React, {
ChangeEvent,
useState,
useEffect,
useRef
} from "https://cdn.skypack.dev/react";
import ReactDOM from "https://cdn.skypack.dev/react-dom";
interface DropdownProps {
value: any;
setValue: (value: any) => void;
}
const Dropdown: React.FC<DropdownProps> = ({ value, setValue, options }) => {
const [isOpen, setIsOpen] = useState(false);
const handleCurrencyOption = (option: string) => {
setValue(option);
setIsOpen(!isOpen);
};
return (
<div aria-label="dropdown" className="dropdown">
<div className="dropbtn" onClick={() => setIsOpen(!isOpen)}>
<h2>{value.toUpperCase() || "Select a currency"}▼</h2>
</div>
{isOpen && (
<ul className="dropdown-content">
{options.map((option, index) => (
<li key={index} onClick={() => handleCurrencyOption(option)}>
{option.toUpperCase()}
</li>
))}
</ul>
)}
</div>
);
};
const formatNum = (number: number) => {
return Number(number.toFixed(2)).toLocaleString();
};
const App = () => {
const options = ["usd", "eur", "gbp", "cny", "jpy"];
const [amount, setAmount] = useState("");
const [wucRate, setWUCRate] = useState(0);
const [previousRate, setPreviousRate] = useState(0);
const interimRate = useRef(0);
const [currency, setCurrency] = useState(options[0]);
const [debouncedAmount, setDebouncedAmount] = useState(amount);
const fetchData = async (currency: string) => {
try {
const response = await fetch(
`https://api.frontendeval.com/fake/crypto/${currency}`
);
const data = await response.json();
setWUCRate(data.value);
interimRate.current = data.value;
} catch (error: any) {
throw new Error(error.message);
}
};
const onChangeAmount = (event: ChangeEvent<HTMLInputElement>) => {
setAmount(event.target.value);
};
useEffect(() => {
const debounceTimer = setTimeout(() => {
setDebouncedAmount(amount);
}, 500);
return () => {
clearTimeout(debounceTimer);
};
}, [amount]);
useEffect(() => {
const resetRates = () => {
setWUCRate(0);
setPreviousRate(0);
interimRate.current = 0;
};
const fetchDataWithDebounce = async () => {
setPreviousRate(interimRate.current);
await fetchData(currency);
};
if (amount && currency) {
resetRates();
fetchDataWithDebounce();
const intervalId = setInterval(async () => {
fetchDataWithDebounce();
}, 10000);
return () => {
clearInterval(intervalId);
};
}
}, [debouncedAmount, currency]);
const numAmount = Number(debouncedAmount);
const convertedAmount = wucRate * numAmount;
const previousAmount = previousRate * numAmount;
const amountDiff = convertedAmount - previousAmount;
const amountDiffCondition = (first: string, second: string, third: string) =>
amountDiff > 0 ? first : amountDiff < 0 ? second : third;
return (
<div className="flex-col align-center justify-center h-full">
<div className="bg-white h-400 flex-col w-half align-center justify-center rounded-m">
<h1 className="flex conditional-text-center">WUC exchange</h1>
<div className="flex-col align-center bg-white w-max">
<div className=" conditional-row-to-col justify-center align-center h-100">
<input
type="number"
aria-label="amount"
name="amount"
onChange={onChangeAmount}
className="input"
min="0"
/>
<Dropdown
value={currency}
setValue={setCurrency}
options={options}
/>
</div>
<div className="conditional-row-to-col align-center w-threefourth justify-around">
<p aria-label="converted-amount">{formatNum(convertedAmount)}</p>
<p>WUC</p>
<p
aria-label="amount-diff"
style={{
color: `${amountDiffCondition("green", "red", "black")}`
}}
>
{amountDiffCondition("↑", "↓", "")}
{formatNum(amountDiff)}
</p>
</div>
</div>
</div>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.