mirror of
https://github.com/quantum5/totp.git
synced 2025-04-24 05:31:59 -04:00
Add basic styling
This commit is contained in:
parent
d8636039a9
commit
4f87a59307
30
index.html
30
index.html
|
@ -4,10 +4,38 @@
|
|||
<meta charset="UTF-8"/>
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Vite + React + TS</title>
|
||||
<title>TOTP.lol — A TOTP Code Generator for Developers</title>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="#">TOTP.lol</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
|
||||
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#what-is-this">What is this?</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#about">About</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="container">
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div class="jumbotron">
|
||||
<p class="lead">This is a code generator for the Time-based One Time Password (TOTP) algorithm.</p>
|
||||
<hr>
|
||||
<div id="root"></div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -11,7 +11,7 @@ function App() {
|
|||
const [algorithm, setAlgorithm] = React.useState<HashAlgorithm>('sha1');
|
||||
|
||||
const validStep = step > 0;
|
||||
const validDigits = digits > 0 && digits < 10;
|
||||
const validDigits = digits > 0 && digits <= 10;
|
||||
const valid = validStep && validDigits;
|
||||
|
||||
React.useEffect(() => {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import {HashAlgorithms, HOTP, HOTPOptions} from '@otplib/core';
|
||||
import {createDigest} from '@otplib/plugin-crypto-js';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const ALGORITHMS = {
|
||||
sha1: HashAlgorithms.SHA1,
|
||||
|
@ -10,8 +11,13 @@ const ALGORITHMS = {
|
|||
|
||||
export type HashAlgorithm = keyof typeof ALGORITHMS;
|
||||
|
||||
function OTPCode({code}: { code: string }) {
|
||||
return <div className="totp-code">
|
||||
function OTPCode({code, delta}: { code: string; delta: number }) {
|
||||
return <div className={classNames('totp-code', {
|
||||
'totp-older': delta < 0,
|
||||
'totp-newer': delta > 0,
|
||||
'totp-current': delta === 0,
|
||||
'totp-far': Math.abs(delta) > 5,
|
||||
})}>
|
||||
{code}
|
||||
</div>;
|
||||
}
|
||||
|
@ -22,7 +28,7 @@ export default function OTPOutput({secret, offset, algorithm, digits}: {
|
|||
algorithm: HashAlgorithm;
|
||||
digits: number;
|
||||
}) {
|
||||
const hotp = React.useMemo(() => new HOTP<HOTPOptions>({
|
||||
const otp = React.useMemo(() => new HOTP<HOTPOptions>({
|
||||
createDigest,
|
||||
digits,
|
||||
algorithm: ALGORITHMS[algorithm],
|
||||
|
@ -30,8 +36,9 @@ export default function OTPOutput({secret, offset, algorithm, digits}: {
|
|||
|
||||
return <div className="totp-output">
|
||||
{[...Array(21).keys()].map((i) => {
|
||||
const current = offset + i - 10;
|
||||
return <OTPCode key={current} code={hotp.generate(secret, current)}/>;
|
||||
const delta = i - 10;
|
||||
const current = offset + delta;
|
||||
return <OTPCode key={current} code={otp.generate(secret, current)} delta={delta}/>;
|
||||
})}
|
||||
</div>;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,76 @@
|
|||
@import 'bootstrap/scss/_functions.scss';
|
||||
@import 'bootstrap/scss/_variables.scss';
|
||||
@import 'bootstrap/scss/_variables-dark.scss';
|
||||
@import 'bootstrap/scss/_maps.scss';
|
||||
@import 'bootstrap/scss/_mixins.scss';
|
||||
@import 'bootstrap/scss/_utilities.scss';
|
||||
|
||||
@import 'bootstrap/scss/_root.scss';
|
||||
@import 'bootstrap/scss/_reboot.scss';
|
||||
@import 'bootstrap/scss/_grid.scss';
|
||||
@import 'bootstrap/scss/_containers.scss';
|
||||
@import 'bootstrap/scss/_navbar.scss';
|
||||
@import 'bootstrap/scss/_nav.scss';
|
||||
@import 'bootstrap/scss/_dropdown.scss';
|
||||
@import 'bootstrap/scss/_buttons.scss';
|
||||
@import 'bootstrap/scss/_forms.scss';
|
||||
@import 'bootstrap/scss/_type.scss';
|
||||
@import 'bootstrap/scss/_helpers.scss';
|
||||
@import 'bootstrap/scss/_transitions.scss';
|
||||
|
||||
nav {
|
||||
background-color: $body-tertiary-bg;
|
||||
}
|
||||
|
||||
.jumbotron {
|
||||
margin: 1em 0;
|
||||
border-radius: 1em;
|
||||
padding: 1em;
|
||||
background-color: $body-secondary-bg;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
.jumbotron {
|
||||
padding: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
.totp-app {
|
||||
@include make-row();
|
||||
}
|
||||
|
||||
.totp-settings {
|
||||
@include make-col-ready();
|
||||
@include make-col($size: 1, $columns: 2);
|
||||
}
|
||||
|
||||
.totp-output {
|
||||
@include make-col-ready();
|
||||
@include make-col($size: 1, $columns: 2);
|
||||
}
|
||||
}
|
||||
|
||||
.totp-input, .totp-select {
|
||||
label {
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.totp-code {
|
||||
text-align: center;
|
||||
height: 1.5em;
|
||||
max-height: 1.5em;
|
||||
transition: transform 0.5s linear, max-height 0.5s linear, font-size 0.5s linear;
|
||||
|
||||
&:first-child, &:last-child {
|
||||
max-height: 0;
|
||||
transform: scale(1, 0);
|
||||
}
|
||||
|
||||
&.totp-current {
|
||||
font-size: 2em;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App.tsx';
|
||||
import './index.scss';
|
||||
import 'bootstrap/js/src/collapse.js';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
|
|
Loading…
Reference in a new issue