@frank-auth/react
Version:
Flexible and customizable React UI components for Frank Authentication
1 lines • 15.5 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../../../../../src/components/auth/sign-up/index.tsx"],"sourcesContent":["/**\n * @frank-auth/react - Sign Up Components Index\n *\n * Main entry point for all sign-up related components.\n * Exports all sign-up variants and utilities.\n */\n\n// ============================================================================\n// Main Components\n// ============================================================================\n\nimport type {RadiusT, SizeT} from \"@/types\";\n\nexport * from './sign-up';\nexport { SignUpForm } from './sign-up-form';\nexport { SignUpModal } from './sign-up-modal';\nexport { SignUpButton } from './sign-up-button';\nexport { SignUpCard } from './sign-up-card';\n\n// ============================================================================\n// Shared Types and Interfaces\n// ============================================================================\n\nexport interface BaseSignUpProps {\n /**\n * Sign-up methods to show\n */\n methods?: ('password' | 'oauth' | 'magic-link' | 'passkey' | 'sso')[];\n\n /**\n * Initial email value\n */\n email?: string;\n\n /**\n * Initial organization ID (for invitations)\n */\n organizationId?: string;\n\n /**\n * Invitation token (for org invitations)\n */\n invitationToken?: string;\n\n /**\n * Redirect URL after successful sign-up\n */\n redirectUrl?: string;\n\n /**\n * Success callback\n */\n onSuccess?: (result: any) => void;\n\n /**\n * Error callback\n */\n onError?: (error: Error) => void;\n\n /**\n * Custom title\n */\n title?: string;\n\n /**\n * Custom subtitle\n */\n subtitle?: string;\n\n /**\n * Form size\n */\n size?: SizeT;\n\n radius?: RadiusT;\n\n /**\n * Whether to show branding\n */\n showBranding?: boolean;\n\n /**\n * Disabled state\n */\n disabled?: boolean;\n\n /**\n * Require terms of service acceptance\n */\n requireTerms?: boolean;\n\n /**\n * Terms of service URL\n */\n termsUrl?: string;\n\n /**\n * Privacy policy URL\n */\n privacyUrl?: string;\n}\n\nexport interface SignUpFormProps extends BaseSignUpProps {\n /**\n * Show sign-in link\n */\n showSignInLink?: boolean;\n\n /**\n * Form variant\n */\n variant?: 'default' | 'minimal' | 'compact';\n\n /**\n * Custom className\n */\n className?: string;\n\n /**\n * Custom footer content\n */\n footer?: React.ReactNode;\n\n /**\n * Custom header content\n */\n header?: React.ReactNode;\n\n /**\n * Password requirements\n */\n passwordRequirements?: {\n minLength?: number;\n requireUppercase?: boolean;\n requireLowercase?: boolean;\n requireNumbers?: boolean;\n requireSymbols?: boolean;\n };\n\n /**\n * Auto-focus first field\n */\n autoFocus?: boolean;\n\n /**\n * Collect additional fields\n */\n collectFields?: ('firstName' | 'lastName' | 'username' | 'phoneNumber')[];\n}\n\nexport interface SignUpModalProps extends BaseSignUpProps {\n /**\n * Whether the modal is open\n */\n isOpen?: boolean;\n\n /**\n * Callback for when modal should close\n */\n onClose?: () => void;\n\n /**\n * Modal size\n */\n modalSize?: 'sm' | 'md' | 'lg' | 'xl' | 'full';\n\n /**\n * Whether modal can be closed by clicking backdrop\n */\n closeOnBackdropClick?: boolean;\n\n /**\n * Whether modal can be closed by pressing escape\n */\n closeOnEscape?: boolean;\n\n /**\n * Custom modal className\n */\n modalClassName?: string;\n\n /**\n * Show close button\n */\n showCloseButton?: boolean;\n\n /**\n * Modal backdrop blur\n */\n backdrop?: 'opaque' | 'blur' | 'transparent';\n\n /**\n * Modal placement\n */\n placement?: 'auto' | 'top' | 'center' | 'bottom';\n}\n\nexport interface SignUpButtonProps {\n /**\n * Button text\n */\n children?: React.ReactNode;\n\n /**\n * Button variant\n */\n variant?: 'solid' | 'bordered' | 'light' | 'flat' | 'faded' | 'shadow' | 'ghost';\n\n /**\n * Button color\n */\n color?: 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger';\n\n /**\n * Button size\n */\n size?: 'sm' | 'md' | 'lg';\n\n /**\n * Full width button\n */\n fullWidth?: boolean;\n\n /**\n * Button icon\n */\n startContent?: React.ReactNode;\n endContent?: React.ReactNode;\n\n /**\n * Custom className\n */\n className?: string;\n\n /**\n * Modal mode - opens sign-up in modal instead of navigation\n */\n modalMode?: boolean;\n\n /**\n * Props to pass to the sign-up modal\n */\n modalProps?: Partial<SignUpModalProps>;\n\n /**\n * Navigation URL (when not in modal mode)\n */\n href?: string;\n\n /**\n * Custom onClick handler\n */\n onClick?: () => void;\n\n /**\n * Disabled state\n */\n disabled?: boolean;\n}\n\nexport interface SignUpCardProps extends BaseSignUpProps {\n /**\n * Card variant\n */\n variant?: 'shadow' | 'bordered' | 'flat';\n\n /**\n * Custom className\n */\n className?: string;\n\n /**\n * Card padding\n */\n padding?: 'none' | 'sm' | 'md' | 'lg';\n\n /**\n * Card radius\n */\n radius?: 'none' | 'sm' | 'md' | 'lg';\n\n /**\n * Whether card has shadow\n */\n shadow?: 'none' | 'sm' | 'md' | 'lg';\n\n /**\n * Whether card is blurred\n */\n isBlurred?: boolean;\n\n /**\n * Custom footer content\n */\n footer?: React.ReactNode;\n\n /**\n * Custom header content\n */\n header?: React.ReactNode;\n\n /**\n * Card max width\n */\n maxWidth?: string | number;\n\n /**\n * Center the card\n */\n centered?: boolean;\n}\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Get default sign-up configuration based on features\n */\nexport const getDefaultSignUpConfig = (features: any) => {\n const methods: ('password' | 'oauth' | 'magic-link' | 'passkey')[] = [];\n\n if (features.signUp) methods.push('password');\n if (features.oauth) methods.push('oauth');\n if (features.magicLink) methods.push('magic-link');\n if (features.passkeys) methods.push('passkey');\n\n return {\n methods,\n showSignInLink: features.signIn,\n requireTerms: true,\n };\n};\n\n/**\n * Sign-up validation helpers\n */\nexport const signUpValidation = {\n email: (email: string) => {\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n return emailRegex.test(email);\n },\n\n password: (password: string, requirements?: SignUpFormProps['passwordRequirements']) => {\n const req = requirements || {};\n\n if (req.minLength && password.length < req.minLength) return false;\n if (req.requireUppercase && !/[A-Z]/.test(password)) return false;\n if (req.requireLowercase && !/[a-z]/.test(password)) return false;\n if (req.requireNumbers && !/\\d/.test(password)) return false;\n if (req.requireSymbols && !/[!@#$%^&*(),.?\":{}|<>]/.test(password)) return false;\n\n return password.length >= (req.minLength || 8);\n },\n\n firstName: (name: string) => {\n return name.length >= 2 && /^[a-zA-Z\\s]+$/.test(name);\n },\n\n lastName: (name: string) => {\n return name.length >= 2 && /^[a-zA-Z\\s]+$/.test(name);\n },\n\n username: (username: string) => {\n return username.length >= 3 && /^[a-zA-Z0-9_-]+$/.test(username);\n },\n\n phoneNumber: (phone: string) => {\n return /^\\+?[1-9]\\d{1,14}$/.test(phone.replace(/\\s/g, ''));\n },\n};\n\n/**\n * Password strength calculator\n */\nexport const getPasswordStrength = (password: string): {\n score: number;\n feedback: string[];\n strength: 'weak' | 'fair' | 'good' | 'strong';\n} => {\n const feedback: string[] = [];\n let score = 0;\n\n // Length check\n if (password.length >= 8) {\n score += 1;\n } else {\n feedback.push('Use at least 8 characters');\n }\n\n if (password.length >= 12) {\n score += 1;\n }\n\n // Character variety\n if (/[a-z]/.test(password)) score += 1;\n else feedback.push('Include lowercase letters');\n\n if (/[A-Z]/.test(password)) score += 1;\n else feedback.push('Include uppercase letters');\n\n if (/\\d/.test(password)) score += 1;\n else feedback.push('Include numbers');\n\n if (/[!@#$%^&*(),.?\":{}|<>]/.test(password)) score += 1;\n else feedback.push('Include symbols');\n\n // Common patterns penalty\n if (/(.)\\1{2,}/.test(password)) {\n score -= 1;\n feedback.push('Avoid repeated characters');\n }\n\n if (/123|abc|qwe/i.test(password)) {\n score -= 1;\n feedback.push('Avoid common patterns');\n }\n\n // Determine strength level\n let strength: 'weak' | 'fair' | 'good' | 'strong';\n if (score <= 2) strength = 'weak';\n else if (score <= 4) strength = 'fair';\n else if (score <= 5) strength = 'good';\n else strength = 'strong';\n\n return {\n score: Math.max(0, Math.min(6, score)),\n feedback,\n strength,\n };\n};\n\n/**\n * Generate secure password suggestions\n */\nexport const generatePasswordSuggestions = (length = 12): string[] => {\n const lowercase = 'abcdefghijklmnopqrstuvwxyz';\n const uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';\n const numbers = '0123456789';\n const symbols = '!@#$%^&*(),.?\":{}|<>';\n\n const allChars = lowercase + uppercase + numbers + symbols;\n\n const suggestions: string[] = [];\n\n for (let i = 0; i < 3; i++) {\n let password = '';\n\n // Ensure at least one character from each category\n password += lowercase[Math.floor(Math.random() * lowercase.length)];\n password += uppercase[Math.floor(Math.random() * uppercase.length)];\n password += numbers[Math.floor(Math.random() * numbers.length)];\n password += symbols[Math.floor(Math.random() * symbols.length)];\n\n // Fill the rest randomly\n for (let j = 4; j < length; j++) {\n password += allChars[Math.floor(Math.random() * allChars.length)];\n }\n\n // Shuffle the password\n password = password.split('').sort(() => Math.random() - 0.5).join('');\n suggestions.push(password);\n }\n\n return suggestions;\n};\n\n/**\n * Format invitation data\n */\nexport const formatInvitationData = (invitationToken?: string) => {\n if (!invitationToken) return null;\n\n try {\n // Decode invitation token (assuming it's base64 encoded JSON)\n const decoded = atob(invitationToken);\n const data = JSON.parse(decoded);\n\n return {\n organizationId: data.orgId,\n organizationName: data.orgName,\n inviterName: data.inviterName,\n inviterEmail: data.inviterEmail,\n role: data.role,\n expiresAt: new Date(data.expiresAt),\n };\n } catch {\n return null;\n }\n};\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nexport const DEFAULT_PASSWORD_REQUIREMENTS = {\n minLength: 8,\n requireUppercase: true,\n requireLowercase: true,\n requireNumbers: true,\n requireSymbols: false,\n};\n\nexport const COLLECT_FIELDS_OPTIONS = [\n { key: 'firstName', label: 'First Name', required: true },\n { key: 'lastName', label: 'Last Name', required: true },\n { key: 'username', label: 'Username', required: false },\n { key: 'phoneNumber', label: 'Phone Number', required: false },\n] as const;\n\n// ============================================================================\n// Default Export - Main Sign Up Component\n// ============================================================================\n\nexport { SignUp as default } from './sign-up';"],"names":["getDefaultSignUpConfig","features","methods","signUpValidation","email","password","requirements","req","name","username","phone","getPasswordStrength","feedback","score","strength","generatePasswordSuggestions","length","lowercase","uppercase","numbers","symbols","allChars","suggestions","i","j","formatInvitationData","invitationToken","decoded","data","DEFAULT_PASSWORD_REQUIREMENTS","COLLECT_FIELDS_OPTIONS"],"mappings":"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"}