First Commit
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
'use client';
|
||||
|
||||
// @mui
|
||||
import { useColorScheme } from '@mui/material/styles';
|
||||
|
||||
/*************************** IMAGE - TYPE IDENTIFY ***************************/
|
||||
|
||||
function isImageComponentProps(value) {
|
||||
return value.light !== undefined && value.dark !== undefined;
|
||||
}
|
||||
|
||||
/*************************** COMMON - IMAGE PATH ***************************/
|
||||
|
||||
export default function GetImagePath(image) {
|
||||
const { colorScheme } = useColorScheme();
|
||||
|
||||
return isImageComponentProps(image) ? image[colorScheme || 'light'] : image;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// material-ui
|
||||
import { alpha } from '@mui/material/styles';
|
||||
|
||||
/**
|
||||
* Converts a hex color string to an RGB channel string ("r g b").
|
||||
*
|
||||
* @param hex - The hex color string (e.g. "#C8FAD6", "#FFF", "#FF00FFAA").
|
||||
* @returns The RGB channel string (e.g. "200 250 214").
|
||||
* @throws {Error} If the input is not a valid hex color.
|
||||
*/
|
||||
export function hexToRgbChannel(hex) {
|
||||
let cleaned = hex.replace(/^#/, '');
|
||||
|
||||
if (cleaned.length === 3) {
|
||||
cleaned = cleaned
|
||||
.split('')
|
||||
.map((c) => c + c)
|
||||
.join('');
|
||||
}
|
||||
if (cleaned.length === 4) {
|
||||
cleaned = cleaned
|
||||
.split('')
|
||||
.map((c) => c + c)
|
||||
.join('');
|
||||
}
|
||||
|
||||
if (cleaned.length !== 6 && cleaned.length !== 8) {
|
||||
throw new Error(`Invalid hex color: ${hex}`);
|
||||
}
|
||||
|
||||
const r = parseInt(cleaned.substring(0, 2), 16);
|
||||
const g = parseInt(cleaned.substring(2, 4), 16);
|
||||
const b = parseInt(cleaned.substring(4, 6), 16);
|
||||
|
||||
return `${r} ${g} ${b}`;
|
||||
}
|
||||
|
||||
export function extendPaletteWithChannels(palette) {
|
||||
const result = { ...palette };
|
||||
|
||||
Object.entries(palette).forEach(([k, v]) => {
|
||||
if (typeof v === 'string' && v.startsWith('#')) {
|
||||
result[`${k}Channel`] = hexToRgbChannel(v);
|
||||
} else if (typeof v === 'object' && v !== null) {
|
||||
result[k] = extendPaletteWithChannels(v);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function withAlpha(color, opacity) {
|
||||
// Case 1: normal color (hex, rgb, hsl…)
|
||||
if (/^#|rgb|hsl|color/i.test(color)) {
|
||||
return alpha(color, opacity);
|
||||
}
|
||||
|
||||
// Case 2: CSS Var: var(--mui-palette-xxx) or var(--palette-xxx, #hex)
|
||||
if (color.startsWith('var(')) {
|
||||
// inject "Channel" *before the closing parenthesis of the var name only*
|
||||
return color.replace(/(--[a-zA-Z0-9-]+)(.*)\)/, `$1Channel$2)`).replace(/^var\((.+)\)$/, `rgba(var($1) / ${opacity})`);
|
||||
}
|
||||
|
||||
// Fallback
|
||||
return color;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
'use client';
|
||||
|
||||
// @project
|
||||
import { withAlpha } from '@/utils/colorUtils';
|
||||
|
||||
/*************************** COMMON - FOCUS STYLE ***************************/
|
||||
|
||||
export function generateFocusStyle(color) {
|
||||
return {
|
||||
boxShadow: `0px 0px 0px 3px ${withAlpha(color, 0.2)}`
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*************************** CARD RADIUS - STYLES ***************************/
|
||||
|
||||
export function getRadiusStyles(radius, ...corners) {
|
||||
const borderRadiusStyles = {
|
||||
'& .MuiPaper-root': {}
|
||||
};
|
||||
|
||||
corners.forEach((corner) => {
|
||||
switch (corner) {
|
||||
case 'topLeft':
|
||||
borderRadiusStyles.borderTopLeftRadius = radius;
|
||||
borderRadiusStyles['& .MuiPaper-root'] = {
|
||||
borderTopLeftRadius: radius
|
||||
};
|
||||
break;
|
||||
case 'topRight':
|
||||
borderRadiusStyles.borderTopRightRadius = radius;
|
||||
borderRadiusStyles['& .MuiPaper-root'] = {
|
||||
borderTopRightRadius: radius
|
||||
};
|
||||
break;
|
||||
case 'bottomLeft':
|
||||
borderRadiusStyles.borderBottomLeftRadius = radius;
|
||||
borderRadiusStyles['& .MuiPaper-root'] = {
|
||||
borderBottomLeftRadius: radius
|
||||
};
|
||||
break;
|
||||
case 'bottomRight':
|
||||
borderRadiusStyles.borderBottomRightRadius = radius;
|
||||
borderRadiusStyles['& .MuiPaper-root'] = {
|
||||
borderBottomRightRadius: radius
|
||||
};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return borderRadiusStyles;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
export const emailSchema = {
|
||||
required: 'Email is required',
|
||||
pattern: { value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i, message: 'Invalid email address' }
|
||||
};
|
||||
|
||||
export const passwordSchema = {
|
||||
required: 'Password is required',
|
||||
minLength: { value: 8, message: 'Password must be at least 8 characters' },
|
||||
validate: {
|
||||
noSpaces: (value) => !/\s/.test(value) || 'Password cannot contain spaces',
|
||||
hasUpperCase: (value) => /[A-Z]/.test(value) || 'Password must have at least one uppercase letter',
|
||||
hasNumber: (value) => /[0-9]/.test(value) || 'Password must have at least one number',
|
||||
hasSpecialChar: (value) => /[!@#$%^&*(),.?":{}|<>]/.test(value) || 'Password must have at least one special character'
|
||||
}
|
||||
};
|
||||
|
||||
export const firstNameSchema = {
|
||||
required: 'First name is required',
|
||||
pattern: { value: /^[a-zA-Z\s]+$/, message: 'Invalid first name' },
|
||||
validate: {
|
||||
trim: (value) => {
|
||||
const trimmedValue = value.trim();
|
||||
return trimmedValue.length > 0 || 'First name cannot be empty or contain only spaces';
|
||||
}
|
||||
},
|
||||
onBlur: (e) => {
|
||||
e.target.value = e.target.value.trim();
|
||||
}
|
||||
};
|
||||
|
||||
export const lastNameSchema = {
|
||||
required: 'Last name is required',
|
||||
pattern: { value: /^[a-zA-Z\s]+$/, message: 'Invalid last name' },
|
||||
validate: {
|
||||
trim: (value) => {
|
||||
const trimmedValue = value.trim();
|
||||
return trimmedValue.length > 0 || 'Last name cannot be empty or contain only spaces';
|
||||
}
|
||||
},
|
||||
onBlur: (e) => {
|
||||
e.target.value = e.target.value.trim();
|
||||
}
|
||||
};
|
||||
|
||||
export const usernameSchema = {
|
||||
required: 'Username is required',
|
||||
pattern: {
|
||||
value: /^[a-zA-Z0-9._]+$/, // Alphanumeric, underscores, and dots
|
||||
message: 'Username can only contain letters, numbers, dots, and underscores'
|
||||
},
|
||||
validate: {
|
||||
trim: (value) => {
|
||||
const trimmedValue = value.trim();
|
||||
return trimmedValue.length > 0 || 'Username cannot be empty or contain only spaces';
|
||||
},
|
||||
noSpaces: (value) => {
|
||||
return !/\s/.test(value) || 'Username cannot contain spaces';
|
||||
}
|
||||
},
|
||||
onBlur: (e) => {
|
||||
e.target.value = e.target.value.trim();
|
||||
}
|
||||
};
|
||||
|
||||
export const contactSchema = {
|
||||
required: 'Contact number is required',
|
||||
pattern: { value: /^[0-9()\-\.]{7,15}$/, message: 'Invalid contact number' }
|
||||
};
|
||||
|
||||
export const otpSchema = {
|
||||
required: 'OTP is required',
|
||||
minLength: { value: 6, message: 'OTP must be exactly 6 characters' }
|
||||
};
|
||||
Reference in New Issue
Block a user