convert to JSDoc comments

This commit is contained in:
Elijah Duffy
2025-07-01 15:07:57 -07:00
parent c01dd71023
commit ff880c3cbf
2 changed files with 100 additions and 61 deletions

151
index.ts
View File

@@ -1,60 +1,70 @@
import type { Action } from 'svelte/action';
// InputElement is a union of all input types that can be validated.
/** InputElement is a union of all input types that can be validated. */
export type InputElement = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;
// InputNodeList is a union of all node lists that can be validated.
/** InputNodeList is a union of all node lists that can be validated. */
export type InputNodeList = NodeListOf<HTMLInputElement> | NodeListOf<HTMLTextAreaElement> | [];
// ValidatorOptions configures the behavior of a validator.
/** ValidatorOptions configures the behavior of a validator. */
export type ValidatorOptions = {
required?: boolean; // required is a flag that indicates whether the input is required.
pattern?: RegExp; // pattern is a regex pattern that the input value must match.
minlength?: number; // minlength is the minimum length of the input value.
maxlength?: number; // maxlength is the maximum length of the input value.
length?: number; // length is the exact length of the input value.
type?: 'email' | 'phone' | 'score'; // NOT IMPLEMENTED. type is a predefined validation type.
baseval?: string; // baseval is a value that the input value must not match (an alternate zero-value).
autovalOnInvalid?: boolean; // autovalOnInvalid is a flag that automatically adds a keyup listener to the input on invalid.
/** required is a flag that indicates whether the input is required. */
required?: boolean;
/** pattern is a regex pattern that the input value must match. */
pattern?: RegExp;
/** minlength is the minimum length of the input value. */
minlength?: number;
/** maxlength is the maximum length of the input value. */
maxlength?: number;
/** length is the exact length of the input value. */
length?: number;
/** NOT IMPLEMENTED. type is a predefined validation type. */
type?: 'email' | 'phone' | 'score';
/** baseval is a value that the input value must not match (an alternate zero-value). */
baseval?: string;
/** autovalOnInvalid is a flag that automatically adds a keyup listener to the input on invalid. */
autovalOnInvalid?: boolean;
func?: (node: InputElement) => boolean | Promise<boolean>;
valfunc?: (val: string) => boolean | Promise<boolean>;
};
// InputValidatorPayload carries the current value of an input element when it is validated.
/** InputValidatorPayload carries the current value of an input element when it is validated. */
export type InputValidatorPayload = { value: string; valid: boolean };
// InputValidatorEvent is a custom event that is dispatched when an input is validated.
/** InputValidatorEvent is a custom event that is dispatched when an input is validated. */
export class InputValidatorEvent extends CustomEvent<InputValidatorPayload> {}
// FormValidatorPayload carries a list of invalid and valid inputs when a form is validated.
/** FormValidatorPayload carries a list of invalid and valid inputs when a form is validated. */
export type FormValidatorPayload = {
invalidInputs: InputElement[];
validInputs: InputElement[];
valid: boolean;
};
// FormValidatorEvent is a custom event that is dispatched when a form is validated.
/** FormValidatorEvent is a custom event that is dispatched when a form is validated. */
export class FormValidatorEvent extends CustomEvent<FormValidatorPayload> {}
// ValidatorState is the state of a validator.
/** ValidatorState is the state of a validator. */
export type ValidatorState = 'unknown' | 'valid' | 'invalid';
const VALIDATOR_VALID: ValidatorState = 'valid';
const VALIDATOR_INVALID: ValidatorState = 'invalid';
const VALIDATOR_UNKNOWN: ValidatorState = 'unknown';
// QUERY_INPUTS is the query string for all input elements that can be validated.
/** QUERY_INPUTS is the query string for all input elements that can be validated. */
const QUERY_INPUTS = 'input[data-validate-identifier], textarea[data-validate-identifier]';
// DATA_ID is the key for the validator index in the input element's dataset.
/** DATA_ID is the key for the validator index in the input element's dataset. */
const DATA_ID = 'validateIdentifier';
// DATA_STATE is the key for the validation state in the input element's dataset.
/** DATA_STATE is the key for the validation state in the input element's dataset. */
const DATA_STATE = 'validateState';
// email is a regex pattern for validating email addresses.
/** email is a regex pattern for validating email addresses. */
export const email: RegExp = new RegExp(String.raw`^[^@\s]+@[^@\s]+\.[^@\s]+$`);
// phone is a regex pattern for validating phone numbers.
/** phone is a regex pattern for validating phone numbers. */
export const phone: RegExp = new RegExp(String.raw`^(\+)?(\(?\d+\)?)(([\s-]+)?(\d+)){0,}$`);
// score is a regex pattern for validating lesson grades.
/** score is a regex pattern for validating lesson grades. */
export const score: RegExp = new RegExp(String.raw`^(-?\d{0,2}|100)$`);
// InputState is some historical state of an input element, tracking its value,
// selectionStart and selectionEnd.
/**
* InputState is some historical state of an input element, tracking its value,
* selectionStart and selectionEnd.
*/
export type InputState = {
value: string;
selectionStart: number;
@@ -65,8 +75,10 @@ let nextValidatorID = 0;
const validators: Record<number, ValidatorOptions> = {};
const lastState: Record<number, InputState> = {};
// validate attaches rules to an input element that can be used to validate the input.
// If opts is undefined, no validation rules will be attached.
/**
* validate attaches rules to an input element that can be used to validate the input.
* If opts is undefined, no validation rules will be attached.
*/
export const validate: Action<
InputElement,
ValidatorOptions | undefined,
@@ -105,10 +117,12 @@ export const validate: Action<
};
};
// keydownValidator attaches a keydown event listener to an input element that will
// process the input value according to any attached validation rules, triggering
// valid or invalid depending on validation success. If opts.constrain is true,
// the input value will be constrained to valid characters.
/**
* keydownValidator attaches a keydown event listener to an input element that will
* process the input value according to any attached validation rules, triggering
* valid or invalid depending on validation success. If opts.constrain is true,
* the input value will be constrained to valid characters.
*/
export const keydownValidator: Action<
InputElement,
{
@@ -131,9 +145,11 @@ export const keydownValidator: Action<
});
};
// keyupValidator attaches a keydown event listener to an input element that will
// process the input value according to any attached validation rules, triggering
// the `validate` event with the input value and validation state.
/**
* keyupValidator attaches a keydown event listener to an input element that will
* process the input value according to any attached validation rules, triggering
* the `validate` event with the input value and validation state.
*/
export const keyupValidator: Action<InputElement> = (el) => {
const node = el as HTMLInputElement;
@@ -147,9 +163,11 @@ export const keyupValidator: Action<InputElement> = (el) => {
});
};
// inputValidator attaches an oninput event listener to an input element that will
// process the input value according to any attached validation rules, triggering
// the `validate` event with the input value and validation state.
/**
* inputValidator attaches an oninput event listener to an input element that will
* process the input value according to any attached validation rules, triggering
* the `validate` event with the input value and validation state.
*/
export const inputValidator: Action<InputElement> = (el) => {
const node = el as HTMLInputElement;
@@ -162,10 +180,12 @@ export const inputValidator: Action<InputElement> = (el) => {
});
};
// liveValidator attaches multiple event listeners to an input element that will
// process the input value according to any attached validation rules, triggering
// the `validate` event with the input value and validation state. liveValidator
// supports the constrain option without introducing edge cases with text selection.
/**
* liveValidator attaches multiple event listeners to an input element that will
* process the input value according to any attached validation rules, triggering
* the `validate` event with the input value and validation state. liveValidator
* supports the constrain option without introducing edge cases with text selection.
*/
export const liveValidator: Action<
InputElement,
{
@@ -227,11 +247,13 @@ export const liveValidator: Action<
node.addEventListener('input', inputHandler);
};
// submitValidator attaches a submit event listener to a form element that will
// process the input values of all children according to any attached validation
// rules, triggering the validate event for individual input elements
// depending on validation success. Similarly, the validate event
// event will be triggered on the form element itself.
/**
* submitValidator attaches a submit event listener to a form element that will
* process the input values of all children according to any attached validation
* rules, triggering the validate event for individual input elements
* depending on validation success. Similarly, the validate event
* event will be triggered on the form element itself.
*/
export const submitValidator: Action<
HTMLFormElement,
undefined,
@@ -254,8 +276,7 @@ export const submitValidator: Action<
});
};
// disableFormDefault helper action prevents the default form submission behavior
// and disables all browser validation.
/** disableFormDefault helper action prevents the default form submission behavior and disables all browser validation. */
export const disableFormDefault: Action<HTMLFormElement> = (form) => {
form.setAttribute('novalidate', 'true');
form.addEventListener('submit', (event) => {
@@ -263,10 +284,12 @@ export const disableFormDefault: Action<HTMLFormElement> = (form) => {
});
};
// validateInput checks an input element's value against its validation rules, triggering
// the validate events with the current value and validation state, updating the input's
// dataset accordingly. If opts.autovalOnInvalid is true, the input will be revalidated on
// on keyup automatically from now on.
/**
* validateInput checks an input element's value against its validation rules, triggering
* the validate events with the current value and validation state, updating the input's
* dataset accordingly. If opts.autovalOnInvalid is true, the input will be revalidated on
* on keyup automatically from now on.
*/
export const validateInput = async (
input: InputElement,
valueOverride?: string
@@ -289,9 +312,11 @@ export const validateInput = async (
return valid;
};
// validateForm checks all input elements in a form element against their validation rules,
// triggering valid or invalid events for individual input elements depending on validation
// success. Similarly, valid and invalid events will be triggered on the form element.
/**
* validateForm checks all input elements in a form element against their validation rules,
* triggering valid or invalid events for individual input elements depending on validation
* success. Similarly, valid and invalid events will be triggered on the form element.
*/
export const validateForm = async (form: HTMLFormElement): Promise<boolean> => {
const inputs = form.querySelectorAll(QUERY_INPUTS);
@@ -321,7 +346,7 @@ export const validateForm = async (form: HTMLFormElement): Promise<boolean> => {
return valid;
};
// isInputValid checks an input element's value against its validation rules.
/** isInputValid checks an input element's value against its validation rules. */
export const isInputValid = async (
input: InputElement,
valueOverride?: string
@@ -360,7 +385,7 @@ export const isInputValid = async (
return true;
};
// isFormValid checks all input elements in a form element against their validation rules.
/** isFormValid checks all input elements in a form element against their validation rules. */
export const isFormValid = async (form: HTMLFormElement): Promise<boolean> => {
const inputs = form.querySelectorAll(QUERY_INPUTS);
@@ -375,8 +400,10 @@ export const isFormValid = async (form: HTMLFormElement): Promise<boolean> => {
return valid;
};
// isValueValid checks a value against a set of validation rules. Note: node-based
// custom validator functions are not supported by this function.
/**
* isValueValid checks a value against a set of validation rules. Note: node-based
* custom validator functions are not supported by this function.
*/
export const isValueValid = async (val: string, opts: ValidatorOptions): Promise<boolean> => {
console.debug('isValueValid', `val="${val}"`, 'opts:', opts);
// if input is required and empty, return false
@@ -424,15 +451,17 @@ export const isValueValid = async (val: string, opts: ValidatorOptions): Promise
return true;
};
// getid returns the validator ID attached to an input element. Returns -1 if
// no ID is attached.
/**
* getid returns the validator ID attached to an input element. Returns -1 if
* no ID is attached.
*/
export const getid = (input: InputElement): number => {
const idstr = input.dataset[DATA_ID] || '-1';
const id = parseInt(idstr);
return id;
};
// getopts returns the validation rules attached to an input element.
/** getopts returns the validation rules attached to an input element. */
export const getopts = (input: InputElement): ValidatorOptions | undefined => {
const id = getid(input);
return validators[id];