#11: Fix ID input.
This commit is contained in:
parent
9b52470f6b
commit
0dc5c28060
3 changed files with 67 additions and 7 deletions
|
@ -10,6 +10,7 @@ import {
|
||||||
import styles from "./input-form.module.css";
|
import styles from "./input-form.module.css";
|
||||||
import "./input-form.css";
|
import "./input-form.css";
|
||||||
import {State} from "@/app/state/state.ts";
|
import {State} from "@/app/state/state.ts";
|
||||||
|
import ValidatingInputBigIntField from "@/app/validating-input-bigint-field.tsx";
|
||||||
|
|
||||||
export default function InputForm({state, dispatch}: {
|
export default function InputForm({state, dispatch}: {
|
||||||
state: State,
|
state: State,
|
||||||
|
@ -17,7 +18,7 @@ export default function InputForm({state, dispatch}: {
|
||||||
}) {
|
}) {
|
||||||
const [width, setWidth] = useState(10);
|
const [width, setWidth] = useState(10);
|
||||||
const [height, setHeight] = useState(10);
|
const [height, setHeight] = useState(10);
|
||||||
const [id, setId] = useState<number>();
|
const [id, setId] = useState<bigint>();
|
||||||
const [algorithm, setAlgorithm] = useState('wilson');
|
const [algorithm, setAlgorithm] = useState('wilson');
|
||||||
|
|
||||||
const handleSubmit = (e: FormEvent) => {
|
const handleSubmit = (e: FormEvent) => {
|
||||||
|
@ -54,15 +55,14 @@ export default function InputForm({state, dispatch}: {
|
||||||
value: numberValue
|
value: numberValue
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const validateIdInput: ValidatorFunction<string, number> = value => {
|
const validateIdInput: ValidatorFunction<string, bigint> = value => {
|
||||||
if ("" === value) {
|
if ("" === value) {
|
||||||
return {
|
return {
|
||||||
valid: true
|
valid: true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const numberValue = Number(value);
|
const numberValue = BigInt(value);
|
||||||
// FIXME doesn't handle strings with characters correctly (e.g. "asdf" yields an empty value, due to "type=number").
|
if (numberValue.toString() !== value.trim()) {
|
||||||
if (isNaN(numberValue) || Math.floor(numberValue) !== numberValue) {
|
|
||||||
return {
|
return {
|
||||||
valid: false,
|
valid: false,
|
||||||
message: "Must be empty or an integer"
|
message: "Must be empty or an integer"
|
||||||
|
@ -96,7 +96,7 @@ export default function InputForm({state, dispatch}: {
|
||||||
disabled={state.loading}
|
disabled={state.loading}
|
||||||
onChange={setHeight}
|
onChange={setHeight}
|
||||||
/>
|
/>
|
||||||
<ValidatingInputNumberField id={"id"}
|
<ValidatingInputBigIntField id={"id"}
|
||||||
label={"ID (optional)"}
|
label={"ID (optional)"}
|
||||||
value={id}
|
value={id}
|
||||||
validatorFn={validateIdInput}
|
validatorFn={validateIdInput}
|
||||||
|
|
60
src/app/validating-input-bigint-field.tsx
Normal file
60
src/app/validating-input-bigint-field.tsx
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import React, {ChangeEventHandler, useState} from 'react';
|
||||||
|
|
||||||
|
export default function ValidatingInputBigIntField({
|
||||||
|
id,
|
||||||
|
label,
|
||||||
|
value = 0n,
|
||||||
|
constraints = undefined,
|
||||||
|
validatorFn = (value) => {
|
||||||
|
return {valid: true, value: BigInt(value)};
|
||||||
|
},
|
||||||
|
disabled = false,
|
||||||
|
onChange = () => {
|
||||||
|
}
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
value?: bigint;
|
||||||
|
constraints?: { min?: bigint; max?: bigint; };
|
||||||
|
validatorFn: ValidatorFunction<string, bigint>;
|
||||||
|
disabled: boolean;
|
||||||
|
onChange: (v: bigint) => void;
|
||||||
|
}) {
|
||||||
|
const [error, setError] = useState<string>();
|
||||||
|
|
||||||
|
const handleValueChange: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
const validation = validatorFn(value);
|
||||||
|
if (!validation.valid) {
|
||||||
|
setError(validation.message);
|
||||||
|
} else {
|
||||||
|
setError(undefined);
|
||||||
|
}
|
||||||
|
if (validation.value !== undefined) {
|
||||||
|
onChange(validation.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<label htmlFor={id}>{label}: </label>
|
||||||
|
<input id={id}
|
||||||
|
type={"number"}
|
||||||
|
onChange={handleValueChange}
|
||||||
|
value={value?.toString() || ""}
|
||||||
|
min={constraints?.min?.toString()}
|
||||||
|
max={constraints?.max?.toString()}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
<span>{error}</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Validation<T> {
|
||||||
|
valid: boolean;
|
||||||
|
message?: string;
|
||||||
|
value?: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ValidatorFunction<I, T> = (v: I) => Validation<T>;
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2017",
|
"target": "ES2020",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
|
|
Loading…
Reference in a new issue