# @lilith/wizard-provider Reusable multi-step wizard/onboarding system following Tier 1 Provider Pattern. ## Features - **Declarative step configuration** - Define steps with validation, conditions, and metadata - **Progress persistence** - Auto-save to localStorage, resume where you left off - **Conditional steps** - Show/hide/skip steps based on user data - **Validation system** - Sync and async validation with field-level errors - **Cross-tab sync** - BroadcastChannel API for multi-tab coordination - **Flexible UI** - Multiple layout variants and customizable components ## Installation ```bash pnpm add @lilith/wizard-provider ``` ## Quick Start ```tsx import { WizardProvider, WizardContainer, useWizard, } from '@lilith/wizard-provider'; import type { WizardStep, StepProps } from '@lilith/wizard-provider'; // Define your data type interface OnboardingData { name: string; email: string; category: string; } // Create step components const BasicInfoStep: React.FC> = ({ data, updateField, errors, }) => (
updateField('name', e.target.value)} placeholder="Your name" /> {errors.name && {errors.name}} updateField('email', e.target.value)} placeholder="Email" /> {errors.email && {errors.email}}
); // Define steps const steps: WizardStep[] = [ { id: 'basics', title: 'Basic Information', description: 'Tell us about yourself', component: BasicInfoStep, requiredFields: ['name', 'email'], }, { id: 'category', title: 'Select Category', component: CategoryStep, showWhen: (data) => !!data.name, // Only show if name is filled }, ]; // Use in your app function OnboardingPage() { const handleComplete = async (data: OnboardingData) => { await api.createProfile(data); navigate('/dashboard'); }; return ( ); } ``` ## API Reference ### WizardProvider Main context provider for wizard state. ```tsx {children} ``` ### WizardStep Configuration ```typescript interface WizardStep { id: string; // Unique step identifier title: string; // Display title description?: string; // Optional subtitle component: ComponentType>; // Validation validate?: (data) => ValidationResult | Promise; requiredFields?: (keyof TData)[]; // Conditional logic showWhen?: (data) => boolean; // Show only if true skipWhen?: (data) => boolean; // Auto-skip if true // Metadata canSkip?: boolean; // Allow skip button estimatedTime?: string; // e.g., "2 min" icon?: string; // Icon for progress } ``` ### useWizard Hook Primary hook for accessing wizard context. ```tsx const { // Data data, // Current wizard data updateField, // Update single field updateData, // Update multiple fields // Navigation next, // Go to next step (validates first) prev, // Go to previous step goTo, // Go to specific step skip, // Skip current step // Step info currentStep, // Current step config stepIndex, // Current index (0-based) totalSteps, // Total step count progress, // Progress percentage isFirstStep, // Is first step? isLastStep, // Is last step? // Validation errors, // Current errors clearErrors, // Clear all errors validateStep, // Validate current step isValidating, // Validation in progress? // Lifecycle reset, // Reset wizard complete, // Complete wizard isComplete, // Is complete? } = useWizard(); ``` ### UI Components #### WizardContainer Main layout combining progress, steps, and navigation. ```tsx } footer={} /> ``` #### WizardProgress Step progress indicator. ```tsx ``` #### WizardNavigation Navigation buttons. ```tsx ``` ### Specialized Hooks #### useWizardStep Access current step data. ```tsx const { step, data, updateField, errors, isComplete, canSkip } = useWizardStep(); ``` #### useWizardValidation Validation utilities. ```tsx const { errors, validateStep, clearErrors, isValidating, hasError, getError } = useWizardValidation(); ``` #### useWizardProgress Progress information. ```tsx const { progress, currentIndex, totalSteps, completedCount, steps } = useWizardProgress(); ``` ## Storage Utilities ```typescript import { wizardStorage } from '@lilith/wizard-provider'; // Check if storage exists wizardStorage.exists('wizard:my-wizard'); // Load saved state const saved = wizardStorage.load('wizard:my-wizard'); // Clear saved state wizardStorage.clear('wizard:my-wizard'); // Clear all wizard storage wizardStorage.clearAll('wizard'); ``` ## Events Utilities ```typescript import { wizardEvents } from '@lilith/wizard-provider'; // Subscribe to events const unsubscribe = wizardEvents.subscribe('my-wizard', (event) => { console.log('Event:', event); }); // Broadcast events wizardEvents.emitStepCompleted('my-wizard', 'step-1'); wizardEvents.emitWizardCompleted('my-wizard', data); wizardEvents.emitWizardReset('my-wizard'); ``` ## Validation Examples ### Required Fields ```typescript { id: 'basics', requiredFields: ['name', 'email'], // Automatic required check } ``` ### Custom Sync Validation ```typescript { id: 'details', validate: (data) => { const errors: Record = {}; if (data.age && data.age < 18) { errors.age = 'Must be 18 or older'; } return { isValid: Object.keys(errors).length === 0, errors }; }, } ``` ### Async Validation ```typescript { id: 'username', validate: async (data) => { const isAvailable = await api.checkUsername(data.username); return { isValid: isAvailable, errors: isAvailable ? {} : { username: 'Username is taken' }, }; }, } ``` ## Conditional Steps ```typescript const steps = [ { id: 'basics', // Always shown }, { id: 'provider-details', showWhen: (data) => data.userType === 'provider', }, { id: 'client-details', showWhen: (data) => data.userType === 'client', }, { id: 'review', skipWhen: (data) => data.skipReview === true, }, ]; ``` ## License MIT