// A text field of the device sheet. Java takes a field's text when it loses // the focus or Enter is pressed, and colours a text its rule refuses red // while it is typed. A refused text stays here, marked, with what the device // keeps meanwhile, instead of being replaced without a word as Java does; // Escape gives it up. A value the device already holds that the rule // refuses (from a file, or written by someone else) is marked, never mended // (plan I6). import { useId, useState, type KeyboardEvent } from 'react'; import { checkField, type CheckedField, type FieldRule } from '@mdg-filius/doc'; import { useTranslate } from '../i18n/i18n.js'; export interface SheetFieldProps { readonly label: string; /** What the device holds. */ readonly value: string; readonly rule: FieldRule; /** Gives the text to the device; says whether it was taken. */ readonly onCommit: (text: string) => CheckedField; readonly disabled?: boolean; readonly readOnly?: boolean; /** A name for the browser tests and the label's input mode. */ readonly name: string; readonly inputMode?: 'text' | 'decimal' | 'numeric'; /** A field still to be filled (a new row's cell): empty and untouched, it is not marked. */ readonly unfilled?: boolean; } export function SheetField({ label, value, rule, onCommit, disabled = false, readOnly = false, name, inputMode = 'text', unfilled = false }: SheetFieldProps) { const t = useTranslate(); const id = useId(); // What is typed and not yet taken; null shows the device's value. const [draft, setDraft] = useState(null); const [refused, setRefused] = useState(false); const shown = draft ?? value; const marked = !readOnly && !(unfilled && draft === null && shown === '') && 'invalid' in checkField(rule, shown); const commit = () => { if (draft === null || readOnly) return; if (draft === value) { setDraft(null); setRefused(false); return; } const result = onCommit(draft); if ('value' in result) { setDraft(null); setRefused(false); } else { setRefused(true); } }; const key = (event: KeyboardEvent) => { if (event.key === 'Enter') commit(); if (event.key === 'Escape') { setDraft(null); setRefused(false); } }; const hint = refused && draft !== null ? t('sheet.notTaken', { value: value === '' ? t('sheet.empty') : value }) : draft === null && marked ? t('sheet.invalidValue') : null; return ( ); } export function SheetCheck({ label, checked, onChange, disabled = false, name, }: { readonly label: string; readonly checked: boolean; readonly onChange: (checked: boolean) => void; readonly disabled?: boolean; readonly name: string; }) { return ( ); }