totp/src/Input.tsx

44 lines
1.4 KiB
TypeScript
Raw Normal View History

2025-02-10 13:34:32 -05:00
import React, {ReactNode, HTMLProps, useCallback, useId} from 'react';
2024-04-07 00:13:19 -04:00
import classNames from 'classnames';
2024-04-06 22:54:44 -04:00
2024-04-07 00:13:19 -04:00
type CommonProps = {
2025-02-10 13:34:32 -05:00
label: ReactNode;
error?: ReactNode;
2024-04-07 00:13:19 -04:00
};
type TextInputProps = {
2024-04-06 22:54:44 -04:00
value: string;
onChange: (value: string) => void;
2024-04-07 00:13:19 -04:00
}
2025-02-10 13:34:32 -05:00
type HTMLInputProps = Omit<HTMLProps<HTMLInputElement>, 'onChange' | 'label'>;
2024-04-07 00:13:19 -04:00
function BaseInput({label, value, onChange, error, ...props}: CommonProps & HTMLInputProps & TextInputProps) {
2025-02-10 13:34:32 -05:00
const id = useId();
const handleChange = useCallback(
(e: ChangeEvent<HTMLInputElement>) => onChange(e.target.value),
2024-04-06 22:54:44 -04:00
[onChange],
);
2024-04-07 00:13:19 -04:00
return <div className={classNames('totp-input', {'has-validation': !!error})}>
2024-04-06 22:54:44 -04:00
<label htmlFor={id} className="form-label">{label}</label>
2024-04-07 00:13:19 -04:00
<input id={id} className={classNames('form-control', {'is-invalid': !!error})}
value={value} onChange={handleChange} {...props}/>
{error && <div className="invalid-feedback">{error}</div>}
2024-04-06 22:54:44 -04:00
</div>;
}
2024-04-07 00:13:19 -04:00
export function TextInput(props: CommonProps & TextInputProps) {
return <BaseInput {...props} />;
}
export function NumberInput({value, onChange, ...props}: CommonProps & {
value: number;
onChange: (value: number) => void;
min?: number;
max?: number;
}) {
2025-02-10 13:34:32 -05:00
const handleChange = useCallback((value: string) => onChange(+value), [onChange]);
2024-04-07 00:13:19 -04:00
return <BaseInput type="number" value={`${value}`} onChange={handleChange} {...props}/>;
}