Unify preset/object GUI as collapsible cards; add NumberField for reliable numeric editing
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
interface NumberFieldProps {
|
||||
value: number;
|
||||
onChange: (value: number) => void;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
width?: number | string;
|
||||
className?: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export function NumberField({ value, onChange, min, max, step, width, className, title }: NumberFieldProps) {
|
||||
const [text, setText] = useState<string>(String(value));
|
||||
|
||||
useEffect(() => {
|
||||
setText(String(value));
|
||||
}, [value]);
|
||||
|
||||
const clamp = (n: number) => {
|
||||
if (min !== undefined) n = Math.max(min, n);
|
||||
if (max !== undefined) n = Math.min(max, n);
|
||||
return n;
|
||||
};
|
||||
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
inputMode="numeric"
|
||||
className={className}
|
||||
title={title}
|
||||
style={width ? { width } : undefined}
|
||||
value={text}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
onChange={(e) => {
|
||||
const raw = e.target.value;
|
||||
if (!/^-?\d*$/.test(raw)) return;
|
||||
setText(raw);
|
||||
if (raw === '' || raw === '-') return;
|
||||
const parsed = Number(raw);
|
||||
if (!Number.isNaN(parsed)) {
|
||||
const next = clamp(parsed);
|
||||
if (next !== value) onChange(next);
|
||||
}
|
||||
}}
|
||||
onBlur={() => {
|
||||
if (text === '' || text === '-') {
|
||||
const fallback = clamp(value);
|
||||
onChange(fallback);
|
||||
setText(String(fallback));
|
||||
} else {
|
||||
setText(String(value));
|
||||
}
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'ArrowUp' && step) {
|
||||
e.preventDefault();
|
||||
const next = clamp(value + step);
|
||||
onChange(next);
|
||||
setText(String(next));
|
||||
}
|
||||
if (e.key === 'ArrowDown' && step) {
|
||||
e.preventDefault();
|
||||
const next = clamp(value - step);
|
||||
onChange(next);
|
||||
setText(String(next));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user