Avoid pointless React namespace

This commit is contained in:
Quantum 2025-02-10 13:34:32 -05:00
parent 8e02568a43
commit bd2166e825
7 changed files with 44 additions and 44 deletions

View file

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

View file

@ -1,4 +1,4 @@
import React, {useState} from 'react'; import React, {useCallback, useEffect, useMemo, useState} from 'react';
import base32Decode from 'base32-decode'; import base32Decode from 'base32-decode';
import {NumberInput, TextInput} from './Input'; import {NumberInput, TextInput} from './Input';
import OTPOutput from './OTPOutput'; import OTPOutput from './OTPOutput';
@ -18,12 +18,12 @@ function parseState() {
function App() { function App() {
const [advanced, setAdvanced] = useState(false); const [advanced, setAdvanced] = useState(false);
const [state, setState] = React.useState(() => parseState() || defaults); const [state, setState] = useState(() => parseState() || defaults);
const {secret, step, digits, algorithm} = state; const {secret, step, digits, algorithm} = state;
const [offset, setOffset] = React.useState(0); const [offset, setOffset] = useState(0);
const [remaining, setRemaining] = React.useState(0); const [remaining, setRemaining] = useState(0);
const [validSecret, decoded] = React.useMemo(() => { const [validSecret, decoded] = useMemo(() => {
try { try {
return [true, base32Decode(secret.toUpperCase(), 'RFC4648')]; return [true, base32Decode(secret.toUpperCase(), 'RFC4648')];
} catch (e) { } catch (e) {
@ -35,13 +35,13 @@ function App() {
const validDigits = digits > 0 && digits <= 10; const validDigits = digits > 0 && digits <= 10;
const valid = validSecret && validStep && validDigits && !!secret; const valid = validSecret && validStep && validDigits && !!secret;
React.useEffect(() => { useEffect(() => {
if (!validStep) return; if (!validStep) return;
const now = Date.now(); const now = Date.now();
setOffset(Math.floor(now / (1000 * step))); setOffset(Math.floor(now / (1000 * step)));
}, [validStep, step]); }, [validStep, step]);
React.useEffect(() => { useEffect(() => {
if (!validStep) return; if (!validStep) return;
const now = Date.now(); const now = Date.now();
const nextUpdate = Math.floor(now / 1000) * 1000 + 1000; const nextUpdate = Math.floor(now / 1000) * 1000 + 1000;
@ -55,50 +55,50 @@ function App() {
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, [validStep, step, offset, remaining]); }, [validStep, step, offset, remaining]);
const showAdvanced = React.useCallback(() => { const showAdvanced = useCallback(() => {
setAdvanced(true); setAdvanced(true);
}, []); }, []);
const hideAdvanced = React.useCallback(() => { const hideAdvanced = useCallback(() => {
setAdvanced(false); setAdvanced(false);
}, []); }, []);
const setSecret = React.useCallback( const setSecret = useCallback(
(secret: string) => setState((state) => ({...state, secret})), (secret: string) => setState((state) => ({...state, secret})),
[], [],
); );
const setStep = React.useCallback( const setStep = useCallback(
(step: number) => setState((state) => ({...state, step})), (step: number) => setState((state) => ({...state, step})),
[], [],
); );
const setDigits = React.useCallback( const setDigits = useCallback(
(digits: number) => setState((state) => ({...state, digits})), (digits: number) => setState((state) => ({...state, digits})),
[], [],
); );
const setAlgorithm = React.useCallback( const setAlgorithm = useCallback(
(algorithm: string) => setState((state) => ({...state, algorithm})), (algorithm: string) => setState((state) => ({...state, algorithm})),
[], [],
); );
const onReset = React.useCallback( const onReset = useCallback(
() => setState((state) => ({...state, step: 30, digits: 6, algorithm: 'sha1'})), () => setState((state) => ({...state, step: 30, digits: 6, algorithm: 'sha1'})),
[], [],
); );
React.useEffect(() => { useEffect(() => {
const value = serializeState(state); const value = serializeState(state);
history.replaceState(null, '', window.location.pathname + window.location.search + (value && `#!${value}`)); history.replaceState(null, '', window.location.pathname + window.location.search + (value && `#!${value}`));
}, [state]); }, [state]);
const onHashChange = React.useCallback(() => { const onHashChange = useCallback(() => {
const state = parseState(); const state = parseState();
state && setState(state); state && setState(state);
}, []); }, []);
React.useEffect(() => { useEffect(() => {
window.addEventListener('hashchange', onHashChange); window.addEventListener('hashchange', onHashChange);
return () => window.removeEventListener('hashchange', onHashChange); return () => window.removeEventListener('hashchange', onHashChange);
}, [onHashChange]); }, [onHashChange]);

View file

@ -1,10 +1,10 @@
import React from 'react'; import React, {ReactNode, useEffect, useId} from 'react';
import {Collapse} from 'bootstrap'; import {Collapse} from 'bootstrap';
export default function Collapsible({children, show}: { children: React.ReactNode; show: boolean }) { export default function Collapsible({children, show}: { children: ReactNode; show: boolean }) {
const id = React.useId(); const id = useId();
React.useEffect(() => { useEffect(() => {
const collapse = Collapse.getOrCreateInstance(`#${id}`, {toggle: show}); const collapse = Collapse.getOrCreateInstance(`#${id}`, {toggle: show});
if (show) if (show)
collapse.show(); collapse.show();

View file

@ -1,12 +1,12 @@
import React from 'react'; import React, {useCallback, useEffect, useId, useState} from 'react';
import Copy from './copy.svg?react'; import Copy from './copy.svg?react';
import {Popover} from 'bootstrap'; import {Popover} from 'bootstrap';
export default function CopyButton({text}: { text: string }) { export default function CopyButton({text}: { text: string }) {
const id = React.useId(); const id = useId();
const [popover, setPopover] = React.useState(false); const [popover, setPopover] = useState(false);
React.useEffect(() => { useEffect(() => {
if (!popover) return; if (!popover) return;
const instance = Popover.getOrCreateInstance(`#${id}`, { const instance = Popover.getOrCreateInstance(`#${id}`, {
trigger: 'manual', trigger: 'manual',
@ -20,7 +20,7 @@ export default function CopyButton({text}: { text: string }) {
}, 1000); }, 1000);
}, [id, popover]); }, [id, popover]);
const onCopy = React.useCallback(async () => { const onCopy = useCallback(async () => {
try { try {
await navigator.clipboard.writeText(text); await navigator.clipboard.writeText(text);
setPopover(true); setPopover(true);

View file

@ -1,9 +1,9 @@
import React from 'react'; import React, {ReactNode, HTMLProps, useCallback, useId} from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
type CommonProps = { type CommonProps = {
label: React.ReactNode; label: ReactNode;
error?: React.ReactNode; error?: ReactNode;
}; };
type TextInputProps = { type TextInputProps = {
@ -11,12 +11,12 @@ type TextInputProps = {
onChange: (value: string) => void; onChange: (value: string) => void;
} }
type HTMLInputProps = Omit<React.HTMLProps<HTMLInputElement>, 'onChange' | 'label'>; type HTMLInputProps = Omit<HTMLProps<HTMLInputElement>, 'onChange' | 'label'>;
function BaseInput({label, value, onChange, error, ...props}: CommonProps & HTMLInputProps & TextInputProps) { function BaseInput({label, value, onChange, error, ...props}: CommonProps & HTMLInputProps & TextInputProps) {
const id = React.useId(); const id = useId();
const handleChange = React.useCallback( const handleChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => onChange(e.target.value), (e: ChangeEvent<HTMLInputElement>) => onChange(e.target.value),
[onChange], [onChange],
); );
@ -38,6 +38,6 @@ export function NumberInput({value, onChange, ...props}: CommonProps & {
min?: number; min?: number;
max?: number; max?: number;
}) { }) {
const handleChange = React.useCallback((value: string) => onChange(+value), [onChange]); const handleChange = useCallback((value: string) => onChange(+value), [onChange]);
return <BaseInput type="number" value={`${value}`} onChange={handleChange} {...props}/>; return <BaseInput type="number" value={`${value}`} onChange={handleChange} {...props}/>;
} }

View file

@ -1,4 +1,4 @@
import React from 'react'; import React, {useMemo} from 'react';
import {HOTP, HOTPOptions} from '@otplib/core'; import {HOTP, HOTPOptions} from '@otplib/core';
import {createDigest} from '@otplib/plugin-crypto-js'; import {createDigest} from '@otplib/plugin-crypto-js';
import classNames from 'classnames'; import classNames from 'classnames';
@ -25,7 +25,7 @@ export default function OTPOutput({secret, offset, algorithm, digits}: {
algorithm: HashAlgorithm; algorithm: HashAlgorithm;
digits: number; digits: number;
}) { }) {
const otp = React.useMemo(() => new HOTP<HOTPOptions>({ const otp = useMemo(() => new HOTP<HOTPOptions>({
createDigest, createDigest,
digits, digits,
algorithm: ALGORITHMS[algorithm], algorithm: ALGORITHMS[algorithm],

View file

@ -1,14 +1,14 @@
import React from 'react'; import React, {ChangeEvent, ReactNode, useCallback, useId} from 'react';
export default function Select<T extends string>({label, options, value, onChange}: { export default function Select<T extends string>({label, options, value, onChange}: {
label: React.ReactNode; label: ReactNode;
options: Record<T, string>; options: Record<T, string>;
value: T; value: T;
onChange: (value: T) => void; onChange: (value: T) => void;
}) { }) {
const id = React.useId(); const id = useId();
const handleChange = React.useCallback( const handleChange = useCallback(
(e: React.ChangeEvent<HTMLSelectElement>) => onChange(e.target.value as T), (e: ChangeEvent<HTMLSelectElement>) => onChange(e.target.value as T),
[onChange], [onChange],
); );