-
-
Notifications
You must be signed in to change notification settings - Fork 583
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* completed the generator * completed password generator * resolved issues
- Loading branch information
Showing
3 changed files
with
343 additions
and
1 deletion.
There are no files selected for viewing
238 changes: 238 additions & 0 deletions
238
packages/kitchen-sink/src/examples/password-generator.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
import { useState, useEffect, useRef } from 'react'; | ||
import { block } from 'million/react'; | ||
import { BsClipboardCheck } from 'react-icons/bs'; | ||
import { FiRefreshCcw } from 'react-icons/fi'; | ||
|
||
const PasswordGenerator = block(() => { | ||
const { | ||
copyPasswordToClipboard, | ||
password, | ||
passwordInputRef, | ||
setPassword, | ||
generatePassword, | ||
} = useGeneratePassword(); | ||
return ( | ||
<div className="password_generator"> | ||
<h1>Password Generator</h1> | ||
|
||
<p> | ||
Create strong and secure passwords to keep your account safe online. | ||
</p> | ||
|
||
<div className="password_generator_top_section"> | ||
{/* Password Generator Output */} | ||
<div className="password_generator_output_container"> | ||
<input | ||
ref={passwordInputRef} | ||
type="text" | ||
readOnly | ||
value={password.value} | ||
/> | ||
|
||
<Tooltip text="Copy to Clipboard" show={password.isCopied}> | ||
<button | ||
type="button" | ||
className="right-icon" | ||
onClick={copyPasswordToClipboard} | ||
> | ||
<BsClipboardCheck /> | ||
</button> | ||
</Tooltip> | ||
</div> | ||
|
||
<button | ||
type="button" | ||
className="password_generator_refresh_password" | ||
onClick={generatePassword} | ||
> | ||
<FiRefreshCcw /> | ||
</button> | ||
</div> | ||
|
||
{/* Options */} | ||
|
||
<div className="password_generator_customizable_options"> | ||
<div className="password_generator_customizable_options__length"> | ||
<label>Length: {password.length}</label> | ||
<input | ||
type="range" | ||
min="3" | ||
max="100" | ||
value={password.length} | ||
className="timeline" | ||
onChange={(e) => { | ||
setPassword({ | ||
...password, | ||
length: parseInt(e.target.value), | ||
}); | ||
}} | ||
/> | ||
</div> | ||
|
||
<div className="password_generator_customizable_options__others"> | ||
<div className="password_generator_customizable_options__password_type"> | ||
<label>Password Type: </label> | ||
<select | ||
value={password.password_type} | ||
onChange={(e) => | ||
setPassword({ | ||
...password, | ||
password_type: e.target.value as IPasswordType, | ||
}) | ||
} | ||
> | ||
<option value={'password'}>Password</option> | ||
<option value={'pin'}>PIN</option> | ||
</select> | ||
</div> | ||
|
||
<div className="password_generator_customizable_options__password_type"> | ||
<input | ||
id="number_checkbox" | ||
name="number_checkbox" | ||
disabled={password.password_type === 'pin'} | ||
type="checkbox" | ||
checked={password.numbers!} | ||
onChange={() => | ||
setPassword({ ...password, numbers: !password.numbers }) | ||
} | ||
className="password_generator_customizable_options__checkbox" | ||
/> | ||
<label htmlFor="number_checkbox">Number</label> | ||
</div> | ||
|
||
<div className="password_generator_customizable_options__password_type"> | ||
<input | ||
id="symbol_checkbox" | ||
name="symbol_checkbox" | ||
type="checkbox" | ||
disabled={password.password_type === 'pin'} | ||
onChange={() => | ||
setPassword({ ...password, symbols: !password.symbols }) | ||
} | ||
className="password_generator_customizable_options__checkbox" | ||
/> | ||
<label htmlFor="symbol_checkbox">Symbols</label> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}); | ||
|
||
export default PasswordGenerator; | ||
|
||
const Tooltip = ({ text, children, show }: IToolTipProps) => { | ||
return ( | ||
<div className="password_generator_tooltip-container"> | ||
<div>{children}</div> | ||
<p className={`password_generator_tooltip-text ${show ? 'visible' : ''}`}> | ||
{text} | ||
</p> | ||
</div> | ||
); | ||
}; | ||
|
||
type IPasswordType = 'password' | 'pin'; | ||
|
||
interface IPasswordState { | ||
value: string; | ||
password_type: IPasswordType; | ||
length: number; | ||
isCopied: boolean; | ||
numbers?: boolean; | ||
symbols?: boolean; | ||
} | ||
|
||
interface IToolTipProps { | ||
children: JSX.Element; | ||
text: string; | ||
show: boolean; | ||
} | ||
|
||
interface IGeneratePassword { | ||
includeNumbers: boolean; | ||
includeSymbols: boolean; | ||
passwordLength: number; | ||
password_type: IPasswordType; | ||
} | ||
|
||
const useGeneratePassword = () => { | ||
const passwordInputRef = useRef<HTMLInputElement | null>(null); | ||
|
||
const [password, setPassword] = useState<IPasswordState>({ | ||
value: '', | ||
password_type: 'password', | ||
length: 3, | ||
isCopied: false, | ||
numbers: false, | ||
symbols: false, | ||
}); | ||
|
||
const copyPasswordToClipboard = () => { | ||
if (passwordInputRef.current) { | ||
passwordInputRef.current.select(); | ||
document.execCommand('copy'); | ||
|
||
// Deselect the text | ||
window.getSelection()?.removeAllRanges(); | ||
|
||
setPassword({ ...password, isCopied: true }); | ||
|
||
setTimeout(() => { | ||
setPassword({ ...password, isCopied: false }); | ||
}, 3000); | ||
} | ||
}; | ||
|
||
const generatePassword = () => { | ||
const lowercaseChars = 'abcdefghijklmnopqrstuvwxyz'; | ||
const uppercaseChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; | ||
const numberChars = '0123456789'; | ||
const symbolChars = '!@#$%^&*()_+{}[]|:;"<>,.?/~'; | ||
|
||
let charset = ''; | ||
|
||
if (password.password_type === 'password') { | ||
charset += lowercaseChars + uppercaseChars; | ||
|
||
if (password.numbers) { | ||
charset += numberChars; | ||
} | ||
|
||
if (password.symbols) { | ||
charset += symbolChars; | ||
} | ||
} | ||
|
||
if (password.password_type === 'pin') { | ||
charset += numberChars; | ||
} | ||
|
||
let generatedValue = ''; | ||
|
||
for (let i = 0; i < password.length; i++) { | ||
const randomIndex = Math.floor(Math.random() * charset.length); | ||
generatedValue += charset[randomIndex]; | ||
} | ||
|
||
setPassword({ ...password, value: generatedValue }); | ||
}; | ||
|
||
useEffect(() => { | ||
generatePassword(); | ||
}, [ | ||
password.length, | ||
password.numbers, | ||
password.password_type, | ||
password.symbols, | ||
]); | ||
|
||
return { | ||
passwordInputRef, | ||
password, | ||
setPassword, | ||
copyPasswordToClipboard, | ||
generatePassword, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
0a713c3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
sink – ./packages/kitchen-sink
sink-millionjs.vercel.app
sink-git-main-millionjs.vercel.app
million-kitchen-sink-atit.vercel.app
sink.million.dev
0a713c3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
million-kitchen-sink – ./packages/kitchen-sink
million-kitchen-sink.vercel.app
million-kitchen-sink-git-main-millionjs.vercel.app
million-kitchen-sink-millionjs.vercel.app