UNPKG

@frank-auth/react

Version:

Flexible and customizable React UI components for Frank Authentication

561 lines (486 loc) 16.4 kB
/** * @frank-auth/react - Configuration System * * Unified entry point for the Frank Auth React UI configuration system. * Provides comprehensive configuration management for themes, appearance, * localization, and organization-specific settings. * * @example Basic Usage * ```typescript * import { createFrankAuthConfig } from '@frank-auth/react/config'; * * const config = createFrankAuthConfig({ * publishableKey: 'pk_test_...', * userType: 'external', * theme: { * mode: 'dark', * colors: { * primary: '#3b82f6', * }, * }, * }); * ``` * * @example Advanced Usage * ```typescript * import { * ConfigManager, * ThemeManager, * AppearanceManager, * LocalizationManager, * OrganizationConfigManager * } from '@frank-auth/react/config'; * * const configManager = new ConfigManager({ * publishableKey: 'pk_test_...', * userType: 'external', * organizationId: 'org_123', * }); * * // Listen for configuration changes * configManager.subscribe((config) => { * console.log('Configuration updated:', config); * }); * ``` */ import {AppearanceManager} from './appearance'; import {LocalizationManager} from './localization'; import {OrganizationConfigManager, transformOrganizationSettings} from './organization'; import {ThemeManager} from './theme'; import type {ConfigValidationResult, FrankAuthUIConfig, OrganizationConfig, UserType} from './types'; import {CONFIG_PRESETS, DEFAULT_FRANK_AUTH_CONFIG,} from './defaults'; import { assertValidConfig, getConfigErrors, getConfigWarnings, isValidConfig, validateApiUrl, validateAppearanceConfig, validateComponentOverrides, validateFrankAuthConfig, validateLocale, validateLocalizationConfig, validateOrganizationConfig, validatePublishableKey, validateThemeConfig, validateUserType, } from './validators'; // ============================================================================ // Type Exports // ============================================================================ // Core configuration types export type { FrankAuthUIConfig, ConfigValidationError, ConfigValidationResult, } from './types'; // Theme types export type { ThemeMode, Typography, } from './types'; // Appearance types export type { AppearanceConfig, AppearanceMode, ComponentAppearance, BrandingConfig, ComponentSize, ColorVariant, } from './types'; // Localization types export type { LocalizationConfig, Locale, LocaleDirection, } from './types'; // Organization types export type { OrganizationConfig, OrganizationSettings, UserType, ComponentOverrides, } from './types'; // Extended organization types export type { OrganizationFeatureFlags, OrganizationLimits, OrganizationCompliance, ExtendedOrganizationConfig, } from './organization'; // ============================================================================ // Default Configuration Exports // ============================================================================ export { // Main defaults DEFAULT_FRANK_AUTH_CONFIG as defaultConfig, CONFIG_PRESETS as configPresets, // Theme defaults DEFAULT_THEME_CONFIG as defaultTheme, DEFAULT_COLOR_PALETTE as defaultColors, DEFAULT_TYPOGRAPHY as defaultTypography, DEFAULT_SPACING as defaultSpacing, DEFAULT_BORDER_RADIUS as defaultBorderRadius, DEFAULT_SHADOWS as defaultShadows, DEFAULT_ANIMATIONS as defaultAnimations, // Appearance defaults DEFAULT_APPEARANCE_CONFIG as defaultAppearance, DEFAULT_COMPONENT_APPEARANCE as defaultComponentAppearance, DEFAULT_LAYOUT_CONFIG as defaultLayout, DEFAULT_BRANDING_CONFIG as defaultBranding, // Localization defaults DEFAULT_LOCALIZATION_CONFIG as defaultLocalization, DEFAULT_LOCALE_MESSAGES as defaultMessages, // Organization defaults DEFAULT_ORGANIZATION_CONFIG as defaultOrganization, } from './defaults'; // ============================================================================ // Manager Class Exports // ============================================================================ export { ThemeManager, THEME_PRESETS as themePresets, createThemeManager, createDarkTheme, getThemeCSS, validateTheme, } from './theme'; export { AppearanceManager, INPUT_VARIANTS as inputVariants, BUTTON_VARIANTS as buttonVariants, CARD_VARIANTS as cardVariants, SIZE_CONFIGS as sizeConfigs, MODAL_SIZES as modalSizes, BREAKPOINTS as breakpoints, RESPONSIVE_UTILITIES as responsiveUtils, createAppearanceManager, getComponentClassNames, appearanceConfigToTailwind, createResponsiveProps, } from './appearance'; export { LocalizationManager, createLocalizationManager, detectBrowserLocale, getLocaleDirection, isRTL, createTranslationNamespace, } from './localization'; export { OrganizationConfigManager, createOrganizationConfigManager, transformOrganizationSettings, getFeaturesByTier, } from './organization'; // ============================================================================ // Validation Exports // ============================================================================ export { validateFrankAuthConfig, validateThemeConfig, validateAppearanceConfig, validateLocalizationConfig, validateOrganizationConfig, validateComponentOverrides, validatePublishableKey, validateApiUrl, validateUserType, validateLocale, assertValidConfig, isValidConfig, getConfigErrors, getConfigWarnings, } from './validators'; // ============================================================================ // Main Configuration Manager // ============================================================================ /** * Comprehensive configuration manager that orchestrates all configuration aspects */ export class ConfigManager { private config: FrankAuthUIConfig; private themeManager: ThemeManager; private appearanceManager: AppearanceManager; private localizationManager: LocalizationManager; private organizationManager?: OrganizationConfigManager; private listeners: Set<(config: FrankAuthUIConfig) => void> = new Set(); constructor(initialConfig: Partial<FrankAuthUIConfig>) { // Validate and merge with defaults this.config = { ...DEFAULT_FRANK_AUTH_CONFIG, ...initialConfig } as FrankAuthUIConfig; // Initialize managers this.themeManager = new ThemeManager(this.config.theme); this.appearanceManager = new AppearanceManager(this.config.appearance); this.localizationManager = new LocalizationManager(this.config.localization); // Initialize organization manager if organization config is provided if (this.config.organization) { this.organizationManager = new OrganizationConfigManager( this.config.organization, this.themeManager, this.appearanceManager ); } this.setupManagerListeners(); this.applyInitialConfiguration(); } /** * Get current configuration */ getConfig(): FrankAuthUIConfig { return { ...this.config }; } /** * Update configuration */ updateConfig(updates: Partial<FrankAuthUIConfig>): void { this.config = { ...this.config, ...updates }; // Update individual managers if (updates.theme) { this.themeManager.setTheme(updates.theme); } if (updates.appearance) { this.appearanceManager.updateConfig(updates.appearance); } if (updates.localization) { this.localizationManager.updateConfig(updates.localization); } if (updates.organization && this.organizationManager) { this.organizationManager.updateConfig(updates.organization); } this.notifyListeners(); } /** * Set organization configuration */ setOrganization(organization: OrganizationConfig): void { this.config.organization = organization; this.config.projectId = organization.id; // Create or update organization manager if (!this.organizationManager) { this.organizationManager = new OrganizationConfigManager( organization, this.themeManager, this.appearanceManager ); // Subscribe to organization changes this.organizationManager.subscribe((orgConfig) => { this.config.organization = orgConfig; this.notifyListeners(); }); } else { this.organizationManager.updateConfig(organization); } // Apply organization-specific UI configuration const orgUIConfig = this.organizationManager.generateUIConfig(); this.updateConfig(orgUIConfig); } /** * Get theme manager */ getThemeManager(): ThemeManager { return this.themeManager; } /** * Get appearance manager */ getAppearanceManager(): AppearanceManager { return this.appearanceManager; } /** * Get localization manager */ getLocalizationManager(): LocalizationManager { return this.localizationManager; } /** * Get organization manager */ getOrganizationManager(): OrganizationConfigManager | undefined { return this.organizationManager; } /** * Apply configuration to DOM */ applyToDOM(): void { this.themeManager.applyToDOM(); this.appearanceManager.applyToDOM(); // Apply locale direction const direction = this.localizationManager.getCurrentLocaleMetadata().direction; if (typeof document !== 'undefined') { document.documentElement.dir = direction; document.documentElement.lang = this.localizationManager.getCurrentLocale(); } } /** * Generate complete CSS for server-side rendering */ generateCSS(): string { let css = ''; // Theme CSS css += this.themeManager.generateCSSVariables(); css += '\n'; // Appearance CSS css += this.appearanceManager.generateCSS(); css += '\n'; return css; } /** * Validate current configuration */ validateConfig(): ConfigValidationResult { return validateFrankAuthConfig(this.config); } /** * Subscribe to configuration changes */ subscribe(callback: (config: FrankAuthUIConfig) => void): () => void { this.listeners.add(callback); return () => { this.listeners.delete(callback); }; } /** * Reset to default configuration */ reset(): void { this.config = { ...DEFAULT_FRANK_AUTH_CONFIG } as any; this.themeManager.setTheme(this.config.theme || {}); this.appearanceManager.updateConfig(this.config.appearance || {}); this.localizationManager.updateConfig(this.config.localization || {}); this.notifyListeners(); } /** * Destroy and cleanup */ destroy(): void { this.listeners.clear(); } // Private methods private setupManagerListeners(): void { // Listen to theme changes this.themeManager.subscribe((theme) => { this.config.theme = theme; this.notifyListeners(); }); // Listen to appearance changes this.appearanceManager.subscribe((appearance) => { this.config.appearance = appearance; this.notifyListeners(); }); // Listen to localization changes this.localizationManager.subscribe((locale) => { if (this.config.localization) { this.config.localization.defaultLocale = locale; } this.notifyListeners(); }); } private applyInitialConfiguration(): void { // Apply organization branding if available if (this.config.organization?.settings.branding) { this.themeManager.applyBranding({ logo: { url: this.config.organization.settings.branding.logo, alt: this.config.organization.name, }, colors: { primary: this.config.organization.settings.branding.primaryColor || '#3b82f6', secondary: this.config.organization.settings.branding.secondaryColor || '#64748b', }, fonts: { primary: 'Inter, ui-sans-serif, system-ui, sans-serif', }, customCSS: this.config.organization.settings.branding.customCSS, }); } // Apply to DOM if in browser environment if (typeof window !== 'undefined') { this.applyToDOM(); } } private notifyListeners(): void { this.listeners.forEach(callback => callback(this.config)); } } // ============================================================================ // Utility Functions // ============================================================================ /** * Create a complete Frank Auth configuration with validation */ export function createFrankAuthConfig(config: Partial<FrankAuthUIConfig>): FrankAuthUIConfig { const validate = validateFrankAuthConfig; // Validate configuration const validation = validate(config); if (!validation.isValid) { throw new Error(`Invalid configuration: ${validation.errors.map(e => e.message).join(', ')}`); } // Merge with defaults return { ...DEFAULT_FRANK_AUTH_CONFIG, ...config } as FrankAuthUIConfig; } /** * Create a configuration manager instance */ export function createConfigManager(config: Partial<FrankAuthUIConfig>): ConfigManager { return new ConfigManager(config); } /** * Create configuration from organization settings (useful for server-side setup) */ export function createConfigFromOrganization( publishableKey: string, userType: UserType, organizationSettings: any ): FrankAuthUIConfig { const organization = transformOrganizationSettings(organizationSettings); return createFrankAuthConfig({ publishableKey, userType, organization, }); } /** * Merge multiple configuration objects with proper type safety */ export function mergeConfigs(...configs: Partial<FrankAuthUIConfig>[]): FrankAuthUIConfig { let merged = { ...DEFAULT_FRANK_AUTH_CONFIG }; for (const config of configs) { merged = { ...merged, ...config, theme: { ...merged.theme, ...config.theme }, appearance: { ...merged.appearance, ...config.appearance, layout: { ...merged.appearance?.layout, ...config.appearance?.layout }, components: { ...merged.appearance?.components, ...config.appearance?.components }, branding: { ...merged.appearance?.branding, ...config.appearance?.branding }, }, localization: { ...merged.localization, ...config.localization }, organization: { ...merged.organization, ...config.organization }, features: { ...merged.features, ...config.features }, components: { ...merged.components, ...config.components }, }; } return merged as FrankAuthUIConfig; } /** * Create a configuration preset */ export function createConfigPreset( presetName: 'minimal' | 'enterprise' | 'b2b' | 'consumer', overrides?: Partial<FrankAuthUIConfig> ): Partial<FrankAuthUIConfig> { const preset = CONFIG_PRESETS[presetName]; if (!preset) { throw new Error(`Unknown preset: ${presetName}`); } return overrides ? mergeConfigs(preset, overrides) : preset; } // ============================================================================ // Re-export everything for convenience // ============================================================================ // Export all types export * from './types'; // Export main manager as default export { ConfigManager as default };