import { Accessor, createMemo } from "solid-js"; export interface Validation { id: string; // snake case identifier that is "touched" name: string; required?: boolean; touched: Accessor>; field: Accessor; fieldBlur?: Accessor; isValidFunc?: (input: string, ...args: any) => boolean; invalidMsg?: string; } export function createValidation(validation: Validation): Accessor { const { id, name, required, touched, isValidFunc, invalidMsg } = validation; const field = () => validation.field().trim(); // When no blur accessor is provided, fall back to the live value so the // "has value but not blurred yet" guard is always false and validation // runs against the live value instead. const fieldBlur = validation.fieldBlur ? () => validation.fieldBlur!().trim() : field; return createMemo(() => { if (!touched()[id] || (field() && !fieldBlur()) || (isValidFunc && isValidFunc(field()))) return ""; if (!field()) return required ? `${name} is required` : ""; if (isValidFunc && !isValidFunc(fieldBlur())) return invalidMsg ?? `${name} is invalid`; return ""; }); } export function isPhoneNumberValid(phoneNumber: string): boolean { phoneNumber = phoneNumber.replace(/\D/g, ""); return phoneNumber.length == 10 } export function isEmailValid(email: string): boolean { const regex = /^[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~](\.?[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~])*@[a-zA-Z0-9](-*\.?[a-zA-Z0-9])*\.[a-zA-Z](-?[a-zA-Z0-9])+$/; if (!email) return false; let emailParts = email.split('@'); if (emailParts.length !== 2) return false; let account = emailParts[0]; let address = emailParts[1]; if (account.length > 64) return false; else if (address.length > 255) return false; let domainParts = address.split('.'); if (domainParts.some(function (part) { return part.length > 63; })) return false; return regex.test(email); } export function isUrlValid(url: string): boolean { const regex = /[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/; return regex.test(url); } export function isZipCodeValid(zip: string): boolean { const rawZip = String(zip).replace(/\D/g, ""); return rawZip.length == 5 || rawZip.length == 9 } export function isTaxIdValid(id: string): boolean { const rawId = String(id).replace(/\D/g, ""); return rawId.length == 9; } // isAtLeastMinChars checks if the input string is at least "min" characters long // and returns a boolean, true if valid, false if not. export function isAtLeastMinChars(input: string, min:number):boolean { return input.length >= min; } // isWithinMaxChars checks if the input string is at most "max" characters long // and returns a boolean, true if valid, false if not. export function isWithinMaxChars(input: string, max:number):boolean { return input.length <= max; } // isNameValid checks if the name string consists of only letters, spaces, hyphens, and apostrophes // and returns a boolean, true if valid, false if not. export function isNameValid(name: string): boolean { const regex = /^[\p{L}]*[\p{L} '\-]*[\p{L}]$/u; return regex.test(name); } // isUsernameValid checks if the username contains 5-50 characters and only consists of alphanumeric // characters. Returns an error message if invalid, empty string if valid. export function isUsernameValid(username: string): string { if (username.length < 5 || username.length > 50) return "Username must have 5-50 characters" const regex = /^[A-Za-z0-9]*$/; if (!regex.test(username)) return "Username must only contain alphanumeric characters" return ""; // Valid }