diff --git a/packages/common/types/control.types.ts b/packages/common/types/control.types.ts index 2c6adb7..1e62922 100644 --- a/packages/common/types/control.types.ts +++ b/packages/common/types/control.types.ts @@ -1,4 +1,4 @@ -import type { ValidatorRules } from "./validator.types"; +import type { ValidationHooks, ValidatorRules } from "./validator.types"; /** * `ControlType` determines the type of form control @@ -37,6 +37,7 @@ export interface ControlBase { label?: string; placeholder?: string; validators?: ValidatorRules; + triggerValidationOn?: ValidationHooks; } export interface Checkbox extends ControlBase { diff --git a/packages/common/types/validator.types.ts b/packages/common/types/validator.types.ts index 0685a09..e199884 100644 --- a/packages/common/types/validator.types.ts +++ b/packages/common/types/validator.types.ts @@ -1,5 +1,7 @@ export type HookType = "onSubmit" | "onControlBlur" | "all"; +export type ValidationHooks = "" | "blur" | "keypress" | "click"; // More to be added + export type CategoryType = "error" | "warn" | "info"; export type ValidatorRules = diff --git a/packages/form/components/controls/Dropdown.astro b/packages/form/components/controls/Dropdown.astro index bd0a350..cf61438 100644 --- a/packages/form/components/controls/Dropdown.astro +++ b/packages/form/components/controls/Dropdown.astro @@ -6,7 +6,7 @@ import type { Dropdown, ControlOption } from '@astro-reactive/common'; export interface Props { control: Dropdown; - readOnly?: boolean; + readOnly?: boolean; } const { control, readOnly } = Astro.props; @@ -26,22 +26,20 @@ const options = control.options.map((option: string | ControlOption) => { name={control.name} id={control.id} disabled={readOnly || null} + data-validation-on={control.triggerValidationOn ? control.triggerValidationOn : null} > -{ - control?.placeholder && ( - - ) -} -{ - options.map((option: ControlOption) => ( - - )) -} + { + control?.placeholder && ( + + ) + } + { + options.map((option: ControlOption) => ( + + )) + } diff --git a/packages/form/components/controls/Input.astro b/packages/form/components/controls/Input.astro index 63b76dd..f8aa66b 100644 --- a/packages/form/components/controls/Input.astro +++ b/packages/form/components/controls/Input.astro @@ -46,6 +46,7 @@ const validatorAttributes: Record = validators.reduce((prev, val data-validator-error={hasError ? hasError.toString() : null} data-validator-warn={hasWarn ? hasWarn.toString() : null} data-validator-info={hasInfo ? hasInfo.toString() : null} + data-validation-on={control.triggerValidationOn ? control.triggerValidationOn : null} readonly={readOnly || null} disabled={(readOnly || null) && control.type === 'checkbox'} {...validatorAttributes} diff --git a/packages/form/components/controls/RadioGroup.astro b/packages/form/components/controls/RadioGroup.astro index ea90f94..25b2468 100644 --- a/packages/form/components/controls/RadioGroup.astro +++ b/packages/form/components/controls/RadioGroup.astro @@ -33,6 +33,7 @@ const options = control.options.map((option: string | ControlOption) => { checked={option.value === control.value} readonly={readOnly || null} disabled={readOnly || null} + data-validation-on={control.triggerValidationOn ? control.triggerValidationOn : null} /> diff --git a/packages/form/components/controls/TextArea.astro b/packages/form/components/controls/TextArea.astro index bc0741c..15ff124 100644 --- a/packages/form/components/controls/TextArea.astro +++ b/packages/form/components/controls/TextArea.astro @@ -38,6 +38,7 @@ const validatorAttributes: Record = validators.reduce((prev, val cols={control?.cols ?? 21} data-label={control?.label} readonly={readOnly || null} + data-validation-on={control.triggerValidationOn ? control.triggerValidationOn : null} {...validatorAttributes} > {control.value} diff --git a/packages/form/core/form-control.ts b/packages/form/core/form-control.ts index 48b5667..12c775c 100644 --- a/packages/form/core/form-control.ts +++ b/packages/form/core/form-control.ts @@ -10,6 +10,7 @@ import type { TextArea, ControlBase, ValidatorRules, + ValidationHooks, } from '@astro-reactive/common'; import ShortUniqueId from 'short-unique-id'; @@ -25,6 +26,7 @@ export class FormControl { private _isPristine = true; private _placeholder: string | null = null; private _validators: ValidatorRules = []; + private _triggerValidationOn: ValidationHooks; private _errors: ValidationError[] = []; private _options: string[] | ControlOption[] = []; private _rows: number | null = null; @@ -47,6 +49,7 @@ export class FormControl { label = '', placeholder = null, validators = [], + triggerValidationOn = 'blur', } = config; const uid = new ShortUniqueId({ length: 9 }); @@ -57,6 +60,7 @@ export class FormControl { this._label = label; this._placeholder = placeholder; this._validators = validators; + this._triggerValidationOn = triggerValidationOn; if (type === 'radio' || type === 'dropdown') { const { options = [] } = config as Radio | Dropdown; @@ -111,6 +115,10 @@ export class FormControl { return this._validators; } + get triggerValidationOn() { + return this._triggerValidationOn; + } + get errors() { return this._errors; } diff --git a/packages/validator/Validator.astro b/packages/validator/Validator.astro index 25b0701..edb74b5 100644 --- a/packages/validator/Validator.astro +++ b/packages/validator/Validator.astro @@ -28,10 +28,10 @@ const { hook = 'all', displayErrorMessages = false } = Astro.props; import { clearErrors, validate } from './core'; // const hook: HookType = (document.getElementById('hook') as HTMLInputElement).value as HookType; - const inputs = [...document.querySelectorAll('form input')] as HTMLInputElement[]; + const inputs = [...document.querySelectorAll('body *[data-validation-on]')] as HTMLInputElement[]; inputs?.forEach((input) => { - input.addEventListener('blur', (e: Event) => { + input.addEventListener(input.dataset?.validationOn ?? 'blur', (e: Event) => { // NOTE: event target attribute names are converted to lowercase const element = e.target as HTMLInputElement; const attributeNames = element?.getAttributeNames() || [];