Compare commits
No commits in common. "79467e29f23730994c5012c1773d35eed7f022ab" and "9b52470f6b8c5321991396d34ea28edfb4c31960" have entirely different histories.
79467e29f2
...
9b52470f6b
3 changed files with 7 additions and 67 deletions
|
@ -10,7 +10,6 @@ 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,
|
||||||
|
@ -18,7 +17,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<bigint>();
|
const [id, setId] = useState<number>();
|
||||||
const [algorithm, setAlgorithm] = useState('wilson');
|
const [algorithm, setAlgorithm] = useState('wilson');
|
||||||
|
|
||||||
const handleSubmit = (e: FormEvent) => {
|
const handleSubmit = (e: FormEvent) => {
|
||||||
|
@ -55,14 +54,15 @@ export default function InputForm({state, dispatch}: {
|
||||||
value: numberValue
|
value: numberValue
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const validateIdInput: ValidatorFunction<string, bigint> = value => {
|
const validateIdInput: ValidatorFunction<string, number> = value => {
|
||||||
if ("" === value) {
|
if ("" === value) {
|
||||||
return {
|
return {
|
||||||
valid: true
|
valid: true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const numberValue = BigInt(value);
|
const numberValue = Number(value);
|
||||||
if (numberValue.toString() !== value.trim()) {
|
// FIXME doesn't handle strings with characters correctly (e.g. "asdf" yields an empty value, due to "type=number").
|
||||||
|
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}
|
||||||
/>
|
/>
|
||||||
<ValidatingInputBigIntField id={"id"}
|
<ValidatingInputNumberField id={"id"}
|
||||||
label={"ID (optional)"}
|
label={"ID (optional)"}
|
||||||
value={id}
|
value={id}
|
||||||
validatorFn={validateIdInput}
|
validatorFn={validateIdInput}
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
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": "ES2020",
|
"target": "ES2017",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue