Tofu/app/components/atoms/TextField.js
2026-09-18 01:41:02 +02:00

46 lines
1.4 KiB
JavaScript

import { html } from 'htm/preact'
// A labelled text input. The <label> is always rendered and visible, and it is
// always tied to the input by `for`/`id`: an aria-label is never used instead.
// `onInput` emits the string, never the event, so input and output share one format.
// `children` is grafted inside .field-control, right after the input: that is where
// a suggestion list is mounted so it sits under the field it belongs to.
// Remaining props land on the input itself, which is how a combobox molecule adds
// its ARIA (role, aria-expanded, aria-controls, aria-activedescendant) and its
// keyboard handlers without the atom knowing anything about suggestions.
export function TextField({
id,
label,
value = '',
onInput,
placeholder,
type = 'text',
autocomplete,
inputMode,
required,
describedBy,
children,
...inputProps
}) {
return html`
<div class="field">
<label class="field-label" for=${id}>${label}</label>
<div class="field-control">
<input
class="field-input"
id=${id}
type=${type}
value=${value}
placeholder=${placeholder}
autocomplete=${autocomplete}
inputMode=${inputMode}
required=${required}
aria-describedby=${describedBy}
onInput=${(event) => onInput(event.target.value)}
...${inputProps}
/>
${children}
</div>
</div>
`
}