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 interface ErorrField { error: Accessor; id: string; touchKey?: string; // specify if different from id } 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, ""); if (phoneNumber.length != 10) { return false; } const areaCode = Number(phoneNumber.substring(0, 3)); return areaCode >= 200; } 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 = /^(https?:\/\/)?(www\.)?[a-zA-Z0-9@:%._\+~#-]{1,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 }