UNPKG

@zeix/ui-element

Version:

UIElement - a HTML-first library for reactive Web Components

95 lines (86 loc) 2.23 kB
import { batch, type Component, component, computed, on, setAttribute, setProperty, setText, } from '../../..' import { clearEffects, clearMethod } from '../../functions/shared/clear-input' export type FormTextboxProps = { value: string length: number error: string description: string clear(): void } export default component<FormTextboxProps>( 'form-textbox', { value: '', length: 0, error: '', description: '', clear: clearMethod<HTMLInputElement | HTMLTextAreaElement>( 'input, textarea', ), }, (el, { first, useElement }) => { const input = useElement<HTMLInputElement | HTMLTextAreaElement>( 'input, textarea', 'Native input or textarea element needed.', ) // Initialize description with existing content or set up computed signal for remaining characters const description = el.querySelector<HTMLElement>('.description') if (description?.dataset.remaining && input.maxLength) { el.setSignal( 'description', computed(() => description.dataset.remaining!.replace( '${n}', String(input.maxLength - el.length), ), ), ) } else if (description?.textContent) { el.description = description.textContent.trim() } const errorId = el.querySelector('.error')?.id const descriptionId = description?.id return [ setAttribute('value'), // Effects on input / textarea first('input, textarea', [ setProperty('ariaInvalid', () => String(!!el.error)), setAttribute('aria-errormessage', () => el.error && errorId ? errorId : null, ), setAttribute('aria-describedby', () => el.description && descriptionId ? descriptionId : null, ), on('change', () => { input.checkValidity() batch(() => { el.value = input.value el.error = input.validationMessage ?? '' }) }), on('input', () => { el.length = input.value.length }), ]), // Effects and event listeners on clear button first('.clear', clearEffects(el)), // Effects on error and description first('.error', setText('error')), first('.description', setText('description')), ] }, ) declare global { interface HTMLElementTagNameMap { 'form-textbox': Component<FormTextboxProps> } }