First Commit
This commit is contained in:
parent
1b0f36a39f
commit
c0bae65c80
10 changed files with 341 additions and 0 deletions
7
README.md
Normal file
7
README.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# A simple password generator built in `React.js`
|
||||||
|
|
||||||
|
`npm install`
|
||||||
|
|
||||||
|
`npm start`
|
||||||
|
|
||||||
|
# For build production run `npm run build`
|
||||||
33
package.json
Normal file
33
package.json
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"name": "password-gen",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-scripts": "5.0.1",
|
||||||
|
"react-toastify": "^9.1.1"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build"
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
12
public/index.html
Normal file
12
public/index.html
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Simple Password Generator</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
3
public/robots.txt
Normal file
3
public/robots.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
188
src/App.jsx
Normal file
188
src/App.jsx
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
import { React, useState} from 'react';
|
||||||
|
import { toast, ToastContainer} from 'react-toastify';
|
||||||
|
import './Assets/CSS/App.min.css';
|
||||||
|
import { numbers, upperCaseLetters, lowerCaseLetters, specialCharacters } from './Components/Characters';
|
||||||
|
import 'react-toastify/dist/ReactToastify.css';
|
||||||
|
import { COPY_SUCCESS } from './Components/Message';
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [password, setPassword] = useState('')
|
||||||
|
const [passwordLength, setPasswordLength] = useState(20)
|
||||||
|
const [includeUppercase, setIncludeUppercase] = useState(false)
|
||||||
|
const [includeLowercase, setIncludeLowercase] = useState(false)
|
||||||
|
const [includeNumbers, setIncludeNumbers] = useState(false)
|
||||||
|
const [includeSymbols, setIncludeSymbols] = useState(false)
|
||||||
|
|
||||||
|
const handleGeneratePassword = (e) => {
|
||||||
|
if (
|
||||||
|
!includeUppercase &&
|
||||||
|
!includeLowercase &&
|
||||||
|
!includeNumbers &&
|
||||||
|
!includeSymbols
|
||||||
|
) {
|
||||||
|
notify('Error! You must select atleast one option.', true)
|
||||||
|
}
|
||||||
|
let characterList = ''
|
||||||
|
|
||||||
|
if (includeLowercase) {
|
||||||
|
characterList = characterList + lowerCaseLetters
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeUppercase) {
|
||||||
|
characterList = characterList + upperCaseLetters
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeNumbers) {
|
||||||
|
characterList = characterList + numbers
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeSymbols) {
|
||||||
|
characterList = characterList + specialCharacters
|
||||||
|
}
|
||||||
|
|
||||||
|
setPassword(createPassword(characterList))
|
||||||
|
}
|
||||||
|
|
||||||
|
const createPassword = (characterList) => {
|
||||||
|
let password = ''
|
||||||
|
const characterListLength = characterList.length
|
||||||
|
|
||||||
|
for (let i = 0; i < passwordLength; i++) {
|
||||||
|
const characterIndex = Math.round(Math.random() * characterListLength)
|
||||||
|
password = password + characterList.charAt(characterIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
return password
|
||||||
|
}
|
||||||
|
|
||||||
|
const copyToClipboard = () => {
|
||||||
|
const newTextArea = document.createElement('textarea')
|
||||||
|
newTextArea.innerText = password
|
||||||
|
document.body.appendChild(newTextArea)
|
||||||
|
newTextArea.select()
|
||||||
|
document.execCommand('copy')
|
||||||
|
newTextArea.remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
const notify = (message, hasError = false) => {
|
||||||
|
if (hasError) {
|
||||||
|
toast.error(message, {
|
||||||
|
position: 'top-center',
|
||||||
|
autoClose: 5000,
|
||||||
|
hideProgressBar: false,
|
||||||
|
closeOnClick: true,
|
||||||
|
pauseOnHover: true,
|
||||||
|
draggable: false,
|
||||||
|
progress: undefined,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
toast(message, {
|
||||||
|
position: 'top-center',
|
||||||
|
autoClose: 5000,
|
||||||
|
hideProgressBar: false,
|
||||||
|
closeOnClick: true,
|
||||||
|
pauseOnHover: true,
|
||||||
|
draggable: false,
|
||||||
|
progress: undefined,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCopyPassword = (e) => {
|
||||||
|
if(password === '') {
|
||||||
|
notify('Error! Nothing to copy', true)
|
||||||
|
} else {
|
||||||
|
copyToClipboard()
|
||||||
|
notify(COPY_SUCCESS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="App">
|
||||||
|
<div className="container">
|
||||||
|
<div className="generator">
|
||||||
|
<h2 className="generator__header">Simple Password Generator</h2>
|
||||||
|
<div className="generator__password">
|
||||||
|
<h3>{password}</h3>
|
||||||
|
<button onClick={handleCopyPassword} className='copy__btn'>
|
||||||
|
<i className="far fa-clipboard"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form-group">
|
||||||
|
<label className='passgen-text' htmlFor='password-strength'>Password Length</label>
|
||||||
|
<input
|
||||||
|
defaultValue={passwordLength}
|
||||||
|
onChange={(e) => setPasswordLength(e.target.value)}
|
||||||
|
type='number'
|
||||||
|
id='password-strength'
|
||||||
|
name='password-strength'
|
||||||
|
max='22'
|
||||||
|
min='10'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form-group">
|
||||||
|
<label className='passgen-text' htmlFor='uppercase-letters'>Include Upppercase Characters</label>
|
||||||
|
<input
|
||||||
|
checked={includeUppercase}
|
||||||
|
onChange={(e) => setIncludeUppercase(e.target.checked)}
|
||||||
|
type='checkbox'
|
||||||
|
id='uppercase-letters'
|
||||||
|
name='uppercase-letters'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form-group">
|
||||||
|
<label className='passgen-text' htmlFor='lowercase-letters'>Include Lowercase Characters</label>
|
||||||
|
<input
|
||||||
|
checked={includeLowercase}
|
||||||
|
onChange={(e) => setIncludeLowercase(e.target.checked)}
|
||||||
|
type='checkbox'
|
||||||
|
id='lowercase-letters'
|
||||||
|
name='lowercase-letters'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form-group">
|
||||||
|
<label className='passgen-text' htmlFor='include-numbers'>Include Numbers</label>
|
||||||
|
<input
|
||||||
|
checked={includeNumbers}
|
||||||
|
onChange={(e) => setIncludeNumbers(e.target.checked)}
|
||||||
|
type='checkbox'
|
||||||
|
id='include-numbers'
|
||||||
|
name='include-numbers'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form-group">
|
||||||
|
<label className='passgen-text' htmlFor='include-symbols'>Include Symbols</label>
|
||||||
|
<input
|
||||||
|
checked={includeSymbols}
|
||||||
|
onChange={(e) => setIncludeSymbols(e.target.checked)}
|
||||||
|
type='checkbox'
|
||||||
|
id='include-symbols'
|
||||||
|
name='include-symbols'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button onClick={handleGeneratePassword} className="generator__btn">
|
||||||
|
Generate
|
||||||
|
</button>
|
||||||
|
<ToastContainer
|
||||||
|
position='top-center'
|
||||||
|
autoClose={5000}
|
||||||
|
hideProgressBar={false}
|
||||||
|
newestOnTop={false}
|
||||||
|
closeOnClick={true}
|
||||||
|
rtl={false}
|
||||||
|
pauseOnFocusLoss={false}
|
||||||
|
draggable={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
||||||
77
src/Assets/CSS/App.min.css
vendored
Normal file
77
src/Assets/CSS/App.min.css
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
@import url(https://fonts.bunny.net/css?family=abril-fatface:400|rubik:600,800i,900i);
|
||||||
|
|
||||||
|
.App {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #101725;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 350px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding-top: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generator {
|
||||||
|
background-color: #0b0f19;
|
||||||
|
border-radius: 3px;
|
||||||
|
box-shadow: 0px 2px 10px rgba(255, 255, 255, 0.2);
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generator__header {
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-family: 'Abril Fatface', display;
|
||||||
|
font-family: 'Rubik', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generator__password {
|
||||||
|
position: relative;
|
||||||
|
background-color: #101725;
|
||||||
|
padding: 13px 10px;
|
||||||
|
color: white;
|
||||||
|
height: 46px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-family: 'Abril Fatface', display;
|
||||||
|
font-family: 'Rubik', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy__btn {
|
||||||
|
position: absolute;
|
||||||
|
background-color: #3b3b98;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
height: 40px;
|
||||||
|
padding: 10;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 3px;
|
||||||
|
right: 3x;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generator__btn {
|
||||||
|
background-color: #101725;
|
||||||
|
border: none;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
color: white;
|
||||||
|
font-size: 17px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: 'Abril Fatface', display;
|
||||||
|
font-family: 'Rubik', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
color: white;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-family: 'Abril Fatface', display;
|
||||||
|
font-family: 'Rubik', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.passgen-text {
|
||||||
|
font-family: 'Abril Fatface', display;
|
||||||
|
font-family: 'Rubik', sans-serif;
|
||||||
|
}
|
||||||
5
src/Assets/CSS/Index.min.css
vendored
Normal file
5
src/Assets/CSS/Index.min.css
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
4
src/Components/Characters.js
Normal file
4
src/Components/Characters.js
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
export const numbers = '0123456789'
|
||||||
|
export const upperCaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||||
|
export const lowerCaseLetters = 'abcdefghijklmnopqrstuvwxyz'
|
||||||
|
export const specialCharacters = "!'^+%&/()=?_#$½§{[]}|;:>÷`<.*-@é"
|
||||||
1
src/Components/Message.js
Normal file
1
src/Components/Message.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export const COPY_SUCCESS = 'Password successfully copied to clipboard'
|
||||||
11
src/index.js
Normal file
11
src/index.js
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import './Assets/CSS/Index.min.css';
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>,
|
||||||
|
document.getElementById('app')
|
||||||
|
)
|
||||||
Loading…
Add table
Reference in a new issue