Added validation loss

This commit is contained in:
Jaret Burkett
2026-07-19 19:33:45 -06:00
parent cd677c70b5
commit 1eb97b7443
7 changed files with 392 additions and 16 deletions

View File

@@ -7,9 +7,52 @@ interface CardProps {
children?: React.ReactNode;
collapsible?: boolean;
defaultOpen?: boolean;
// when provided, the card is opened/closed by a toggle switch on the right instead of a chevron
toggled?: boolean;
onToggle?: (value: boolean) => void;
}
const Card: React.FC<CardProps> = ({ title, children, collapsible, defaultOpen }) => {
const Card: React.FC<CardProps> = ({ title, children, collapsible, defaultOpen, toggled, onToggle }) => {
if (onToggle) {
return (
<section className="space-y-2 px-4 pb-2 pt-2 bg-gray-900 rounded-lg">
<div className="flex items-center justify-between">
<div className="flex-1">
{title && (
<h2 className={classNames('text-lg font-semibold uppercase text-gray-500', toggled ? 'mb-2' : 'mb-0')}>
{title}
</h2>
)}
</div>
<button
type="button"
role="switch"
aria-checked={toggled}
onClick={() => onToggle(!toggled)}
className={classNames(
'relative ml-2 inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2',
toggled ? 'bg-blue-500' : 'bg-gray-600',
'hover:bg-opacity-80',
)}
>
<span className="sr-only">Toggle {title}</span>
<span
className={classNames(
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out',
toggled ? 'translate-x-5' : 'translate-x-0',
)}
/>
</button>
</div>
{toggled && (
<>
{children ?? null}
<div className="pt-2"></div>
</>
)}
</section>
);
}
if (collapsible) {
return (
<Disclosure as="section" className="space-y-2 px-4 pb-2 pt-2 bg-gray-900 rounded-lg" defaultOpen={defaultOpen}>

View File

@@ -198,18 +198,26 @@ export const NumberInput = (props: NumberInputProps) => {
const numValue = Number(rawValue);
// Only apply constraints and call onChange when we have a valid number
// don't clamp to min/max while typing, it mangles partial input (typing 1024 with
// min 64 becomes 64024). Clamping happens on blur.
if (!isNaN(numValue)) {
let constrainedValue = numValue;
// Apply min/max constraints if they exist
if (min !== undefined && constrainedValue < min) {
constrainedValue = min;
}
if (max !== undefined && constrainedValue > max) {
constrainedValue = max;
}
onChange(numValue);
}
}}
onBlur={() => {
const numValue = Number(inputValue);
if (inputValue === '' || isNaN(numValue)) {
return;
}
let constrainedValue = numValue;
if (min !== undefined && constrainedValue < min) {
constrainedValue = min;
}
if (max !== undefined && constrainedValue > max) {
constrainedValue = max;
}
if (constrainedValue !== numValue) {
setInputValue(constrainedValue);
onChange(constrainedValue);
}
}}