aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
299 lines (297 loc) • 8.62 kB
JavaScript
/**
* Built-in validation rules
*/
const validationRules = {
required: (message = 'This field is required') => ({
type: 'required',
message,
validator: value => {
if (typeof value === 'string') return value.trim().length > 0;
if (Array.isArray(value)) return value.length > 0;
return value !== null && value !== undefined;
}
}),
email: (message = 'Please enter a valid email address') => ({
type: 'email',
message,
validator: value => {
if (!value) return true; // Let required rule handle empty values
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(value);
}
}),
minLength: (min, message) => ({
type: 'min',
value: min,
message: message || `Must be at least ${min} characters`,
validator: value => {
if (!value) return true;
return value.length >= min;
}
}),
maxLength: (max, message) => ({
type: 'max',
value: max,
message: message || `Must be no more than ${max} characters`,
validator: value => {
if (!value) return true;
return value.length <= max;
}
}),
minValue: (min, message) => ({
type: 'min',
value: min,
message: message || `Must be at least ${min}`,
validator: value => {
if (value === null || value === undefined) return true;
return value >= min;
}
}),
maxValue: (max, message) => ({
type: 'max',
value: max,
message: message || `Must be no more than ${max}`,
validator: value => {
if (value === null || value === undefined) return true;
return value <= max;
}
}),
pattern: (regex, message) => ({
type: 'pattern',
value: regex,
message,
validator: value => {
if (!value) return true;
return regex.test(value);
}
}),
custom: (validator, message) => ({
type: 'custom',
message,
validator
}),
// Common patterns
phone: (message = 'Please enter a valid phone number') => ({
type: 'pattern',
value: /^[\+]?[1-9][\d]{0,15}$/,
message,
validator: value => {
if (!value) return true;
return /^[\+]?[1-9][\d]{0,15}$/.test(value.replace(/[\s\-\(\)]/g, ''));
}
}),
url: (message = 'Please enter a valid URL') => ({
type: 'pattern',
value: /^https?:\/\/.+/,
message,
validator: value => {
if (!value) return true;
try {
new URL(value);
return true;
} catch {
return false;
}
}
}),
strongPassword: (message = 'Password must contain at least 8 characters, including uppercase, lowercase, number, and special character') => ({
type: 'pattern',
value: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/,
message,
validator: value => {
if (!value) return true;
return /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/.test(value);
}
})
};
/**
* Validate a single field value against its rules
*/
const validateField = (value, rules) => {
for (const rule of rules) {
if (rule.validator && !rule.validator(value)) {
return rule.message;
}
}
return null;
};
/**
* Validate multiple fields
*/
const validateFields = (values, validation) => {
const errors = {};
for (const [fieldId, rules] of Object.entries(validation)) {
const fieldValue = values[fieldId];
const error = validateField(fieldValue, rules);
if (error) {
errors[fieldId] = error;
}
}
return {
isValid: Object.keys(errors).length === 0,
errors
};
};
/**
* Common validation schemas
*/
const commonValidationSchemas = {
userRegistration: {
firstName: [validationRules.required()],
lastName: [validationRules.required()],
email: [validationRules.required(), validationRules.email()],
password: [validationRules.required(), validationRules.strongPassword()],
confirmPassword: [validationRules.required()],
terms: [validationRules.required('You must accept the terms and conditions')]
},
contactForm: {
name: [validationRules.required()],
email: [validationRules.required(), validationRules.email()],
subject: [validationRules.required()],
message: [validationRules.required(), validationRules.minLength(10)]
},
profileUpdate: {
displayName: [validationRules.required(), validationRules.minLength(2), validationRules.maxLength(50)],
bio: [validationRules.maxLength(500)],
website: [validationRules.url()],
phone: [validationRules.phone()]
},
passwordChange: {
currentPassword: [validationRules.required()],
newPassword: [validationRules.required(), validationRules.strongPassword()],
confirmNewPassword: [validationRules.required()]
}
};
/**
* Custom validation functions for common scenarios
*/
const customValidators = {
passwordsMatch: (password, confirmPassword) => {
return password === confirmPassword;
},
uniqueEmail: async (email, checkFunction) => {
if (!email) return true;
return await checkFunction(email);
},
fileSize: (file, maxSizeInMB) => {
if (!file) return true;
const maxSizeInBytes = maxSizeInMB * 1024 * 1024;
return file.size <= maxSizeInBytes;
},
fileType: (file, allowedTypes) => {
if (!file) return true;
return allowedTypes.some(type => {
if (type.startsWith('.')) {
return file.name.toLowerCase().endsWith(type.toLowerCase());
}
return file.type.startsWith(type);
});
},
dateRange: (date, minDate, maxDate) => {
if (!date) return true;
if (minDate && date < minDate) return false;
if (maxDate && date > maxDate) return false;
return true;
},
creditCard: cardNumber => {
if (!cardNumber) return true;
// Remove spaces and dashes
const cleanNumber = cardNumber.replace(/[\s-]/g, '');
// Check if it's all digits and proper length
if (!/^\d{13,19}$/.test(cleanNumber)) return false;
// Luhn algorithm
let sum = 0;
let isEven = false;
for (let i = cleanNumber.length - 1; i >= 0; i--) {
let digit = parseInt(cleanNumber[i]);
if (isEven) {
digit *= 2;
if (digit > 9) {
digit -= 9;
}
}
sum += digit;
isEven = !isEven;
}
return sum % 10 === 0;
}
};
/**
* Async validation utilities
*/
const asyncValidation = {
debounce: (func, delay) => {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
return new Promise(resolve => {
timeoutId = setTimeout(() => resolve(func(...args)), delay);
});
};
},
createAsyncValidator: (asyncCheck, message, debounceMs = 500) => {
const debouncedCheck = asyncValidation.debounce(asyncCheck, debounceMs);
return async value => {
if (!value) return null;
try {
const isValid = await debouncedCheck(value);
return isValid ? null : message;
} catch (error) {
if (process.env.NODE_ENV === 'development') {
console.error('Async validation error:', error);
}
return 'Validation failed. Please try again.';
}
};
}
};
/**
* Form validation state manager
*/
class FormValidator {
constructor(validationSchema = {}) {
this.validationSchema = validationSchema;
this.asyncValidators = {};
}
setValidationSchema(schema) {
this.validationSchema = schema;
}
addAsyncValidator(fieldId, validator) {
this.asyncValidators[fieldId] = validator;
}
async validateForm(values) {
// Sync validation
const syncResult = validateFields(values, this.validationSchema);
// Async validation
const asyncErrors = {};
for (const [fieldId, validator] of Object.entries(this.asyncValidators)) {
const fieldValue = values[fieldId];
const error = await validator(fieldValue);
if (error) {
asyncErrors[fieldId] = error;
}
}
const allErrors = {
...syncResult.errors,
...asyncErrors
};
return {
isValid: Object.keys(allErrors).length === 0,
errors: allErrors
};
}
async validateField(fieldId, value) {
// Sync validation
const rules = this.validationSchema[fieldId] || [];
const syncError = validateField(value, rules);
if (syncError) return syncError;
// Async validation
const asyncValidator = this.asyncValidators[fieldId];
if (asyncValidator) {
return await asyncValidator(value);
}
return null;
}
}
export { FormValidator, asyncValidation, commonValidationSchemas, customValidators, validateField, validateFields, validationRules };
//# sourceMappingURL=FormValidationUtils.js.map