Add advanced option collapsing

This commit is contained in:
Quantum 2024-04-07 01:45:31 -04:00
parent 4f87a59307
commit 9c6cdf6ed3
6 changed files with 88 additions and 11 deletions

10
package-lock.json generated
View file

@ -17,6 +17,7 @@
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/bootstrap": "^5.2.10",
"@types/node": "^20.12.5",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
@ -1302,6 +1303,15 @@
"@babel/types": "^7.20.7"
}
},
"node_modules/@types/bootstrap": {
"version": "5.2.10",
"resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.10.tgz",
"integrity": "sha512-F2X+cd6551tep0MvVZ6nM8v7XgGN/twpdNDjqS1TUM7YFNEtQYWk+dKAnH+T1gr6QgCoGMPl487xw/9hXooa2g==",
"dev": true,
"dependencies": {
"@popperjs/core": "^2.9.2"
}
},
"node_modules/@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",

View file

@ -19,6 +19,7 @@
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/bootstrap": "^5.2.10",
"@types/node": "^20.12.5",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",

12
src/ActionLink.tsx Normal file
View file

@ -0,0 +1,12 @@
import React from 'react';
type HTMLAnchorProps = Omit<React.HTMLProps<HTMLAnchorElement>, 'href' | 'onClick'>;
export default function ActionLink({onClick, className, ...props}: HTMLAnchorProps & { onClick: () => void }) {
const handleClick = React.useCallback((e: React.SyntheticEvent) => {
e.preventDefault();
onClick();
}, [onClick]);
return <a className={`totp-action-link ${className}`} onClick={handleClick} {...props} />;
}

View file

@ -1,9 +1,13 @@
import React from 'react';
import React, {useState} from 'react';
import {NumberInput, TextInput} from './Input';
import OTPOutput, {HashAlgorithm} from './OTPOutput';
import Select from './Select';
import Collapsible from './Collapsible';
import ActionLink from './ActionLink.tsx';
function App() {
const [advanced, setAdvanced] = useState(false);
const [secret, setSecret] = React.useState('');
const [step, setStep] = React.useState(30);
const [offset, setOffset] = React.useState(0);
@ -29,6 +33,14 @@ function App() {
return () => clearTimeout(timer);
}, [validStep, offset, step]);
const showAdvanced = React.useCallback(() => {
setAdvanced(true);
}, []);
const hideAdvanced = React.useCallback(() => {
setAdvanced(false);
}, []);
const onReset = React.useCallback(() => {
setStep(30);
setDigits(6);
@ -39,6 +51,10 @@ function App() {
<div className="totp-app">
<div className="totp-settings">
<TextInput label="Secret key" value={secret} onChange={setSecret}/>
{advanced ?
<ActionLink onClick={hideAdvanced}>Hide advanced options</ActionLink> :
<ActionLink onClick={showAdvanced}>Show advanced options</ActionLink>}
<Collapsible show={advanced}>
<NumberInput label="Time step" value={step} onChange={setStep} min={1}
error={!validStep && 'You must enter an integer time step ≥ 1 second'}/>
<NumberInput label="Code digits" value={digits} onChange={setDigits} min={1} max={10}
@ -49,6 +65,7 @@ function App() {
sha512: 'SHA-512',
}}/>
<button type="button" className="btn btn-secondary" onClick={onReset}>Reset</button>
</Collapsible>
</div>
{valid && <OTPOutput secret={secret} offset={offset} algorithm={algorithm} digits={digits}/>}
</div>

28
src/Collapsible.tsx Normal file
View file

@ -0,0 +1,28 @@
import React from 'react';
import {Collapse} from 'bootstrap';
import classNames from 'classnames';
export interface CollapsibleHandle {
show: () => void;
hide: () => void;
toggle: () => void;
}
export default function Collapsible({children, show}: { children: React.ReactNode; show: boolean }) {
const collapse = React.useRef<Collapse>();
const onLoad = React.useCallback((element: HTMLDivElement) => {
collapse.current = new Collapse(element, {toggle: show});
}, []);
React.useEffect(() => {
if (show)
collapse.current?.show();
else
collapse.current?.hide();
}, [show]);
return <div ref={onLoad} className={classNames('collapse', {show})}>
{children}
</div>;
}

View file

@ -35,6 +35,10 @@ nav {
}
}
.totp-settings {
margin-bottom: 0.5em;
}
@include media-breakpoint-up(sm) {
.totp-app {
@include make-row();
@ -51,6 +55,11 @@ nav {
}
}
.totp-action-link {
cursor: pointer;
user-select: none;
}
.totp-input, .totp-select {
label {
font-weight: $font-weight-bold;