Progress Bar
Implement a progress bar that updates based on a value input by the user.
Solution
Enter a string with at least 50 characters
Explanation
The HTML Structure
A progress container will have two overlapping elements. A background div that has a constant fixed width and a progress div that will change size based on a calculation.
Beneath that is the text area to enter the content to be counted.
Counting the characters
The text area has an onChange attribute linked to a handleCount function.
The function calculates the characters by measuring the length of the value. This is tracked in useState, with count.
Animating the progress bar
To update the width of the progress div a small calculation is done on progressStyle to see how many pixels should be added for every extra unit of the count.
The width of this element has been fixed to 400px and an upper limit of 50 characters. This means every 1 unit is equivalent to 8px.
Bonus animation
For extra benefit to the user experience, isComplete is a state to track when the 50 character limit has been reached. Then short-circuit evaluation controls the display of the 'complete' alert.
Code
import { useState, CSSProperties } from "react";
import styles from "./Solution.module.css";
const Solution = () => {
const [count, setCount] = useState(0);
const [isComplete, setIsComplete] = useState(false);
const progressStyle: CSSProperties = {
// 400px / 50 characters = 8 -> calc proportion of length
width: `${8 * count}px`,
};
const handleCount: React.ChangeEventHandler<HTMLTextAreaElement> = (
event
) => {
const newCount = event.target.value.length;
setCount(newCount);
setIsComplete(newCount >= 50);
};
return (
<div>
<h3>Enter a string with at least 50 characters</h3>
<div className={styles.progressContainer}>
<div className={styles.progressBG}></div>
<div style={progressStyle} className={styles.progress}></div>
</div>
<textarea
className={styles.input}
onChange={handleCount}
cols={46}
rows={3}
/>
{isComplete && <h3 className={styles.completeMsg}>Complete!</h3>}
</div>
);
};
export default Solution;Styling
.input {
padding: 5px;
margin: 50px 0 20px;
}
.progressContainer {
position: relative;
}
.progress {
position: absolute;
margin-top: 10px;
height: 20px;
max-width: 400px;
background-color: #7b8ade;
border: 1px solid black;
}
.progressBG {
position: absolute;
margin-top: 10px;
height: 20px;
width: 400px;
border: 1px solid black;
}
.completeMsg {
color: red;
}