// Helper functions for input masks and validations /** Remove all non-digit characters from string */ export const onlyDigits = (value: string): string => { return (value || "").replace(/\D/g, ""); }; /** Format CPF: 000.000.000-00 */ export const maskCPF = (value: string): string => { const digits = onlyDigits(value).slice(0, 11); return digits .replace(/(\d{3})(\d)/, "$1.$2") .replace(/(\d{3})(\d)/, "$1.$2") .replace(/(\d{3})(\d{1,2})$/, "$1-$2"); }; /** Validate CPF format and checksum */ export const validateCPF = (value: string): boolean => { const digits = onlyDigits(value); if (digits.length !== 11 || /^([0-9])\1+$/.test(digits)) { return false; } const calculateDigit = (base: string, factor: number): number => { let sum = 0; for (let i = 0; i < base.length; i++) { sum += parseInt(base[i]) * (factor - i); } const rest = (sum * 10) % 11; return rest === 10 ? 0 : rest; }; const digit1 = calculateDigit(digits.slice(0, 9), 10); const digit2 = calculateDigit(digits.slice(0, 10), 11); return digits[9] === String(digit1) && digits[10] === String(digit2); }; /** Format CEP: 00000-000 */ export const maskCEP = (value: string): string => { const digits = onlyDigits(value).slice(0, 8); return digits.replace(/(\d{5})(\d{1,3})$/, "$1-$2"); }; /** Format phone: (00) 0000-0000 or (00) 00000-0000 */ export const maskPhone = (value: string): string => { const digits = onlyDigits(value).slice(0, 11); if (digits.length <= 10) { return digits .replace(/(\d{2})(\d)/, "($1) $2") .replace(/(\d{4})(\d{1,4})$/, "$1-$2"); } return digits .replace(/(\d{2})(\d)/, "($1) $2") .replace(/(\d{5})(\d{1,4})$/, "$1-$2"); }; /** Format date: dd/mm/aaaa */ export const maskDate = (value: string): string => { const digits = onlyDigits(value).slice(0, 8); return digits .replace(/(\d{2})(\d)/, "$1/$2") .replace(/(\d{2})(\d{1,4})$/, "$1/$2"); }; /** Validate date in format dd/mm/aaaa */ export const validateDate = (value: string): boolean => { const match = value.match(/^(\d{2})\/(\d{2})\/(\d{4})$/); if (!match) return false; const day = Number(match[1]); const month = Number(match[2]) - 1; const year = Number(match[3]); const date = new Date(year, month, day); return ( date.getFullYear() === year && date.getMonth() === month && date.getDate() === day ); }; /** Format UF: uppercase, max 2 chars */ export const maskUF = (value: string): string => { return (value || "").toUpperCase().replace(/[^A-Z]/g, "").slice(0, 2); }; /** Format RG by UF */ const rgFormatByUF: Record = { RJ: [2, 3, 2, 1], SP: [2, 3, 3, 1], MG: [2, 3, 3, 1], ES: [2, 3, 3, 1], PR: [2, 3, 3, 1], SC: [2, 3, 3, 1], RS: [2, 3, 3, 1], BA: [2, 3, 3, 1], PE: [2, 3, 3, 1], CE: [2, 3, 3, 1], PA: [2, 3, 3, 1], AM: [2, 3, 3, 1], AC: [2, 3, 3, 1], AP: [2, 3, 3, 1], AL: [2, 3, 3, 1], RN: [2, 3, 3, 1], PB: [2, 3, 3, 1], MA: [2, 3, 3, 1], PI: [2, 3, 3, 1], DF: [2, 3, 3, 1], GO: [2, 3, 3, 1], MT: [2, 3, 3, 1], MS: [2, 3, 3, 1], RO: [2, 3, 3, 1], RR: [2, 3, 3, 1], TO: [2, 3, 3, 1], }; export const maskRGByUF = (uf: string, value: string): string => { const raw = (value || "").toUpperCase().replace(/[^0-9X]/g, ""); const [a, b, c, dv] = rgFormatByUF[uf?.toUpperCase()] ?? [2, 3, 3, 1]; const baseMax = a + b + c; const baseDigits = raw.replace(/X/g, "").slice(0, baseMax); const verifier = raw.slice(baseDigits.length, baseDigits.length + dv).slice(0, 1); const g1 = baseDigits.slice(0, a); const g2 = baseDigits.slice(a, a + b); const g3 = baseDigits.slice(a + b, a + b + c); let formatted = g1; if (g2) formatted += `.${g2}`; if (g3) formatted += `.${g3}`; if (verifier) formatted += `-${verifier}`; return formatted; }; export const padRGLeftByUF = (uf: string, value: string): string => { const raw = (value || "").toUpperCase().replace(/[^0-9X]/g, ""); const [a, b, c, dv] = rgFormatByUF[uf?.toUpperCase()] ?? [2, 3, 3, 1]; const baseMax = a + b + c; let base = raw.replace(/X/g, ""); const verifier = raw.slice(base.length, base.length + dv).slice(0, 1); if (base.length < baseMax) { base = base.padStart(baseMax, "0"); } return maskRGByUF(uf, base + (verifier || "")); }; /** Format account number */ export const maskContaBancaria = (value: string): string => { const digits = onlyDigits(value); return digits; }; /** Format zone and section for voter title */ export const maskZonaSecao = (value: string): string => { const digits = onlyDigits(value).slice(0, 4); return digits; }; /** Format general numeric field */ export const maskNumeric = (value: string): string => { return onlyDigits(value); }; /** Remove extra spaces and trim */ export const normalizeText = (value: string): string => { return (value || "").replace(/\s+/g, " ").trim(); };