Add 25-5-clock certification project
This commit is contained in:
parent
0a6f1dd983
commit
0006c8b50a
10 changed files with 29146 additions and 0 deletions
23
3-frontend-dev-libraries/5-25-plus-5-clock/.gitignore
vendored
Normal file
23
3-frontend-dev-libraries/5-25-plus-5-clock/.gitignore
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
28738
3-frontend-dev-libraries/5-25-plus-5-clock/package-lock.json
generated
Normal file
28738
3-frontend-dev-libraries/5-25-plus-5-clock/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
39
3-frontend-dev-libraries/5-25-plus-5-clock/package.json
Normal file
39
3-frontend-dev-libraries/5-25-plus-5-clock/package.json
Normal file
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"name": "5-25-plus-5-clock",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"homepage": "https://cherrykitten.github.io/freecodecamp-projects/3-frontend-dev-libraries/5-25-plus-5-clock",
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
29
3-frontend-dev-libraries/5-25-plus-5-clock/public/index.html
Normal file
29
3-frontend-dev-libraries/5-25-plus-5-clock/public/index.html
Normal file
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="25+5 Clock App created for Freecodecamp"
|
||||
/>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
<title>25 + 5 Clock</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
<script src='https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js'></script>
|
||||
</body>
|
||||
</html>
|
55
3-frontend-dev-libraries/5-25-plus-5-clock/src/App.css
Normal file
55
3-frontend-dev-libraries/5-25-plus-5-clock/src/App.css
Normal file
|
@ -0,0 +1,55 @@
|
|||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#App {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
font-family: sans-serif;
|
||||
background-color: #232323;
|
||||
}
|
||||
|
||||
#controls {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
text-align: center;
|
||||
width: 50%;
|
||||
padding: 20px;
|
||||
margin: 30px;
|
||||
font-size: 2rem;
|
||||
}
|
||||
#controls #session-controls, #controls #break-controls {
|
||||
padding: 10px;
|
||||
background-color: grey;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
#timer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 50%;
|
||||
padding: 20px;
|
||||
margin: 30px;
|
||||
font-size: 2rem;
|
||||
background-color: grey;
|
||||
border-radius: 10px;
|
||||
}
|
||||
#timer #time-left {
|
||||
font-size: 8rem;
|
||||
font-family: monospace;
|
||||
}
|
||||
#timer #timer-controls {
|
||||
display: flex;
|
||||
padding-top: 50px;
|
||||
}
|
||||
#timer #timer-controls #start_stop, #timer #timer-controls #reset {
|
||||
padding: 0 30px;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=App.css.map */
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sourceRoot":"","sources":["App.scss"],"names":[],"mappings":"AAAA;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AACA;EACE","file":"App.css"}
|
200
3-frontend-dev-libraries/5-25-plus-5-clock/src/App.js
Normal file
200
3-frontend-dev-libraries/5-25-plus-5-clock/src/App.js
Normal file
|
@ -0,0 +1,200 @@
|
|||
import './App.css';
|
||||
import React from 'react';
|
||||
import beep from './beep.mp3'
|
||||
|
||||
class App extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
activeTimer: 'Session',
|
||||
sessionLength: 25,
|
||||
breakLength: 5,
|
||||
timeLeft: 1500,
|
||||
status: 'stopped',
|
||||
}
|
||||
this.increment = this.increment.bind(this);
|
||||
this.decrement = this.decrement.bind(this);
|
||||
this.toggleTimer = this.toggleTimer.bind(this);
|
||||
this.resetTimer = this.resetTimer.bind(this);
|
||||
this.decrementTimer = this.decrementTimer.bind(this);
|
||||
this.updateTimer = this.updateTimer.bind(this);
|
||||
this.finishTimer = this.finishTimer.bind(this);
|
||||
this.stopTimer = this.stopTimer.bind(this);
|
||||
this.showTime = this.showTime.bind(this);
|
||||
}
|
||||
|
||||
increment(type) {
|
||||
if (this.state.status === 'stopped') {
|
||||
if (type === 'session') {
|
||||
if (this.state.sessionLength < 60) {
|
||||
this.setState({
|
||||
sessionLength: this.state.sessionLength + 1,
|
||||
timeLeft: this.state.timeLeft + 60,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (this.state.breakLength < 60) {
|
||||
this.setState({breakLength: this.state.breakLength + 1})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decrement(type){
|
||||
if (type === 'session'){
|
||||
if (this.state.sessionLength > 1){this.setState({sessionLength: this.state.sessionLength - 1})}
|
||||
} else {
|
||||
if (this.state.breakLength > 1){this.setState({breakLength: this.state.breakLength - 1})};
|
||||
}
|
||||
}
|
||||
|
||||
resetTimer(){
|
||||
this.setState({
|
||||
activeTimer: 'Session',
|
||||
sessionLength: 25,
|
||||
breakLength: 5,
|
||||
status: 'stopped',
|
||||
timeLeft: 1500
|
||||
})
|
||||
this.stopTimer()
|
||||
document.getElementById('beep').pause()
|
||||
document.getElementById('beep').currentTime = 0;
|
||||
}
|
||||
|
||||
stopTimer(){
|
||||
if (this.state.intervalID !== 0){
|
||||
clearInterval(this.state.intervalID)
|
||||
this.setState({
|
||||
intervalID: 0
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
toggleTimer(){
|
||||
if (this.state.status === 'stopped'){
|
||||
this.setState({
|
||||
status: 'running',
|
||||
timeLeft: this.state.sessionLength * 60,
|
||||
})
|
||||
this.updateTimer();
|
||||
} else if (this.state.status === 'running'){
|
||||
this.stopTimer()
|
||||
this.setState({
|
||||
status: 'paused'
|
||||
})
|
||||
} else if (this.state.status === 'paused'){
|
||||
this.setState({
|
||||
status: 'running',
|
||||
})
|
||||
this.updateTimer();
|
||||
} else if (this.state.status === 'finished'){
|
||||
this.setState({
|
||||
timeLeft: this.state.timeLeft + 1,
|
||||
status: 'running',
|
||||
})
|
||||
this.updateTimer()
|
||||
}
|
||||
}
|
||||
|
||||
decrementTimer(){
|
||||
if (this.state.status === 'stopped'){return this.stopTimer()}
|
||||
if (this.state.timeLeft === 0){
|
||||
this.finishTimer();
|
||||
}
|
||||
if (this.state.status === 'running') {
|
||||
this.setState({
|
||||
timeLeft: this.state.timeLeft - 1,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
updateTimer(){
|
||||
let intervalID = setInterval(() => {this.decrementTimer()}, 1000)
|
||||
this.setState({
|
||||
intervalID: intervalID
|
||||
})
|
||||
}
|
||||
|
||||
finishTimer(){
|
||||
clearInterval(this.state.intervalID)
|
||||
this.setState({
|
||||
intervalID: 0
|
||||
})
|
||||
let beep = document.getElementById('beep');
|
||||
let nextSession;
|
||||
let timeLeft;
|
||||
if (this.state.activeTimer === 'Session'){
|
||||
nextSession = 'Break';
|
||||
timeLeft = this.state.breakLength * 60;
|
||||
} else{
|
||||
nextSession = 'Session';
|
||||
timeLeft = this.state.sessionLength * 60;
|
||||
}
|
||||
this.setState({
|
||||
activeTimer: nextSession,
|
||||
timeLeft: timeLeft,
|
||||
status: 'finished'
|
||||
})
|
||||
beep.currentTime = 0
|
||||
beep.play()
|
||||
this.toggleTimer()
|
||||
}
|
||||
|
||||
showTime(){
|
||||
let mins = (Math.floor(this.state.timeLeft / 60)).toString().padStart(2, '0');
|
||||
let secs = (this.state.timeLeft - mins * 60).toString().padStart(2, '0');
|
||||
return (mins + ':' + secs)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div id={'App'}>
|
||||
<div id={'controls'}>
|
||||
<Controls type={'session'} time={this.state.sessionLength} increment={this.increment} decrement={this.decrement}/>
|
||||
<Controls type={'break'} time={this.state.breakLength} increment={this.increment} decrement={this.decrement} />
|
||||
</div>
|
||||
<div id={'timer'}>
|
||||
<div id={'timer-label'}>
|
||||
{this.state.activeTimer}
|
||||
</div>
|
||||
<div id={'time-left'}>
|
||||
{this.showTime()}
|
||||
</div>
|
||||
<div id={'timer-controls'}>
|
||||
<div id={'start_stop'} onClick={this.toggleTimer}>
|
||||
<i className="fa-solid fa-play"></i><i className="fa-solid fa-pause"></i>
|
||||
</div>
|
||||
<div id={'reset'} onClick={this.resetTimer}>
|
||||
<i className="fa-solid fa-arrows-rotate"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<audio id={'beep'} src={beep} preload='true'></audio>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Controls extends React.Component {
|
||||
render() {
|
||||
const type = this.props.type;
|
||||
return (
|
||||
<div id={type + '-controls'}>
|
||||
<div id={type + '-label'}>
|
||||
{type} length
|
||||
</div>
|
||||
<div id={type + '-increment'} onClick={() => this.props.increment(type)}>
|
||||
<i className={'fa-solid fa-arrow-up'}></i>
|
||||
</div>
|
||||
<div id={type + '-length'}>
|
||||
{this.props.time}
|
||||
</div>
|
||||
<div id={type + '-decrement'} onClick={() => this.props.decrement(type)}>
|
||||
<i className={'fa-solid fa-arrow-down'}></i>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
56
3-frontend-dev-libraries/5-25-plus-5-clock/src/App.scss
Normal file
56
3-frontend-dev-libraries/5-25-plus-5-clock/src/App.scss
Normal file
|
@ -0,0 +1,56 @@
|
|||
body{
|
||||
margin: 0;
|
||||
}
|
||||
#App{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
font-family: sans-serif;
|
||||
background-color: #232323
|
||||
}
|
||||
|
||||
#controls{
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
text-align: center;
|
||||
width: 50%;
|
||||
padding: 20px;
|
||||
margin: 30px;
|
||||
font-size: 2rem;
|
||||
|
||||
#session-controls, #break-controls{
|
||||
padding: 10px;
|
||||
background-color: grey;
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
#timer{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 50%;
|
||||
padding: 20px;
|
||||
margin: 30px;
|
||||
font-size: 2rem;
|
||||
background-color: grey;
|
||||
border-radius: 10px;
|
||||
|
||||
#time-left{
|
||||
font-size: 8rem;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
#timer-controls{
|
||||
display: flex;
|
||||
padding-top: 50px;
|
||||
#start_stop, #reset{
|
||||
padding: 0 30px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
BIN
3-frontend-dev-libraries/5-25-plus-5-clock/src/beep.mp3
Normal file
BIN
3-frontend-dev-libraries/5-25-plus-5-clock/src/beep.mp3
Normal file
Binary file not shown.
5
3-frontend-dev-libraries/5-25-plus-5-clock/src/index.js
Normal file
5
3-frontend-dev-libraries/5-25-plus-5-clock/src/index.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'))
|
Reference in a new issue