react-form-steps 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/FormSteps.tsx","../src/context/FormStepsContext.tsx","../src/utils/merge.ts","../src/utils/diff.ts","../src/persistence/browserStorage.ts","../src/utils/fileStorage.ts","../src/components/DraftBanner.tsx","../src/components/Step.tsx","../src/hooks/useFormSteps.ts"],"sourcesContent":["import React, { useState, useEffect, useContext } from 'react';\nimport { FormStepsProvider, FormStepsContext } from '../context/FormStepsContext';\nimport { FormStepsProps, DraftData } from '../types';\nimport { browserStoragePersistence } from '../persistence/browserStorage';\nimport { DraftBanner } from './DraftBanner';\n\ninterface InnerContentProps {\n children: React.ReactNode;\n showBanner: boolean;\n draft: DraftData | null;\n setShowBanner: (val: boolean) => void;\n setDraft: (val: DraftData | null) => void;\n bannerConfig?: any;\n renderDraftBanner?: any;\n transition?: 'slide' | 'fade' | 'none';\n}\n\nconst InnerContent: React.FC<InnerContentProps & { Autofilldata?: boolean }> = ({\n children,\n showBanner,\n draft,\n setShowBanner,\n setDraft,\n bannerConfig,\n Autofilldata,\n renderDraftBanner,\n transition = 'none'\n}) => {\n const context = useContext(FormStepsContext);\n const contextResume = context?.resumeDraft;\n\n useEffect(() => {\n if (Autofilldata && draft && contextResume) {\n contextResume(draft);\n setShowBanner(false);\n }\n }, [Autofilldata, draft, contextResume, setShowBanner]);\n\n if (!context) return <>{children}</>;\n\n const { resumeDraft, clearDraft, currentStep } = context;\n\n const handleResume = () => {\n if (draft && resumeDraft) {\n resumeDraft(draft);\n setShowBanner(false);\n }\n };\n\n const handleFresh = () => {\n clearDraft();\n setShowBanner(false);\n setDraft(null);\n };\n\n const handleDismiss = () => setShowBanner(false);\n\n return (\n <>\n <style>{`\n .step-transition-wrapper {\n position: relative;\n width: 100%;\n }\n .step-container {\n width: 100%;\n }\n .step-transition-fade .step-active {\n animation: formStepsFadeIn 0.4s ease-out;\n }\n .step-transition-slide .step-active {\n animation: formStepsSlideIn 0.4s cubic-bezier(0.4, 0, 0.2, 1);\n }\n @keyframes formStepsFadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes formStepsSlideIn {\n from { opacity: 0; transform: translateX(20px); }\n to { opacity: 1; transform: translateX(0); }\n }\n `}</style>\n {showBanner && draft && !Autofilldata && (\n renderDraftBanner ? (\n renderDraftBanner({ draft, resume: handleResume, startFresh: handleFresh, dismiss: handleDismiss })\n ) : (\n <DraftBanner\n {...bannerConfig}\n onResume={handleResume}\n onFresh={handleFresh}\n onDismiss={handleDismiss}\n />\n )\n )}\n <div className={`form-steps-wrapper step-transition-wrapper step-transition-${transition}`}>\n {React.Children.map(children, child => {\n if (!React.isValidElement(child)) return child;\n\n const childProps = child.props as any;\n\n // If it's a Step (has an index), wrap it and handle visibility\n if (childProps.hasOwnProperty('index')) {\n const isActive = childProps.index === currentStep;\n return (\n <div\n className={`step-container ${isActive ? 'step-active' : 'step-hidden'}`}\n style={{ display: isActive ? 'block' : 'none' }}\n >\n {child}\n </div>\n );\n }\n\n // Persistent UI (like sidebar) stays visible\n return child;\n })}\n </div>\n </>\n );\n};\n\nexport const FormSteps: React.FC<FormStepsProps> = (props) => {\n const [draft, setDraft] = useState<DraftData | null>(null);\n const [showBanner, setShowBanner] = useState(false);\n\n useEffect(() => {\n const isBrowserStorage = props.storageStrategy === 'localStorage' || props.storageStrategy === 'sessionStorage';\n\n if (props.formKey && isBrowserStorage) {\n const savedDraft = browserStoragePersistence.load(\n props.formKey,\n props.storageStrategy as 'localStorage' | 'sessionStorage',\n props.draftTTL\n );\n if (savedDraft) {\n setDraft(savedDraft);\n setShowBanner(true);\n if (props.onDraftFound) props.onDraftFound(savedDraft);\n }\n }\n }, [props.formKey, props.storageStrategy, props.draftTTL, props.onDraftFound]);\n\n // Load from custom asyncStorage asynchronously\n useEffect(() => {\n const loadAsyncDraft = async () => {\n if (props.formKey && props.asyncStorage) {\n try {\n const raw = await props.asyncStorage.getItem(`form-steps-draft:${props.formKey}`);\n if (raw) {\n const savedDraft: DraftData = JSON.parse(raw);\n if (props.draftTTL) {\n const now = Date.now();\n const expired = now - savedDraft.savedAt > props.draftTTL * 1000;\n if (expired) {\n await props.asyncStorage.removeItem(`form-steps-draft:${props.formKey}`);\n return;\n }\n }\n setDraft(savedDraft);\n setShowBanner(true);\n if (props.onDraftFound) props.onDraftFound(savedDraft);\n }\n } catch (e) {\n console.error('Failed to load async draft', e);\n }\n }\n };\n loadAsyncDraft();\n }, [props.formKey, props.asyncStorage, props.draftTTL, props.onDraftFound]);\n\n // Calculate active index manually\n let stepCounter = 0;\n const childrenWithFixedIndex = React.Children.map(props.children, (child) => {\n if (React.isValidElement(child)) {\n // Check if it's a Step component (has label prop)\n if (child.props.label && !child.props.hasOwnProperty('index')) {\n return React.cloneElement(child as React.ReactElement<any>, { index: stepCounter++ });\n }\n }\n return child;\n });\n\n return (\n <FormStepsProvider\n formKey={props.formKey}\n persistence={{\n storageType: props.storageStrategy || 'none',\n formKey: props.formKey,\n onAutoSave: props.onAutoSave,\n onClearDraft: props.onClearDraft,\n draftTTL: props.draftTTL,\n asyncStorage: props.asyncStorage\n }}\n allowJump={props.allowJump}\n unrestrictedNav={props.unrestrictedNav}\n onStepEnter={props.onStepEnter}\n onStepComplete={props.onStepComplete}\n onDataChange={props.onDataChange}\n onClear={() => {\n setDraft(null);\n setShowBanner(false);\n }}\n defaultValues={props.defaultValues}\n onSubmit={props.onSubmit}\n >\n <InnerContent\n showBanner={showBanner}\n draft={draft}\n setShowBanner={setShowBanner}\n setDraft={setDraft}\n bannerConfig={props.bannerConfig}\n Autofilldata={props.Autofilldata}\n renderDraftBanner={props.renderDraftBanner}\n transition={props.transition}\n >\n {childrenWithFixedIndex}\n </InnerContent>\n </FormStepsProvider>\n );\n};\n","import React, { createContext, useState, useCallback, useMemo, useEffect, useRef } from 'react';\nimport { FormStepsContextType, StepInfo, StepStatus, PersistenceOptions, DraftData } from '../types';\nimport { mergeStepData } from '../utils/merge';\nimport { getChangedFieldsMap } from '../utils/diff';\nimport { browserStoragePersistence } from '../persistence/browserStorage';\nimport { serializeFiles, deserializeFiles } from '../utils/fileStorage';\nimport debounce from 'lodash.debounce';\n\nexport const FormStepsContext = createContext<FormStepsContextType | undefined>(undefined);\n\ninterface ProviderProps {\n children: React.ReactNode;\n formKey?: string;\n persistence?: PersistenceOptions;\n allowJump?: boolean;\n unrestrictedNav?: boolean;\n onStepEnter?: (idx: number) => void;\n onStepComplete?: (idx: number) => void;\n onClear?: () => void;\n defaultValues?: any;\n onSubmit: (payload: any, diff: any) => void | Promise<void>;\n onDataChange?: (data: any) => void;\n onDraftFound?: (draft: any) => void;\n}\n\nexport const FormStepsProvider: React.FC<ProviderProps> = ({\n children,\n formKey,\n persistence,\n allowJump,\n unrestrictedNav,\n onStepEnter,\n onStepComplete,\n onClear,\n defaultValues,\n onSubmit,\n onDataChange,\n}) => {\n const [currentStep, setCurrentStep] = useState(0);\n const [stepConfigs, setStepConfigs] = useState<Record<number, { label: string; schema?: any }>>({});\n const [allStepData, setAllStepData] = useState<Record<number, any>>({});\n const [completedSteps, setCompletedSteps] = useState<Record<number, boolean>>({});\n const [resumedDraftData, setResumedDraftData] = useState<any>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n\n const isEditMode = !!defaultValues;\n\n // Step registration\n const registerStep = useCallback((index: number, label: string, schema?: any) => {\n setStepConfigs((prev) => ({ ...prev, [index]: { label, schema } }));\n }, []);\n\n const unregisterStep = useCallback((index: number) => {\n setStepConfigs((prev) => {\n const next = { ...prev };\n delete next[index];\n return next;\n });\n }, []);\n\n // Data management\n const updateStepData = useCallback((index: number, data: any) => {\n setAllStepData((prev) => ({ ...prev, [index]: data }));\n }, []);\n\n const mergedData = useMemo(() => {\n const stepData = mergeStepData(allStepData);\n let result = { ...resumedDraftData };\n\n Object.keys(stepData).forEach(key => {\n const val = stepData[key];\n if (val !== undefined && val !== null) {\n result[key] = val;\n }\n });\n\n if (isEditMode) {\n result = { ...defaultValues, ...result };\n }\n return result;\n }, [allStepData, defaultValues, isEditMode, resumedDraftData]);\n\n // Trigger onDataChange\n useEffect(() => {\n if (onDataChange) {\n onDataChange(mergedData);\n }\n }, [mergedData, onDataChange]);\n\n const changedFields = useMemo(() => {\n if (!isEditMode) return {};\n return getChangedFieldsMap(defaultValues, mergedData);\n }, [isEditMode, defaultValues, mergedData]);\n\n // Status tracking\n const steps: StepInfo[] = useMemo(() => {\n const indices = Object.keys(stepConfigs).map(Number).sort((a, b) => a - b);\n return indices.map((idx) => {\n let status: StepStatus = 'pending';\n if (idx === currentStep) status = 'active';\n else if (completedSteps[idx]) status = 'complete';\n return {\n index: idx,\n label: stepConfigs[idx].label,\n status,\n };\n });\n }, [stepConfigs, currentStep, completedSteps]);\n\n // Persistence\n const onAutoSaveRef = useRef(persistence?.onAutoSave);\n useEffect(() => {\n onAutoSaveRef.current = persistence?.onAutoSave;\n }, [persistence?.onAutoSave]);\n\n const remoteSave = useMemo(() => {\n if (persistence?.storageType === 'database') {\n return debounce(async (index: number, data: any, merged: any) => {\n try {\n if (onAutoSaveRef.current) {\n await onAutoSaveRef.current(index, data, merged);\n }\n } catch (e) {\n console.error('Remote auto-save failed', e);\n }\n }, 800);\n }\n return null;\n }, [persistence?.storageType]);\n\n // Clean up debounce on unmount\n useEffect(() => {\n return () => {\n if (remoteSave) {\n remoteSave.cancel();\n }\n };\n }, [remoteSave]);\n\n const triggerPersistence = useCallback(async (stepIdx: number, data: any, merged: any, completedOverride?: Record<number, boolean>) => {\n if (!formKey) return;\n\n const stepsToSave = completedOverride || completedSteps;\n\n // 1. AsyncStorage Persistence\n if (persistence?.asyncStorage) {\n try {\n const serializedData = await serializeFiles(merged);\n await persistence.asyncStorage.setItem(`form-steps-draft:${formKey}`, JSON.stringify({\n stepIndex: stepIdx,\n mergedData: serializedData,\n completedSteps: stepsToSave,\n savedAt: Date.now(),\n }));\n } catch (e) {\n console.warn('AsyncStorage draft save failed:', e);\n }\n }\n\n // 2. Browser Storage Persistence\n if (persistence?.storageType === 'localStorage' || persistence?.storageType === 'sessionStorage') {\n try {\n const serializedData = await serializeFiles(merged);\n browserStoragePersistence.save(formKey, {\n stepIndex: stepIdx,\n mergedData: serializedData,\n completedSteps: stepsToSave,\n savedAt: Date.now(),\n }, persistence.storageType);\n } catch (e) {\n console.warn('Browser storage draft save failed:', e);\n }\n }\n\n if (remoteSave) {\n remoteSave(stepIdx, data, merged);\n }\n }, [formKey, persistence, remoteSave, completedSteps]);\n\n // Analytics Callbacks\n useEffect(() => {\n if (onStepEnter) onStepEnter(currentStep);\n }, [currentStep, onStepEnter]);\n\n // Error Summary\n const getAllErrors = useCallback(() => {\n const errors: Record<number, any> = {};\n Object.keys(stepConfigs).forEach(idx => {\n const index = Number(idx);\n const schema = stepConfigs[index].schema;\n if (schema) {\n const result = schema.safeParse(mergedData);\n if (!result.success) {\n errors[index] = result.error.format();\n }\n }\n });\n return errors;\n }, [stepConfigs, mergedData]);\n\n const resumeDraft = useCallback((draft: DraftData) => {\n if (draft.mergedData) {\n setAllStepData({});\n const cleanData = deserializeFiles(draft.mergedData);\n setResumedDraftData(cleanData);\n setCurrentStep(draft.stepIndex);\n\n if (draft.completedSteps) {\n setCompletedSteps(draft.completedSteps);\n } else {\n const newCompleted: Record<number, boolean> = {};\n for (let i = 0; i < draft.stepIndex; i++) {\n newCompleted[i] = true;\n }\n setCompletedSteps(newCompleted);\n }\n }\n }, []);\n\n const clearDraft = useCallback(async () => {\n console.log('๐Ÿงน [clearDraft] Running... formKey:', formKey, 'persistence.asyncStorage exists:', !!persistence?.asyncStorage);\n\n // 1. Clear Browser Storage\n if (formKey) {\n try {\n console.log('๐Ÿงน [clearDraft] Clearing browser local/session storage...');\n browserStoragePersistence.clear(formKey, 'localStorage');\n browserStoragePersistence.clear(formKey, 'sessionStorage');\n } catch (e) {\n console.warn('๐Ÿงน [clearDraft] Browser storage clear failed:', e);\n }\n }\n\n // 1.5 Clear AsyncStorage\n if (formKey && persistence?.asyncStorage) {\n try {\n console.log('๐Ÿ“ฑ [clearDraft] Removing AsyncStorage key:', `form-steps-draft:${formKey}`);\n await persistence.asyncStorage.removeItem(`form-steps-draft:${formKey}`);\n console.log('๐Ÿ“ฑ [clearDraft] AsyncStorage key removed successfully!');\n } catch (e) {\n console.warn('๐Ÿ“ฑ [clearDraft] AsyncStorage clear failed:', e);\n }\n } else {\n console.log('๐Ÿ“ฑ [clearDraft] Skipping AsyncStorage clear. formKey:', formKey, 'hasAsyncStorage:', !!persistence?.asyncStorage);\n }\n\n // 2. Clear Database/Remote Storage\n if (persistence?.onClearDraft) {\n console.log('โ˜๏ธ Calling remote clearDraft callback...');\n await persistence.onClearDraft();\n }\n\n // 3. Clear Internal State\n setAllStepData({});\n setResumedDraftData(null);\n setCompletedSteps({});\n setCurrentStep(0);\n\n // 4. Sync with external state (Redux)\n if (onDataChange) {\n onDataChange({});\n }\n\n // 5. Notify Parent UI\n if (onClear) {\n console.log('๐Ÿงน [clearDraft] Calling onClear callback...');\n onClear();\n }\n console.log('๐Ÿงน [clearDraft] Finished clearDraft execution!');\n }, [formKey, persistence, onDataChange, onClear]);\n\n // Navigation\n const goNext = useCallback(async () => {\n const isLastStep = currentStep === steps.length - 1;\n console.log('๐Ÿ‘‰ [goNext] Triggered! currentStep:', currentStep, 'steps.length:', steps.length, 'isLastStep:', isLastStep);\n\n // Mark current as complete\n const nextCompleted = { ...completedSteps, [currentStep]: true };\n setCompletedSteps(nextCompleted);\n if (onStepComplete) onStepComplete(currentStep);\n\n // Auto-save logic: Don't save draft if it's the last step (we're about to clear it)\n if (!isLastStep) {\n const currentData = allStepData[currentStep] || {};\n console.log('๐Ÿ‘‰ [goNext] Saving step draft for index:', currentStep + 1);\n // We pass nextCompleted here to ensure the save includes the step we just finished!\n await triggerPersistence(currentStep + 1, currentData, mergedData, nextCompleted);\n }\n\n if (isLastStep) {\n const errors = getAllErrors();\n if (Object.keys(errors).length > 0) {\n console.warn('Cannot submit form: validation errors found in steps', errors);\n const firstErrorStep = Object.keys(errors).map(Number).sort((a, b) => a - b)[0];\n setCurrentStep(firstErrorStep);\n return;\n }\n\n console.log('๐Ÿ‘‰ [goNext] isLastStep is TRUE! Submitting form. Calling clearDraft()...');\n setIsSubmitting(true);\n try {\n const diff = isEditMode ? getChangedFieldsMap(defaultValues, mergedData) : mergedData;\n // Immediately clear the draft before submitting to avoid unmount race conditions\n await clearDraft();\n console.log('๐Ÿ‘‰ [goNext] clearDraft completed! Calling onSubmit callback...');\n await onSubmit(mergedData, diff);\n console.log('๐Ÿ‘‰ [goNext] onSubmit callback completed successfully!');\n } finally {\n setIsSubmitting(false);\n }\n } else {\n console.log('๐Ÿ‘‰ [goNext] Moving to next step:', currentStep + 1);\n setCurrentStep((prev) => prev + 1);\n }\n }, [currentStep, steps.length, allStepData, triggerPersistence, mergedData, isEditMode, defaultValues, onSubmit, formKey, persistence?.storageType, onStepComplete, getAllErrors, clearDraft, completedSteps]);\n\n const goBack = useCallback(() => {\n setCurrentStep((prev) => Math.max(0, prev - 1));\n }, []);\n\n const goToStep = useCallback((index: number) => {\n if (!allowJump) return;\n\n if (unrestrictedNav || index < currentStep) {\n setCurrentStep(index);\n } else {\n let canJump = true;\n for (let i = 0; i < index; i++) {\n if (!completedSteps[i] && i !== currentStep) {\n canJump = false;\n break;\n }\n }\n if (canJump) {\n setCurrentStep(index);\n }\n }\n }, [allowJump, unrestrictedNav, currentStep, completedSteps]);\n\n\n const contextValue: FormStepsContextType = useMemo(() => ({\n values: mergedData,\n mergedData,\n resumedDraftData,\n currentStep,\n steps,\n isEditMode,\n changedFields,\n isSubmitting,\n goNext,\n goBack,\n goToStep,\n getAllErrors,\n registerStep,\n unregisterStep,\n updateStepData,\n resumeDraft,\n clearDraft,\n }), [\n mergedData,\n resumedDraftData,\n currentStep,\n steps,\n isEditMode,\n changedFields,\n isSubmitting,\n goNext,\n goBack,\n goToStep,\n getAllErrors,\n registerStep,\n unregisterStep,\n updateStepData,\n resumeDraft,\n clearDraft\n ]);\n\n return (\n <FormStepsContext.Provider value={contextValue}>\n {children}\n </FormStepsContext.Provider>\n );\n};\n","/**\n * Merges data from all steps into a single object.\n * Currently supports flat merging, but can be extended for nested structures if needed.\n */\nexport function mergeStepData(allStepData: Record<number, any>): any {\n let merged = {};\n \n // Sort keys to ensure consistent merging order\n const sortedIndices = Object.keys(allStepData)\n .map(Number)\n .sort((a, b) => a - b);\n\n sortedIndices.forEach((index) => {\n merged = { ...merged, ...allStepData[index] };\n });\n\n return merged;\n}\n","/**\n * Simple diff utility to find changed fields between two objects.\n * Returns an object containing only the fields that are different in 'current' compared to 'initial'.\n */\nexport function getDiff(initial: any, current: any): any {\n const diff: any = {};\n\n if (!initial) return current;\n\n Object.keys(current).forEach((key) => {\n if (JSON.stringify(initial[key]) !== JSON.stringify(current[key])) {\n diff[key] = current[key];\n }\n });\n\n return diff;\n}\n\n/**\n * Returns a map of changed field keys.\n */\nexport function getChangedFieldsMap(initial: any, current: any): Record<string, boolean> {\n const changed: Record<string, boolean> = {};\n \n if (!initial) {\n Object.keys(current).forEach(key => changed[key] = true);\n return changed;\n }\n\n Object.keys(current).forEach((key) => {\n if (JSON.stringify(initial[key]) !== JSON.stringify(current[key])) {\n changed[key] = true;\n }\n });\n\n return changed;\n}\n","import { DraftData } from '../types';\n\nconst PREFIX = 'form-steps-draft';\n\nexport const browserStoragePersistence = {\n save: (formKey: string, data: DraftData, type: 'localStorage' | 'sessionStorage') => {\n try {\n if (typeof window === 'undefined') return;\n const storage = type === 'localStorage' ? window.localStorage : window.sessionStorage;\n if (!storage) return;\n storage.setItem(`${PREFIX}:${formKey}`, JSON.stringify(data));\n } catch (e) {\n console.warn('Browser storage save failed:', e);\n }\n },\n\n load: (formKey: string, type: 'localStorage' | 'sessionStorage', ttl?: number): DraftData | null => {\n try {\n if (typeof window === 'undefined') return null;\n const storage = type === 'localStorage' ? window.localStorage : window.sessionStorage;\n if (!storage) return null;\n const raw = storage.getItem(`${PREFIX}:${formKey}`);\n if (!raw) return null;\n\n const data: DraftData = JSON.parse(raw);\n \n // Check TTL\n if (ttl) {\n const now = Date.now();\n const expired = now - data.savedAt > ttl * 1000;\n if (expired) {\n try {\n storage.removeItem(`${PREFIX}:${formKey}`);\n } catch (rmError) {\n // Ignore remove failure\n }\n return null;\n }\n }\n\n return data;\n } catch (e) {\n console.warn('Browser storage load failed:', e);\n return null;\n }\n },\n\n clear: (formKey: string, type: 'localStorage' | 'sessionStorage') => {\n try {\n if (typeof window === 'undefined') return;\n const storage = type === 'localStorage' ? window.localStorage : window.sessionStorage;\n if (!storage) return;\n storage.removeItem(`${PREFIX}:${formKey}`);\n } catch (e) {\n console.warn('Browser storage clear failed:', e);\n }\n }\n};\n","/**\n * Utility to handle File objects in JSON storage (LocalStorage/SessionStorage)\n * by converting them to Base64 strings and back.\n * \n * โš ๏ธ WARNING FOR REACT NATIVE / MOBILE DEVELOPERS:\n * Serializing files to Base64 and storing them in AsyncStorage is highly discouraged\n * due to AsyncStorage storage limits (typically 6MB on Android). In React Native,\n * file picker outputs should keep their reference 'uri' paths as plain strings rather\n * than converting full binary payloads to Base64.\n */\n\nexport const serializeFiles = async (data: any): Promise<any> => {\n if (!data) return data;\n\n // Handle File objects\n if (typeof File !== 'undefined' && data instanceof File) {\n console.log('๐Ÿ“ฆ Serializing file:', data.name);\n const base64 = await fileToBase64(data);\n return {\n __is_file: true,\n name: data.name,\n type: data.type,\n data: base64,\n };\n }\n\n // Handle FileList (the common format for file inputs)\n if (typeof FileList !== 'undefined' && data instanceof FileList) {\n const files = Array.from(data);\n return {\n __is_file_list: true,\n files: await Promise.all(files.map(serializeFiles)),\n };\n }\n\n // Handle Arrays\n if (Array.isArray(data)) {\n return Promise.all(data.map(serializeFiles));\n }\n\n // Handle Objects (Recursion)\n if (typeof data === 'object' && data !== null) {\n const result: any = {};\n for (const key in data) {\n if (Object.prototype.hasOwnProperty.call(data, key)) {\n result[key] = await serializeFiles(data[key]);\n }\n }\n return result;\n }\n\n return data;\n};\n\nexport const deserializeFiles = (data: any): any => {\n if (!data) return data;\n\n // Restore File from our custom format\n if (data && typeof data === 'object' && data.__is_file && data.data) {\n console.log('๐Ÿ“‚ Deserializing file:', data.name);\n return dataUrlToFile(data.data, data.name, data.type);\n }\n\n // Restore FileList from our custom format\n if (data && typeof data === 'object' && data.__is_file_list && Array.isArray(data.files)) {\n const files = data.files.map(deserializeFiles);\n if (typeof DataTransfer !== 'undefined') {\n const dt = new DataTransfer();\n files.forEach((f: any) => dt.items.add(f));\n return dt.files;\n }\n return files; // Fallback to array if DataTransfer is not available (e.g. React Native)\n }\n\n // Handle Arrays\n if (Array.isArray(data)) {\n return data.map(deserializeFiles);\n }\n\n // Handle Objects\n if (typeof data === 'object' && data !== null) {\n const result: any = {};\n for (const key in data) {\n if (Object.prototype.hasOwnProperty.call(data, key)) {\n result[key] = deserializeFiles(data[key]);\n }\n }\n return result;\n }\n\n return data;\n};\n\nconst fileToBase64 = (file: File): Promise<string> => {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.readAsDataURL(file);\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = (error) => reject(error);\n });\n};\n\nconst dataUrlToFile = (dataUrl: string, filename: string, mimeType: string): any => {\n if (typeof File === 'undefined') {\n return { uri: dataUrl, name: filename, type: mimeType };\n }\n const arr = dataUrl.split(',');\n const bstr = typeof atob !== 'undefined' ? atob(arr[1]) : '';\n let n = bstr.length;\n const u8arr = new Uint8Array(n);\n while (n--) {\n u8arr[n] = bstr.charCodeAt(n);\n }\n return new File([u8arr], filename, { type: mimeType });\n};\n","import React from 'react';\nimport { RotateCcw, Play, X } from 'lucide-react';\n\ninterface DraftBannerProps {\n title?: string;\n description?: string;\n resumeLabel?: string;\n freshLabel?: string;\n onResume: () => void;\n onFresh: () => void;\n onDismiss: () => void;\n className?: string;\n}\n\nexport const DraftBanner: React.FC<DraftBannerProps> = ({\n title = 'Draft Found',\n description = 'You have a saved draft from a previous session. Would you like to resume where you left off?',\n resumeLabel = 'Resume Draft',\n freshLabel = 'Start Fresh',\n onResume,\n onFresh,\n onDismiss,\n className = '',\n}) => {\n return (\n <div \n className={`form-steps-draft-banner ${className}`}\n style={{\n background: 'linear-gradient(135deg, #1e293b 0%, #0f172a 100%)',\n color: '#f8fafc',\n padding: '1rem 1.5rem',\n borderRadius: '0.75rem',\n boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: '1.5rem',\n marginBottom: '2rem',\n border: '1px solid #334155',\n fontFamily: 'Inter, system-ui, sans-serif',\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>\n <div style={{ \n background: '#3b82f6', \n padding: '0.5rem', \n borderRadius: '0.5rem',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center'\n }}>\n <RotateCcw size={20} />\n </div>\n <div>\n <h4 style={{ margin: 0, fontSize: '0.875rem', fontWeight: 600 }}>{title}</h4>\n <p style={{ margin: 0, fontSize: '0.75rem', color: '#94a3b8' }}>{description}</p>\n </div>\n </div>\n\n <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>\n <button\n onClick={onFresh}\n style={{\n background: 'transparent',\n border: 'none',\n color: '#94a3b8',\n fontSize: '0.75rem',\n fontWeight: 500,\n cursor: 'pointer',\n padding: '0.5rem 0.75rem',\n borderRadius: '0.375rem',\n transition: 'all 0.2s',\n }}\n onMouseOver={(e) => (e.currentTarget.style.color = '#f8fafc')}\n onMouseOut={(e) => (e.currentTarget.style.color = '#94a3b8')}\n >\n {freshLabel}\n </button>\n <button\n onClick={onResume}\n style={{\n background: '#3b82f6',\n border: 'none',\n color: 'white',\n fontSize: '0.75rem',\n fontWeight: 600,\n cursor: 'pointer',\n padding: '0.5rem 1rem',\n borderRadius: '0.375rem',\n display: 'flex',\n alignItems: 'center',\n gap: '0.5rem',\n transition: 'all 0.2s',\n boxShadow: '0 4px 6px -1px rgba(59, 130, 246, 0.3)',\n }}\n onMouseOver={(e) => (e.currentTarget.style.transform = 'translateY(-1px)')}\n onMouseOut={(e) => (e.currentTarget.style.transform = 'translateY(0)')}\n >\n <Play size={14} />\n {resumeLabel}\n </button>\n <button\n onClick={onDismiss}\n style={{\n background: 'transparent',\n border: 'none',\n color: '#475569',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: '0.25rem',\n }}\n >\n <X size={16} />\n </button>\n </div>\n </div>\n );\n};\n","import React, { useEffect } from 'react';\nimport { useForm, FormProvider } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { StepProps } from '../types';\nimport { useFormSteps } from '../hooks/useFormSteps';\n\nexport const Step: React.FC<StepProps> = ({ children, label, schema, index }) => {\n const { \n mergedData, \n currentStep, \n resumedDraftData, \n registerStep, \n unregisterStep, \n updateStepData, \n goNext \n } = useFormSteps();\n\n const methods = useForm({\n resolver: schema ? zodResolver(schema) : undefined,\n defaultValues: mergedData,\n mode: 'onTouched',\n });\n\n const { handleSubmit, watch, reset } = methods;\n\n // Sync form with mergedData when the step becomes active OR when a draft is resumed\n useEffect(() => {\n if (mergedData && Object.keys(mergedData).length > 0) {\n reset(mergedData, { keepDefaultValues: true });\n } else {\n reset({});\n }\n }, [currentStep, reset, resumedDraftData]);\n\n // Register step with context\n useEffect(() => {\n if (typeof index === 'number') {\n registerStep(index, label, schema);\n return () => unregisterStep(index);\n }\n }, [index, label, schema, registerStep, unregisterStep]);\n\n // Update context data when fields change\n useEffect(() => {\n const subscription = watch((value) => {\n if (typeof index === 'number') {\n updateStepData(index, value);\n }\n });\n return () => subscription.unsubscribe();\n }, [watch, index, updateStepData]);\n\n\n return (\n <FormProvider {...methods}>\n <form \n onSubmit={handleSubmit(async () => {\n await goNext();\n })}\n id={`step-form-${index}`}\n className=\"form-step-container\"\n >\n {children}\n </form>\n </FormProvider>\n );\n};\n","import { useContext } from 'react';\nimport { useFormContext } from 'react-hook-form';\nimport { FormStepsContext } from '../context/FormStepsContext';\nimport { FormStepsContextType } from '../types';\n\n/**\n * Custom hook to access FormSteps context and internal form methods.\n * Must be used within a <Step> component to access form methods like register.\n */\nexport function useFormSteps(): FormStepsContextType & ReturnType<typeof useFormContext> {\n const context = useContext(FormStepsContext);\n const formMethods = useFormContext();\n\n if (!context) {\n throw new Error('useFormSteps must be used within a <FormSteps> provider');\n }\n\n return { ...context, ...formMethods };\n}\n"],"mappings":"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","names":["React","useState","useEffect","useContext","createContext","useState","useCallback","useMemo","useEffect","useRef","mergeStepData","allStepData","merged","a","b","index","getDiff","initial","current","diff","key","getChangedFieldsMap","changed","PREFIX","browserStoragePersistence","formKey","data","type","storage","e","ttl","raw","serializeFiles","data","base64","fileToBase64","files","result","key","deserializeFiles","dataUrlToFile","dt","f","file","resolve","reject","reader","error","dataUrl","filename","mimeType","arr","bstr","n","u8arr","debounce","jsx","FormStepsContext","createContext","FormStepsProvider","children","formKey","persistence","allowJump","unrestrictedNav","onStepEnter","onStepComplete","onClear","defaultValues","onSubmit","onDataChange","currentStep","setCurrentStep","useState","stepConfigs","setStepConfigs","allStepData","setAllStepData","completedSteps","setCompletedSteps","resumedDraftData","setResumedDraftData","isSubmitting","setIsSubmitting","isEditMode","registerStep","useCallback","index","label","schema","prev","unregisterStep","next","updateStepData","data","mergedData","useMemo","stepData","mergeStepData","result","key","val","useEffect","changedFields","getChangedFieldsMap","steps","a","b","idx","status","onAutoSaveRef","useRef","remoteSave","merged","e","triggerPersistence","stepIdx","completedOverride","stepsToSave","serializedData","serializeFiles","browserStoragePersistence","getAllErrors","errors","resumeDraft","draft","cleanData","deserializeFiles","newCompleted","i","clearDraft","goNext","isLastStep","nextCompleted","currentData","firstErrorStep","diff","goBack","goToStep","canJump","contextValue","RotateCcw","Play","X","jsx","jsxs","DraftBanner","title","description","resumeLabel","freshLabel","onResume","onFresh","onDismiss","className","e","Fragment","jsx","jsxs","InnerContent","children","showBanner","draft","setShowBanner","setDraft","bannerConfig","Autofilldata","renderDraftBanner","transition","context","useContext","FormStepsContext","contextResume","useEffect","resumeDraft","clearDraft","currentStep","handleResume","handleFresh","handleDismiss","DraftBanner","React","child","childProps","isActive","FormSteps","props","useState","isBrowserStorage","savedDraft","browserStoragePersistence","raw","e","stepCounter","childrenWithFixedIndex","FormStepsProvider","useEffect","useForm","FormProvider","zodResolver","useContext","useFormContext","useFormSteps","context","useContext","FormStepsContext","formMethods","useFormContext","jsx","Step","children","label","schema","index","mergedData","currentStep","resumedDraftData","registerStep","unregisterStep","updateStepData","goNext","useFormSteps","methods","useForm","zodResolver","handleSubmit","watch","reset","useEffect","subscription","value","FormProvider"]}
@@ -0,0 +1,129 @@
1
+ import React, { ReactNode } from 'react';
2
+ import { useFormContext } from 'react-hook-form';
3
+
4
+ type StepStatus = 'pending' | 'active' | 'complete' | 'error';
5
+ interface StepInfo {
6
+ index: number;
7
+ label: string;
8
+ status: StepStatus;
9
+ }
10
+ interface PersistenceOptions {
11
+ storageType: 'localStorage' | 'sessionStorage' | 'database' | 'none';
12
+ formKey?: string;
13
+ draftTTL?: number;
14
+ onAutoSave?: (stepIndex: number, stepData: any, mergedData: any) => Promise<void>;
15
+ onClearDraft?: () => Promise<void> | void;
16
+ asyncStorage?: {
17
+ getItem: (key: string) => any;
18
+ setItem: (key: string, value: string) => any;
19
+ removeItem: (key: string) => any;
20
+ };
21
+ }
22
+ interface FormStepsContextType {
23
+ values: any;
24
+ mergedData: any;
25
+ resumedDraftData: any;
26
+ currentStep: number;
27
+ steps: StepInfo[];
28
+ isEditMode: boolean;
29
+ changedFields: Record<string, boolean>;
30
+ isSubmitting: boolean;
31
+ goNext: () => Promise<void>;
32
+ goBack: () => void;
33
+ goToStep: (index: number) => void;
34
+ getAllErrors: () => Record<number, any>;
35
+ registerStep: (index: number, label: string, schema?: any) => void;
36
+ unregisterStep: (index: number) => void;
37
+ updateStepData: (index: number, data: any) => void;
38
+ resumeDraft: (draft: any) => void;
39
+ clearDraft: () => void;
40
+ }
41
+ interface FormStepsProps {
42
+ children: ReactNode;
43
+ formKey?: string;
44
+ storageStrategy?: 'localStorage' | 'sessionStorage' | 'database' | 'none';
45
+ asyncStorage?: {
46
+ getItem: (key: string) => any;
47
+ setItem: (key: string, value: string) => any;
48
+ removeItem: (key: string) => any;
49
+ };
50
+ onAutoSave?: (i: number, data: any, merged: any) => Promise<void>;
51
+ onDataChange?: (data: any) => void;
52
+ onClearDraft?: () => Promise<void> | void;
53
+ defaultValues?: any;
54
+ onSubmit: (payload: any, diff: any) => void | Promise<void>;
55
+ onDraftFound?: (draft: any) => void;
56
+ draftTTL?: number;
57
+ Autofilldata?: boolean;
58
+ allowJump?: boolean;
59
+ unrestrictedNav?: boolean;
60
+ transition?: 'slide' | 'fade' | 'none';
61
+ onStepEnter?: (index: number) => void;
62
+ onStepComplete?: (index: number) => void;
63
+ bannerConfig?: {
64
+ title?: string;
65
+ description?: string;
66
+ resumeLabel?: string;
67
+ freshLabel?: string;
68
+ className?: string;
69
+ style?: any;
70
+ };
71
+ renderDraftBanner?: (props: {
72
+ draft: DraftData;
73
+ resume: () => void;
74
+ startFresh: () => void;
75
+ dismiss: () => void;
76
+ }) => ReactNode;
77
+ }
78
+ interface StepProps {
79
+ children: ReactNode;
80
+ label: string;
81
+ schema?: any;
82
+ index?: number;
83
+ }
84
+ interface DraftData {
85
+ stepIndex: number;
86
+ mergedData: any;
87
+ completedSteps: Record<number, boolean>;
88
+ savedAt: number;
89
+ }
90
+
91
+ declare const FormSteps: React.FC<FormStepsProps>;
92
+
93
+ declare const Step: React.FC<StepProps>;
94
+
95
+ interface DraftBannerProps {
96
+ title?: string;
97
+ description?: string;
98
+ resumeLabel?: string;
99
+ freshLabel?: string;
100
+ onResume: () => void;
101
+ onFresh: () => void;
102
+ onDismiss: () => void;
103
+ style?: any;
104
+ }
105
+ declare const DraftBanner: React.FC<DraftBannerProps>;
106
+
107
+ /**
108
+ * Custom hook to access FormSteps context and internal form methods.
109
+ * Must be used within a <Step> component to access form methods like register.
110
+ */
111
+ declare function useFormSteps(): FormStepsContextType & ReturnType<typeof useFormContext>;
112
+
113
+ /**
114
+ * Simple diff utility to find changed fields between two objects.
115
+ * Returns an object containing only the fields that are different in 'current' compared to 'initial'.
116
+ */
117
+ declare function getDiff(initial: any, current: any): any;
118
+ /**
119
+ * Returns a map of changed field keys.
120
+ */
121
+ declare function getChangedFieldsMap(initial: any, current: any): Record<string, boolean>;
122
+
123
+ /**
124
+ * Merges data from all steps into a single object.
125
+ * Currently supports flat merging, but can be extended for nested structures if needed.
126
+ */
127
+ declare function mergeStepData(allStepData: Record<number, any>): any;
128
+
129
+ export { DraftBanner, type DraftData, FormSteps, type FormStepsContextType, type FormStepsProps, type PersistenceOptions, Step, type StepInfo, type StepProps, type StepStatus, getChangedFieldsMap, getDiff, mergeStepData, useFormSteps };
@@ -0,0 +1,2 @@
1
+ import H,{useState as Se,useEffect as oe,useContext as Ve,useRef as De}from"react";import{View as we,Animated as V}from"react-native";import{createContext as Te,useState as z,useCallback as h,useMemo as M,useEffect as Z,useRef as Re}from"react";function te(e){let r={};return Object.keys(e).map(Number).sort((o,c)=>o-c).forEach(o=>{r={...r,...e[o]}}),r}function xe(e,r){let t={};return e?(Object.keys(r).forEach(o=>{JSON.stringify(e[o])!==JSON.stringify(r[o])&&(t[o]=r[o])}),t):r}function q(e,r){let t={};return e?(Object.keys(r).forEach(o=>{JSON.stringify(e[o])!==JSON.stringify(r[o])&&(t[o]=!0)}),t):(Object.keys(r).forEach(o=>t[o]=!0),t)}var G="form-steps-draft",Q={save:(e,r,t)=>{try{if(typeof window>"u")return;let o=t==="localStorage"?window.localStorage:window.sessionStorage;if(!o)return;o.setItem(`${G}:${e}`,JSON.stringify(r))}catch(o){console.warn("Browser storage save failed:",o)}},load:(e,r,t)=>{try{if(typeof window>"u")return null;let o=r==="localStorage"?window.localStorage:window.sessionStorage;if(!o)return null;let c=o.getItem(`${G}:${e}`);if(!c)return null;let u=JSON.parse(c);if(t&&Date.now()-u.savedAt>t*1e3){try{o.removeItem(`${G}:${e}`)}catch{}return null}return u}catch(o){return console.warn("Browser storage load failed:",o),null}},clear:(e,r)=>{try{if(typeof window>"u")return;let t=r==="localStorage"?window.localStorage:window.sessionStorage;if(!t)return;t.removeItem(`${G}:${e}`)}catch(t){console.warn("Browser storage clear failed:",t)}}};var k=async e=>{if(!e)return e;if(typeof File<"u"&&e instanceof File){console.log("\u{1F4E6} Serializing file:",e.name);let r=await Ce(e);return{__is_file:!0,name:e.name,type:e.type,data:r}}if(typeof FileList<"u"&&e instanceof FileList){let r=Array.from(e);return{__is_file_list:!0,files:await Promise.all(r.map(k))}}if(Array.isArray(e))return Promise.all(e.map(k));if(typeof e=="object"&&e!==null){let r={};for(let t in e)Object.prototype.hasOwnProperty.call(e,t)&&(r[t]=await k(e[t]));return r}return e},J=e=>{if(!e)return e;if(e&&typeof e=="object"&&e.__is_file&&e.data)return console.log("\u{1F4C2} Deserializing file:",e.name),Fe(e.data,e.name,e.type);if(e&&typeof e=="object"&&e.__is_file_list&&Array.isArray(e.files)){let r=e.files.map(J);if(typeof DataTransfer<"u"){let t=new DataTransfer;return r.forEach(o=>t.items.add(o)),t.files}return r}if(Array.isArray(e))return e.map(J);if(typeof e=="object"&&e!==null){let r={};for(let t in e)Object.prototype.hasOwnProperty.call(e,t)&&(r[t]=J(e[t]));return r}return e},Ce=e=>new Promise((r,t)=>{let o=new FileReader;o.readAsDataURL(e),o.onload=()=>r(o.result),o.onerror=c=>t(c)}),Fe=(e,r,t)=>{if(typeof File>"u")return{uri:e,name:r,type:t};let o=e.split(","),c=typeof atob<"u"?atob(o[1]):"",u=c.length,g=new Uint8Array(u);for(;u--;)g[u]=c.charCodeAt(u);return new File([g],r,{type:t})};import Ae from"lodash.debounce";import{jsx as Pe}from"react/jsx-runtime";var W=Te(void 0),ge=({children:e,formKey:r,persistence:t,allowJump:o,unrestrictedNav:c,onStepEnter:u,onStepComplete:g,onClear:m,defaultValues:i,onSubmit:d,onDataChange:y})=>{let[l,w]=z(0),[b,R]=z({}),[A,N]=z({}),[p,x]=z({}),[I,P]=z(null),[L,_]=z(!1),T=!!i,ae=h((n,s,a)=>{R(f=>({...f,[n]:{label:s,schema:a}}))},[]),se=h(n=>{R(s=>{let a={...s};return delete a[n],a})},[]),ie=h((n,s)=>{N(a=>({...a,[n]:s}))},[]),D=M(()=>{let n=te(A),s={...I};return Object.keys(n).forEach(a=>{let f=n[a];f!=null&&(s[a]=f)}),T&&(s={...i,...s}),s},[A,i,T,I]);Z(()=>{y&&y(D)},[D,y]);let le=M(()=>T?q(i,D):{},[T,i,D]),$=M(()=>Object.keys(b).map(Number).sort((s,a)=>s-a).map(s=>{let a="pending";return s===l?a="active":p[s]&&(a="complete"),{index:s,label:b[s].label,status:a}}),[b,l,p]),ee=Re(t?.onAutoSave);Z(()=>{ee.current=t?.onAutoSave},[t?.onAutoSave]);let E=M(()=>t?.storageType==="database"?Ae(async(n,s,a)=>{try{ee.current&&await ee.current(n,s,a)}catch(f){console.error("Remote auto-save failed",f)}},800):null,[t?.storageType]);Z(()=>()=>{E&&E.cancel()},[E]);let ce=h(async(n,s,a,f)=>{if(!r)return;let B=f||p;if(t?.asyncStorage)try{let C=await k(a);await t.asyncStorage.setItem(`form-steps-draft:${r}`,JSON.stringify({stepIndex:n,mergedData:C,completedSteps:B,savedAt:Date.now()}))}catch(C){console.warn("AsyncStorage draft save failed:",C)}if(t?.storageType==="localStorage"||t?.storageType==="sessionStorage")try{let C=await k(a);Q.save(r,{stepIndex:n,mergedData:C,completedSteps:B,savedAt:Date.now()},t.storageType)}catch(C){console.warn("Browser storage draft save failed:",C)}E&&E(n,s,a)},[r,t,E,p]);Z(()=>{u&&u(l)},[l,u]);let X=h(()=>{let n={};return Object.keys(b).forEach(s=>{let a=Number(s),f=b[a].schema;if(f){let B=f.safeParse(D);B.success||(n[a]=B.error.format())}}),n},[b,D]),fe=h(n=>{if(n.mergedData){N({});let s=J(n.mergedData);if(P(s),w(n.stepIndex),n.completedSteps)x(n.completedSteps);else{let a={};for(let f=0;f<n.stepIndex;f++)a[f]=!0;x(a)}}},[]),Y=h(async()=>{if(console.log("\u{1F9F9} [clearDraft] Running... formKey:",r,"persistence.asyncStorage exists:",!!t?.asyncStorage),r)try{console.log("\u{1F9F9} [clearDraft] Clearing browser local/session storage..."),Q.clear(r,"localStorage"),Q.clear(r,"sessionStorage")}catch(n){console.warn("\u{1F9F9} [clearDraft] Browser storage clear failed:",n)}if(r&&t?.asyncStorage)try{console.log("\u{1F4F1} [clearDraft] Removing AsyncStorage key:",`form-steps-draft:${r}`),await t.asyncStorage.removeItem(`form-steps-draft:${r}`),console.log("\u{1F4F1} [clearDraft] AsyncStorage key removed successfully!")}catch(n){console.warn("\u{1F4F1} [clearDraft] AsyncStorage clear failed:",n)}else console.log("\u{1F4F1} [clearDraft] Skipping AsyncStorage clear. formKey:",r,"hasAsyncStorage:",!!t?.asyncStorage);t?.onClearDraft&&(console.log("\u2601\uFE0F Calling remote clearDraft callback..."),await t.onClearDraft()),N({}),P(null),x({}),w(0),y&&y({}),m&&(console.log("\u{1F9F9} [clearDraft] Calling onClear callback..."),m()),console.log("\u{1F9F9} [clearDraft] Finished clearDraft execution!")},[r,t,y,m]),ue=h(async()=>{let n=l===$.length-1;console.log("\u{1F449} [goNext] Triggered! currentStep:",l,"steps.length:",$.length,"isLastStep:",n);let s={...p,[l]:!0};if(x(s),g&&g(l),!n){let a=A[l]||{};console.log("\u{1F449} [goNext] Saving step draft for index:",l+1),await ce(l+1,a,D,s)}if(n){let a=X();if(Object.keys(a).length>0){console.warn("Cannot submit form: validation errors found in steps",a);let f=Object.keys(a).map(Number).sort((B,C)=>B-C)[0];w(f);return}console.log("\u{1F449} [goNext] isLastStep is TRUE! Submitting form. Calling clearDraft()..."),_(!0);try{let f=T?q(i,D):D;await Y(),console.log("\u{1F449} [goNext] clearDraft completed! Calling onSubmit callback..."),await d(D,f),console.log("\u{1F449} [goNext] onSubmit callback completed successfully!")}finally{_(!1)}}else console.log("\u{1F449} [goNext] Moving to next step:",l+1),w(a=>a+1)},[l,$.length,A,ce,D,T,i,d,r,t?.storageType,g,X,Y,p]),me=h(()=>{w(n=>Math.max(0,n-1))},[]),de=h(n=>{if(o)if(c||n<l)w(n);else{let s=!0;for(let a=0;a<n;a++)if(!p[a]&&a!==l){s=!1;break}s&&w(n)}},[o,c,l,p]),ve=M(()=>({values:D,mergedData:D,resumedDraftData:I,currentStep:l,steps:$,isEditMode:T,changedFields:le,isSubmitting:L,goNext:ue,goBack:me,goToStep:de,getAllErrors:X,registerStep:ae,unregisterStep:se,updateStepData:ie,resumeDraft:fe,clearDraft:Y}),[D,I,l,$,T,le,L,ue,me,de,X,ae,se,ie,fe,Y]);return Pe(W.Provider,{value:ve,children:e})};import{useEffect as Be,useRef as ye}from"react";import{View as K,Text as O,TouchableOpacity as re,Animated as F,StyleSheet as Oe}from"react-native";import{jsx as v,jsxs as U}from"react/jsx-runtime";var pe=({title:e="Draft Found",description:r="You have a saved draft from a previous session. Would you like to resume?",resumeLabel:t="Resume Draft",freshLabel:o="Start Fresh",onResume:c,onFresh:u,onDismiss:g,style:m})=>{let i=ye(new F.Value(-50)).current,d=ye(new F.Value(0)).current;Be(()=>{F.parallel([F.timing(i,{toValue:0,duration:350,useNativeDriver:!0}),F.timing(d,{toValue:1,duration:350,useNativeDriver:!0})]).start()},[i,d]);let y=()=>{F.parallel([F.timing(i,{toValue:-50,duration:250,useNativeDriver:!0}),F.timing(d,{toValue:0,duration:250,useNativeDriver:!0})]).start(()=>{g()})};return U(F.View,{style:[S.banner,{opacity:d,transform:[{translateY:i}]},m],children:[U(K,{style:S.contentContainer,children:[v(K,{style:S.iconContainer,children:v(O,{style:S.iconText,children:"\u{1F504}"})}),U(K,{style:S.textContainer,children:[v(O,{style:S.title,children:e}),v(O,{style:S.description,children:r})]}),v(re,{onPress:y,style:S.closeButton,children:v(O,{style:S.closeButtonText,children:"\u2715"})})]}),U(K,{style:S.actionsContainer,children:[v(re,{onPress:u,style:S.freshButton,children:v(O,{style:S.freshButtonText,children:o})}),U(re,{onPress:c,style:S.resumeButton,children:[v(O,{style:S.resumeButtonIcon,children:"\u25B6"}),v(O,{style:S.resumeButtonText,children:t})]})]})]})},S=Oe.create({banner:{backgroundColor:"#1e293b",borderColor:"#334155",borderWidth:1,borderRadius:12,padding:16,marginBottom:16,shadowColor:"#000",shadowOffset:{width:0,height:4},shadowOpacity:.1,shadowRadius:6,elevation:4},contentContainer:{flexDirection:"row",alignItems:"center",marginBottom:12},iconContainer:{backgroundColor:"#3b82f6",borderRadius:8,width:36,height:36,alignItems:"center",justifyContent:"center",marginRight:12},iconText:{fontSize:16},textContainer:{flex:1},title:{color:"#f8fafc",fontSize:14,fontWeight:"600",marginBottom:2},description:{color:"#94a3b8",fontSize:12},closeButton:{padding:4,marginLeft:8},closeButtonText:{color:"#475569",fontSize:16,fontWeight:"600"},actionsContainer:{flexDirection:"row",justifyContent:"flex-end",alignItems:"center"},freshButton:{paddingVertical:8,paddingHorizontal:12,marginRight:8},freshButtonText:{color:"#94a3b8",fontSize:12,fontWeight:"500"},resumeButton:{backgroundColor:"#3b82f6",borderRadius:6,paddingVertical:8,paddingHorizontal:16,flexDirection:"row",alignItems:"center"},resumeButtonIcon:{color:"#ffffff",fontSize:10,marginRight:6},resumeButtonText:{color:"#ffffff",fontSize:12,fontWeight:"600"}});import{Fragment as Ie,jsx as j,jsxs as Ee}from"react/jsx-runtime";var Ne=({children:e,showBanner:r,draft:t,setShowBanner:o,setDraft:c,bannerConfig:u,Autofilldata:g,renderDraftBanner:m,transition:i="none"})=>{let d=Ve(W),y=d?.resumeDraft;if(oe(()=>{g&&t&&y&&(y(t),o(!1))},[g,t,y,o]),!d)return j(Ie,{children:e});let{resumeDraft:l,clearDraft:w,currentStep:b}=d,R=()=>{t&&l&&(l(t),o(!1))},A=()=>{w(),o(!1),c(null)},N=()=>o(!1),p=De(new V.Value(1)).current,x=De(new V.Value(0)).current;oe(()=>{i!=="none"&&(i==="fade"?(p.setValue(0),V.timing(p,{toValue:1,duration:350,useNativeDriver:!0}).start()):i==="slide"&&(x.setValue(25),p.setValue(0),V.parallel([V.timing(x,{toValue:0,duration:350,useNativeDriver:!0}),V.timing(p,{toValue:1,duration:350,useNativeDriver:!0})]).start()))},[b,i,p,x]);let I=i==="none"?{}:{opacity:p,transform:i==="slide"?[{translateX:x}]:[]};return Ee(we,{style:{flex:1},children:[r&&t&&!g&&(m?m({draft:t,resume:R,startFresh:A,dismiss:N}):j(pe,{...u,onResume:R,onFresh:A,onDismiss:N})),j(V.View,{style:[{flex:1},I],children:H.Children.map(e,P=>{if(!H.isValidElement(P))return null;let L=P.props;if(L.hasOwnProperty("index")){let _=L.index===b;return j(we,{style:{display:_?"flex":"none",flex:_?1:0},children:P})}return P})})]})},ft=e=>{let[r,t]=Se(null),[o,c]=Se(!1);oe(()=>{(async()=>{if(e.formKey&&e.asyncStorage)try{let i=await e.asyncStorage.getItem(`form-steps-draft:${e.formKey}`);if(i){let d=JSON.parse(i);if(e.draftTTL&&Date.now()-d.savedAt>e.draftTTL*1e3){await e.asyncStorage.removeItem(`form-steps-draft:${e.formKey}`);return}t(d),c(!0),e.onDraftFound&&e.onDraftFound(d)}}catch(i){console.error("Failed to load async draft",i)}})()},[e.formKey,e.asyncStorage,e.draftTTL,e.onDraftFound]);let u=0,g=H.Children.map(e.children,m=>H.isValidElement(m)?m.props.label&&!m.props.hasOwnProperty("index")?H.cloneElement(m,{index:u++}):m:null);return j(ge,{formKey:e.formKey,persistence:{storageType:e.storageStrategy||"none",formKey:e.formKey,onAutoSave:e.onAutoSave,onClearDraft:e.onClearDraft,draftTTL:e.draftTTL,asyncStorage:e.asyncStorage},allowJump:e.allowJump,unrestrictedNav:e.unrestrictedNav,onStepEnter:e.onStepEnter,onStepComplete:e.onStepComplete,onDataChange:e.onDataChange,onClear:()=>{t(null),c(!1)},defaultValues:e.defaultValues,onSubmit:e.onSubmit,children:j(Ne,{showBanner:o,draft:r,setShowBanner:c,setDraft:t,bannerConfig:e.bannerConfig,Autofilldata:e.Autofilldata,renderDraftBanner:e.renderDraftBanner,transition:e.transition,children:g})})};import{useEffect as ne}from"react";import{View as je}from"react-native";import{useForm as Le,FormProvider as _e}from"react-hook-form";import{zodResolver as $e}from"@hookform/resolvers/zod";import{useContext as ke}from"react";import{useFormContext as ze}from"react-hook-form";function be(){let e=ke(W),r=ze();if(!e)throw new Error("useFormSteps must be used within a <FormSteps> provider");return{...e,...r}}import{jsx as he}from"react/jsx-runtime";var xt=({children:e,label:r,schema:t,index:o})=>{let{mergedData:c,currentStep:u,resumedDraftData:g,registerStep:m,unregisterStep:i,updateStepData:d}=be(),y=Le({resolver:t?$e(t):void 0,defaultValues:c,mode:"onTouched"}),{reset:l,watch:w}=y;return ne(()=>{c&&Object.keys(c).length>0?l(c,{keepDefaultValues:!0}):l({})},[u,l,g]),ne(()=>{if(typeof o=="number")return m(o,r,t),()=>i(o)},[o,r,t,m,i]),ne(()=>{let b=w(R=>{typeof o=="number"&&d(o,R)});return()=>b.unsubscribe()},[w,o,d]),he(_e,{...y,children:he(je,{style:{flex:1},children:e})})};export{pe as DraftBanner,ft as FormSteps,xt as Step,q as getChangedFieldsMap,xe as getDiff,te as mergeStepData,be as useFormSteps};
2
+ //# sourceMappingURL=index.native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/FormSteps.native.tsx","../src/context/FormStepsContext.tsx","../src/utils/merge.ts","../src/utils/diff.ts","../src/persistence/browserStorage.ts","../src/utils/fileStorage.ts","../src/components/DraftBanner.native.tsx","../src/components/Step.native.tsx","../src/hooks/useFormSteps.ts"],"sourcesContent":["import React, { useState, useEffect, useContext, useRef } from 'react';\nimport { View, Animated } from 'react-native';\nimport { FormStepsProvider, FormStepsContext } from '../context/FormStepsContext';\nimport { FormStepsProps, DraftData } from '../types';\nimport { DraftBanner } from './DraftBanner.native';\n\ninterface InnerContentProps {\n children: React.ReactNode;\n showBanner: boolean;\n draft: DraftData | null;\n setShowBanner: (val: boolean) => void;\n setDraft: (val: DraftData | null) => void;\n bannerConfig?: any;\n renderDraftBanner?: any;\n transition?: 'slide' | 'fade' | 'none';\n}\n\nconst InnerContent: React.FC<InnerContentProps & { Autofilldata?: boolean }> = ({\n children,\n showBanner,\n draft,\n setShowBanner,\n setDraft,\n bannerConfig,\n Autofilldata,\n renderDraftBanner,\n transition = 'none'\n}) => {\n const context = useContext(FormStepsContext);\n const contextResume = context?.resumeDraft;\n\n useEffect(() => {\n if (Autofilldata && draft && contextResume) {\n contextResume(draft);\n setShowBanner(false);\n }\n }, [Autofilldata, draft, contextResume, setShowBanner]);\n\n if (!context) return <>{children}</>;\n\n const { resumeDraft, clearDraft, currentStep } = context;\n\n const handleResume = () => {\n if (draft && resumeDraft) {\n resumeDraft(draft);\n setShowBanner(false);\n }\n };\n\n const handleFresh = () => {\n clearDraft();\n setShowBanner(false);\n setDraft(null);\n };\n\n const handleDismiss = () => setShowBanner(false);\n\n // Transitions using React Native Animated API\n const fadeAnim = useRef(new Animated.Value(1)).current;\n const slideAnim = useRef(new Animated.Value(0)).current;\n\n useEffect(() => {\n if (transition === 'none') return;\n\n if (transition === 'fade') {\n fadeAnim.setValue(0);\n Animated.timing(fadeAnim, {\n toValue: 1,\n duration: 350,\n useNativeDriver: true,\n }).start();\n } else if (transition === 'slide') {\n slideAnim.setValue(25);\n fadeAnim.setValue(0);\n Animated.parallel([\n Animated.timing(slideAnim, {\n toValue: 0,\n duration: 350,\n useNativeDriver: true,\n }),\n Animated.timing(fadeAnim, {\n toValue: 1,\n duration: 350,\n useNativeDriver: true,\n }),\n ]).start();\n }\n }, [currentStep, transition, fadeAnim, slideAnim]);\n\n const animatedStyle = transition === 'none' ? {} : {\n opacity: fadeAnim,\n transform: transition === 'slide' ? [{ translateX: slideAnim }] : [],\n };\n\n return (\n <View style={{ flex: 1 }}>\n {showBanner && draft && !Autofilldata && (\n renderDraftBanner ? (\n renderDraftBanner({ draft, resume: handleResume, startFresh: handleFresh, dismiss: handleDismiss })\n ) : (\n <DraftBanner\n {...bannerConfig}\n onResume={handleResume}\n onFresh={handleFresh}\n onDismiss={handleDismiss}\n />\n )\n )}\n <Animated.View style={[{ flex: 1 }, animatedStyle]}>\n {React.Children.map(children, child => {\n if (!React.isValidElement(child)) return null;\n\n const childProps = child.props as any;\n\n // If it's a Step (has an index), wrap it and handrle visibility\n if (childProps.hasOwnProperty('index')) {\n const isActive = childProps.index === currentStep;\n return (\n <View\n style={{ display: isActive ? 'flex' : 'none', flex: isActive ? 1 : 0 }}\n >\n {child}\n </View>\n );\n }\n\n // Persistent UI elements (like sidebar headers) stay visible\n return child;\n })}\n </Animated.View>\n </View>\n );\n};\n\nexport const FormSteps: React.FC<FormStepsProps> = (props) => {\n const [draft, setDraft] = useState<DraftData | null>(null);\n const [showBanner, setShowBanner] = useState(false);\n\n // Load from custom asyncStorage asynchronously\n useEffect(() => {\n const loadAsyncDraft = async () => {\n if (props.formKey && props.asyncStorage) {\n try {\n const raw = await props.asyncStorage.getItem(`form-steps-draft:${props.formKey}`);\n if (raw) {\n const savedDraft: DraftData = JSON.parse(raw);\n if (props.draftTTL) {\n const now = Date.now();\n const expired = now - savedDraft.savedAt > props.draftTTL * 1000;\n if (expired) {\n await props.asyncStorage.removeItem(`form-steps-draft:${props.formKey}`);\n return;\n }\n }\n setDraft(savedDraft);\n setShowBanner(true);\n if (props.onDraftFound) props.onDraftFound(savedDraft);\n }\n } catch (e) {\n console.error('Failed to load async draft', e);\n }\n }\n };\n loadAsyncDraft();\n }, [props.formKey, props.asyncStorage, props.draftTTL, props.onDraftFound]);\n\n // Calculate active index manually\n let stepCounter = 0;\n const childrenWithFixedIndex = React.Children.map(props.children, (child) => {\n if (React.isValidElement(child)) {\n // Check if it's a Step component (has label prop)\n if (child.props.label && !child.props.hasOwnProperty('index')) {\n return React.cloneElement(child as React.ReactElement<any>, { index: stepCounter++ });\n }\n return child;\n }\n return null;\n });\n\n return (\n <FormStepsProvider\n formKey={props.formKey}\n persistence={{\n storageType: props.storageStrategy || 'none',\n formKey: props.formKey,\n onAutoSave: props.onAutoSave,\n onClearDraft: props.onClearDraft,\n draftTTL: props.draftTTL,\n asyncStorage: props.asyncStorage\n }}\n allowJump={props.allowJump}\n unrestrictedNav={props.unrestrictedNav}\n onStepEnter={props.onStepEnter}\n onStepComplete={props.onStepComplete}\n onDataChange={props.onDataChange}\n onClear={() => {\n setDraft(null);\n setShowBanner(false);\n }}\n defaultValues={props.defaultValues}\n onSubmit={props.onSubmit}\n >\n <InnerContent\n showBanner={showBanner}\n draft={draft}\n setShowBanner={setShowBanner}\n setDraft={setDraft}\n bannerConfig={props.bannerConfig}\n Autofilldata={props.Autofilldata}\n renderDraftBanner={props.renderDraftBanner}\n transition={props.transition}\n >\n {childrenWithFixedIndex}\n </InnerContent>\n </FormStepsProvider>\n );\n};\n","import React, { createContext, useState, useCallback, useMemo, useEffect, useRef } from 'react';\nimport { FormStepsContextType, StepInfo, StepStatus, PersistenceOptions, DraftData } from '../types';\nimport { mergeStepData } from '../utils/merge';\nimport { getChangedFieldsMap } from '../utils/diff';\nimport { browserStoragePersistence } from '../persistence/browserStorage';\nimport { serializeFiles, deserializeFiles } from '../utils/fileStorage';\nimport debounce from 'lodash.debounce';\n\nexport const FormStepsContext = createContext<FormStepsContextType | undefined>(undefined);\n\ninterface ProviderProps {\n children: React.ReactNode;\n formKey?: string;\n persistence?: PersistenceOptions;\n allowJump?: boolean;\n unrestrictedNav?: boolean;\n onStepEnter?: (idx: number) => void;\n onStepComplete?: (idx: number) => void;\n onClear?: () => void;\n defaultValues?: any;\n onSubmit: (payload: any, diff: any) => void | Promise<void>;\n onDataChange?: (data: any) => void;\n onDraftFound?: (draft: any) => void;\n}\n\nexport const FormStepsProvider: React.FC<ProviderProps> = ({\n children,\n formKey,\n persistence,\n allowJump,\n unrestrictedNav,\n onStepEnter,\n onStepComplete,\n onClear,\n defaultValues,\n onSubmit,\n onDataChange,\n}) => {\n const [currentStep, setCurrentStep] = useState(0);\n const [stepConfigs, setStepConfigs] = useState<Record<number, { label: string; schema?: any }>>({});\n const [allStepData, setAllStepData] = useState<Record<number, any>>({});\n const [completedSteps, setCompletedSteps] = useState<Record<number, boolean>>({});\n const [resumedDraftData, setResumedDraftData] = useState<any>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n\n const isEditMode = !!defaultValues;\n\n // Step registration\n const registerStep = useCallback((index: number, label: string, schema?: any) => {\n setStepConfigs((prev) => ({ ...prev, [index]: { label, schema } }));\n }, []);\n\n const unregisterStep = useCallback((index: number) => {\n setStepConfigs((prev) => {\n const next = { ...prev };\n delete next[index];\n return next;\n });\n }, []);\n\n // Data management\n const updateStepData = useCallback((index: number, data: any) => {\n setAllStepData((prev) => ({ ...prev, [index]: data }));\n }, []);\n\n const mergedData = useMemo(() => {\n const stepData = mergeStepData(allStepData);\n let result = { ...resumedDraftData };\n\n Object.keys(stepData).forEach(key => {\n const val = stepData[key];\n if (val !== undefined && val !== null) {\n result[key] = val;\n }\n });\n\n if (isEditMode) {\n result = { ...defaultValues, ...result };\n }\n return result;\n }, [allStepData, defaultValues, isEditMode, resumedDraftData]);\n\n // Trigger onDataChange\n useEffect(() => {\n if (onDataChange) {\n onDataChange(mergedData);\n }\n }, [mergedData, onDataChange]);\n\n const changedFields = useMemo(() => {\n if (!isEditMode) return {};\n return getChangedFieldsMap(defaultValues, mergedData);\n }, [isEditMode, defaultValues, mergedData]);\n\n // Status tracking\n const steps: StepInfo[] = useMemo(() => {\n const indices = Object.keys(stepConfigs).map(Number).sort((a, b) => a - b);\n return indices.map((idx) => {\n let status: StepStatus = 'pending';\n if (idx === currentStep) status = 'active';\n else if (completedSteps[idx]) status = 'complete';\n return {\n index: idx,\n label: stepConfigs[idx].label,\n status,\n };\n });\n }, [stepConfigs, currentStep, completedSteps]);\n\n // Persistence\n const onAutoSaveRef = useRef(persistence?.onAutoSave);\n useEffect(() => {\n onAutoSaveRef.current = persistence?.onAutoSave;\n }, [persistence?.onAutoSave]);\n\n const remoteSave = useMemo(() => {\n if (persistence?.storageType === 'database') {\n return debounce(async (index: number, data: any, merged: any) => {\n try {\n if (onAutoSaveRef.current) {\n await onAutoSaveRef.current(index, data, merged);\n }\n } catch (e) {\n console.error('Remote auto-save failed', e);\n }\n }, 800);\n }\n return null;\n }, [persistence?.storageType]);\n\n // Clean up debounce on unmount\n useEffect(() => {\n return () => {\n if (remoteSave) {\n remoteSave.cancel();\n }\n };\n }, [remoteSave]);\n\n const triggerPersistence = useCallback(async (stepIdx: number, data: any, merged: any, completedOverride?: Record<number, boolean>) => {\n if (!formKey) return;\n\n const stepsToSave = completedOverride || completedSteps;\n\n // 1. AsyncStorage Persistence\n if (persistence?.asyncStorage) {\n try {\n const serializedData = await serializeFiles(merged);\n await persistence.asyncStorage.setItem(`form-steps-draft:${formKey}`, JSON.stringify({\n stepIndex: stepIdx,\n mergedData: serializedData,\n completedSteps: stepsToSave,\n savedAt: Date.now(),\n }));\n } catch (e) {\n console.warn('AsyncStorage draft save failed:', e);\n }\n }\n\n // 2. Browser Storage Persistence\n if (persistence?.storageType === 'localStorage' || persistence?.storageType === 'sessionStorage') {\n try {\n const serializedData = await serializeFiles(merged);\n browserStoragePersistence.save(formKey, {\n stepIndex: stepIdx,\n mergedData: serializedData,\n completedSteps: stepsToSave,\n savedAt: Date.now(),\n }, persistence.storageType);\n } catch (e) {\n console.warn('Browser storage draft save failed:', e);\n }\n }\n\n if (remoteSave) {\n remoteSave(stepIdx, data, merged);\n }\n }, [formKey, persistence, remoteSave, completedSteps]);\n\n // Analytics Callbacks\n useEffect(() => {\n if (onStepEnter) onStepEnter(currentStep);\n }, [currentStep, onStepEnter]);\n\n // Error Summary\n const getAllErrors = useCallback(() => {\n const errors: Record<number, any> = {};\n Object.keys(stepConfigs).forEach(idx => {\n const index = Number(idx);\n const schema = stepConfigs[index].schema;\n if (schema) {\n const result = schema.safeParse(mergedData);\n if (!result.success) {\n errors[index] = result.error.format();\n }\n }\n });\n return errors;\n }, [stepConfigs, mergedData]);\n\n const resumeDraft = useCallback((draft: DraftData) => {\n if (draft.mergedData) {\n setAllStepData({});\n const cleanData = deserializeFiles(draft.mergedData);\n setResumedDraftData(cleanData);\n setCurrentStep(draft.stepIndex);\n\n if (draft.completedSteps) {\n setCompletedSteps(draft.completedSteps);\n } else {\n const newCompleted: Record<number, boolean> = {};\n for (let i = 0; i < draft.stepIndex; i++) {\n newCompleted[i] = true;\n }\n setCompletedSteps(newCompleted);\n }\n }\n }, []);\n\n const clearDraft = useCallback(async () => {\n console.log('๐Ÿงน [clearDraft] Running... formKey:', formKey, 'persistence.asyncStorage exists:', !!persistence?.asyncStorage);\n\n // 1. Clear Browser Storage\n if (formKey) {\n try {\n console.log('๐Ÿงน [clearDraft] Clearing browser local/session storage...');\n browserStoragePersistence.clear(formKey, 'localStorage');\n browserStoragePersistence.clear(formKey, 'sessionStorage');\n } catch (e) {\n console.warn('๐Ÿงน [clearDraft] Browser storage clear failed:', e);\n }\n }\n\n // 1.5 Clear AsyncStorage\n if (formKey && persistence?.asyncStorage) {\n try {\n console.log('๐Ÿ“ฑ [clearDraft] Removing AsyncStorage key:', `form-steps-draft:${formKey}`);\n await persistence.asyncStorage.removeItem(`form-steps-draft:${formKey}`);\n console.log('๐Ÿ“ฑ [clearDraft] AsyncStorage key removed successfully!');\n } catch (e) {\n console.warn('๐Ÿ“ฑ [clearDraft] AsyncStorage clear failed:', e);\n }\n } else {\n console.log('๐Ÿ“ฑ [clearDraft] Skipping AsyncStorage clear. formKey:', formKey, 'hasAsyncStorage:', !!persistence?.asyncStorage);\n }\n\n // 2. Clear Database/Remote Storage\n if (persistence?.onClearDraft) {\n console.log('โ˜๏ธ Calling remote clearDraft callback...');\n await persistence.onClearDraft();\n }\n\n // 3. Clear Internal State\n setAllStepData({});\n setResumedDraftData(null);\n setCompletedSteps({});\n setCurrentStep(0);\n\n // 4. Sync with external state (Redux)\n if (onDataChange) {\n onDataChange({});\n }\n\n // 5. Notify Parent UI\n if (onClear) {\n console.log('๐Ÿงน [clearDraft] Calling onClear callback...');\n onClear();\n }\n console.log('๐Ÿงน [clearDraft] Finished clearDraft execution!');\n }, [formKey, persistence, onDataChange, onClear]);\n\n // Navigation\n const goNext = useCallback(async () => {\n const isLastStep = currentStep === steps.length - 1;\n console.log('๐Ÿ‘‰ [goNext] Triggered! currentStep:', currentStep, 'steps.length:', steps.length, 'isLastStep:', isLastStep);\n\n // Mark current as complete\n const nextCompleted = { ...completedSteps, [currentStep]: true };\n setCompletedSteps(nextCompleted);\n if (onStepComplete) onStepComplete(currentStep);\n\n // Auto-save logic: Don't save draft if it's the last step (we're about to clear it)\n if (!isLastStep) {\n const currentData = allStepData[currentStep] || {};\n console.log('๐Ÿ‘‰ [goNext] Saving step draft for index:', currentStep + 1);\n // We pass nextCompleted here to ensure the save includes the step we just finished!\n await triggerPersistence(currentStep + 1, currentData, mergedData, nextCompleted);\n }\n\n if (isLastStep) {\n const errors = getAllErrors();\n if (Object.keys(errors).length > 0) {\n console.warn('Cannot submit form: validation errors found in steps', errors);\n const firstErrorStep = Object.keys(errors).map(Number).sort((a, b) => a - b)[0];\n setCurrentStep(firstErrorStep);\n return;\n }\n\n console.log('๐Ÿ‘‰ [goNext] isLastStep is TRUE! Submitting form. Calling clearDraft()...');\n setIsSubmitting(true);\n try {\n const diff = isEditMode ? getChangedFieldsMap(defaultValues, mergedData) : mergedData;\n // Immediately clear the draft before submitting to avoid unmount race conditions\n await clearDraft();\n console.log('๐Ÿ‘‰ [goNext] clearDraft completed! Calling onSubmit callback...');\n await onSubmit(mergedData, diff);\n console.log('๐Ÿ‘‰ [goNext] onSubmit callback completed successfully!');\n } finally {\n setIsSubmitting(false);\n }\n } else {\n console.log('๐Ÿ‘‰ [goNext] Moving to next step:', currentStep + 1);\n setCurrentStep((prev) => prev + 1);\n }\n }, [currentStep, steps.length, allStepData, triggerPersistence, mergedData, isEditMode, defaultValues, onSubmit, formKey, persistence?.storageType, onStepComplete, getAllErrors, clearDraft, completedSteps]);\n\n const goBack = useCallback(() => {\n setCurrentStep((prev) => Math.max(0, prev - 1));\n }, []);\n\n const goToStep = useCallback((index: number) => {\n if (!allowJump) return;\n\n if (unrestrictedNav || index < currentStep) {\n setCurrentStep(index);\n } else {\n let canJump = true;\n for (let i = 0; i < index; i++) {\n if (!completedSteps[i] && i !== currentStep) {\n canJump = false;\n break;\n }\n }\n if (canJump) {\n setCurrentStep(index);\n }\n }\n }, [allowJump, unrestrictedNav, currentStep, completedSteps]);\n\n\n const contextValue: FormStepsContextType = useMemo(() => ({\n values: mergedData,\n mergedData,\n resumedDraftData,\n currentStep,\n steps,\n isEditMode,\n changedFields,\n isSubmitting,\n goNext,\n goBack,\n goToStep,\n getAllErrors,\n registerStep,\n unregisterStep,\n updateStepData,\n resumeDraft,\n clearDraft,\n }), [\n mergedData,\n resumedDraftData,\n currentStep,\n steps,\n isEditMode,\n changedFields,\n isSubmitting,\n goNext,\n goBack,\n goToStep,\n getAllErrors,\n registerStep,\n unregisterStep,\n updateStepData,\n resumeDraft,\n clearDraft\n ]);\n\n return (\n <FormStepsContext.Provider value={contextValue}>\n {children}\n </FormStepsContext.Provider>\n );\n};\n","/**\n * Merges data from all steps into a single object.\n * Currently supports flat merging, but can be extended for nested structures if needed.\n */\nexport function mergeStepData(allStepData: Record<number, any>): any {\n let merged = {};\n \n // Sort keys to ensure consistent merging order\n const sortedIndices = Object.keys(allStepData)\n .map(Number)\n .sort((a, b) => a - b);\n\n sortedIndices.forEach((index) => {\n merged = { ...merged, ...allStepData[index] };\n });\n\n return merged;\n}\n","/**\n * Simple diff utility to find changed fields between two objects.\n * Returns an object containing only the fields that are different in 'current' compared to 'initial'.\n */\nexport function getDiff(initial: any, current: any): any {\n const diff: any = {};\n\n if (!initial) return current;\n\n Object.keys(current).forEach((key) => {\n if (JSON.stringify(initial[key]) !== JSON.stringify(current[key])) {\n diff[key] = current[key];\n }\n });\n\n return diff;\n}\n\n/**\n * Returns a map of changed field keys.\n */\nexport function getChangedFieldsMap(initial: any, current: any): Record<string, boolean> {\n const changed: Record<string, boolean> = {};\n \n if (!initial) {\n Object.keys(current).forEach(key => changed[key] = true);\n return changed;\n }\n\n Object.keys(current).forEach((key) => {\n if (JSON.stringify(initial[key]) !== JSON.stringify(current[key])) {\n changed[key] = true;\n }\n });\n\n return changed;\n}\n","import { DraftData } from '../types';\n\nconst PREFIX = 'form-steps-draft';\n\nexport const browserStoragePersistence = {\n save: (formKey: string, data: DraftData, type: 'localStorage' | 'sessionStorage') => {\n try {\n if (typeof window === 'undefined') return;\n const storage = type === 'localStorage' ? window.localStorage : window.sessionStorage;\n if (!storage) return;\n storage.setItem(`${PREFIX}:${formKey}`, JSON.stringify(data));\n } catch (e) {\n console.warn('Browser storage save failed:', e);\n }\n },\n\n load: (formKey: string, type: 'localStorage' | 'sessionStorage', ttl?: number): DraftData | null => {\n try {\n if (typeof window === 'undefined') return null;\n const storage = type === 'localStorage' ? window.localStorage : window.sessionStorage;\n if (!storage) return null;\n const raw = storage.getItem(`${PREFIX}:${formKey}`);\n if (!raw) return null;\n\n const data: DraftData = JSON.parse(raw);\n \n // Check TTL\n if (ttl) {\n const now = Date.now();\n const expired = now - data.savedAt > ttl * 1000;\n if (expired) {\n try {\n storage.removeItem(`${PREFIX}:${formKey}`);\n } catch (rmError) {\n // Ignore remove failure\n }\n return null;\n }\n }\n\n return data;\n } catch (e) {\n console.warn('Browser storage load failed:', e);\n return null;\n }\n },\n\n clear: (formKey: string, type: 'localStorage' | 'sessionStorage') => {\n try {\n if (typeof window === 'undefined') return;\n const storage = type === 'localStorage' ? window.localStorage : window.sessionStorage;\n if (!storage) return;\n storage.removeItem(`${PREFIX}:${formKey}`);\n } catch (e) {\n console.warn('Browser storage clear failed:', e);\n }\n }\n};\n","/**\n * Utility to handle File objects in JSON storage (LocalStorage/SessionStorage)\n * by converting them to Base64 strings and back.\n * \n * โš ๏ธ WARNING FOR REACT NATIVE / MOBILE DEVELOPERS:\n * Serializing files to Base64 and storing them in AsyncStorage is highly discouraged\n * due to AsyncStorage storage limits (typically 6MB on Android). In React Native,\n * file picker outputs should keep their reference 'uri' paths as plain strings rather\n * than converting full binary payloads to Base64.\n */\n\nexport const serializeFiles = async (data: any): Promise<any> => {\n if (!data) return data;\n\n // Handle File objects\n if (typeof File !== 'undefined' && data instanceof File) {\n console.log('๐Ÿ“ฆ Serializing file:', data.name);\n const base64 = await fileToBase64(data);\n return {\n __is_file: true,\n name: data.name,\n type: data.type,\n data: base64,\n };\n }\n\n // Handle FileList (the common format for file inputs)\n if (typeof FileList !== 'undefined' && data instanceof FileList) {\n const files = Array.from(data);\n return {\n __is_file_list: true,\n files: await Promise.all(files.map(serializeFiles)),\n };\n }\n\n // Handle Arrays\n if (Array.isArray(data)) {\n return Promise.all(data.map(serializeFiles));\n }\n\n // Handle Objects (Recursion)\n if (typeof data === 'object' && data !== null) {\n const result: any = {};\n for (const key in data) {\n if (Object.prototype.hasOwnProperty.call(data, key)) {\n result[key] = await serializeFiles(data[key]);\n }\n }\n return result;\n }\n\n return data;\n};\n\nexport const deserializeFiles = (data: any): any => {\n if (!data) return data;\n\n // Restore File from our custom format\n if (data && typeof data === 'object' && data.__is_file && data.data) {\n console.log('๐Ÿ“‚ Deserializing file:', data.name);\n return dataUrlToFile(data.data, data.name, data.type);\n }\n\n // Restore FileList from our custom format\n if (data && typeof data === 'object' && data.__is_file_list && Array.isArray(data.files)) {\n const files = data.files.map(deserializeFiles);\n if (typeof DataTransfer !== 'undefined') {\n const dt = new DataTransfer();\n files.forEach((f: any) => dt.items.add(f));\n return dt.files;\n }\n return files; // Fallback to array if DataTransfer is not available (e.g. React Native)\n }\n\n // Handle Arrays\n if (Array.isArray(data)) {\n return data.map(deserializeFiles);\n }\n\n // Handle Objects\n if (typeof data === 'object' && data !== null) {\n const result: any = {};\n for (const key in data) {\n if (Object.prototype.hasOwnProperty.call(data, key)) {\n result[key] = deserializeFiles(data[key]);\n }\n }\n return result;\n }\n\n return data;\n};\n\nconst fileToBase64 = (file: File): Promise<string> => {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.readAsDataURL(file);\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = (error) => reject(error);\n });\n};\n\nconst dataUrlToFile = (dataUrl: string, filename: string, mimeType: string): any => {\n if (typeof File === 'undefined') {\n return { uri: dataUrl, name: filename, type: mimeType };\n }\n const arr = dataUrl.split(',');\n const bstr = typeof atob !== 'undefined' ? atob(arr[1]) : '';\n let n = bstr.length;\n const u8arr = new Uint8Array(n);\n while (n--) {\n u8arr[n] = bstr.charCodeAt(n);\n }\n return new File([u8arr], filename, { type: mimeType });\n};\n","import React, { useEffect, useRef } from 'react';\nimport { View, Text, TouchableOpacity, Animated, StyleSheet } from 'react-native';\n\ninterface DraftBannerProps {\n title?: string;\n description?: string;\n resumeLabel?: string;\n freshLabel?: string;\n onResume: () => void;\n onFresh: () => void;\n onDismiss: () => void;\n style?: any;\n}\n\nexport const DraftBanner: React.FC<DraftBannerProps> = ({\n title = 'Draft Found',\n description = 'You have a saved draft from a previous session. Would you like to resume?',\n resumeLabel = 'Resume Draft',\n freshLabel = 'Start Fresh',\n onResume,\n onFresh,\n onDismiss,\n style,\n}) => {\n const slideAnim = useRef(new Animated.Value(-50)).current;\n const opacityAnim = useRef(new Animated.Value(0)).current;\n\n useEffect(() => {\n Animated.parallel([\n Animated.timing(slideAnim, {\n toValue: 0,\n duration: 350,\n useNativeDriver: true,\n }),\n Animated.timing(opacityAnim, {\n toValue: 1,\n duration: 350,\n useNativeDriver: true,\n }),\n ]).start();\n }, [slideAnim, opacityAnim]);\n\n const handleDismiss = () => {\n Animated.parallel([\n Animated.timing(slideAnim, {\n toValue: -50,\n duration: 250,\n useNativeDriver: true,\n }),\n Animated.timing(opacityAnim, {\n toValue: 0,\n duration: 250,\n useNativeDriver: true,\n }),\n ]).start(() => {\n onDismiss();\n });\n };\n\n return (\n <Animated.View\n style={[\n styles.banner,\n {\n opacity: opacityAnim,\n transform: [{ translateY: slideAnim }],\n },\n style,\n ]}\n >\n <View style={styles.contentContainer}>\n <View style={styles.iconContainer}>\n <Text style={styles.iconText}>๐Ÿ”„</Text>\n </View>\n <View style={styles.textContainer}>\n <Text style={styles.title}>{title}</Text>\n <Text style={styles.description}>{description}</Text>\n </View>\n <TouchableOpacity onPress={handleDismiss} style={styles.closeButton}>\n <Text style={styles.closeButtonText}>โœ•</Text>\n </TouchableOpacity>\n </View>\n\n <View style={styles.actionsContainer}>\n <TouchableOpacity onPress={onFresh} style={styles.freshButton}>\n <Text style={styles.freshButtonText}>{freshLabel}</Text>\n </TouchableOpacity>\n <TouchableOpacity onPress={onResume} style={styles.resumeButton}>\n <Text style={styles.resumeButtonIcon}>โ–ถ</Text>\n <Text style={styles.resumeButtonText}>{resumeLabel}</Text>\n </TouchableOpacity>\n </View>\n </Animated.View>\n );\n};\n\nconst styles = StyleSheet.create({\n banner: {\n backgroundColor: '#1e293b',\n borderColor: '#334155',\n borderWidth: 1,\n borderRadius: 12,\n padding: 16,\n marginBottom: 16,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 4 },\n shadowOpacity: 0.1,\n shadowRadius: 6,\n elevation: 4,\n },\n contentContainer: {\n flexDirection: 'row',\n alignItems: 'center',\n marginBottom: 12,\n },\n iconContainer: {\n backgroundColor: '#3b82f6',\n borderRadius: 8,\n width: 36,\n height: 36,\n alignItems: 'center',\n justifyContent: 'center',\n marginRight: 12,\n },\n iconText: {\n fontSize: 16,\n },\n textContainer: {\n flex: 1,\n },\n title: {\n color: '#f8fafc',\n fontSize: 14,\n fontWeight: '600',\n marginBottom: 2,\n },\n description: {\n color: '#94a3b8',\n fontSize: 12,\n },\n closeButton: {\n padding: 4,\n marginLeft: 8,\n },\n closeButtonText: {\n color: '#475569',\n fontSize: 16,\n fontWeight: '600',\n },\n actionsContainer: {\n flexDirection: 'row',\n justifyContent: 'flex-end',\n alignItems: 'center',\n },\n freshButton: {\n paddingVertical: 8,\n paddingHorizontal: 12,\n marginRight: 8,\n },\n freshButtonText: {\n color: '#94a3b8',\n fontSize: 12,\n fontWeight: '500',\n },\n resumeButton: {\n backgroundColor: '#3b82f6',\n borderRadius: 6,\n paddingVertical: 8,\n paddingHorizontal: 16,\n flexDirection: 'row',\n alignItems: 'center',\n },\n resumeButtonIcon: {\n color: '#ffffff',\n fontSize: 10,\n marginRight: 6,\n },\n resumeButtonText: {\n color: '#ffffff',\n fontSize: 12,\n fontWeight: '600',\n },\n});\n","import React, { useEffect } from 'react';\nimport { View } from 'react-native';\nimport { useForm, FormProvider } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { StepProps } from '../types';\nimport { useFormSteps } from '../hooks/useFormSteps';\n\nexport const Step: React.FC<StepProps> = ({ children, label, schema, index }) => {\n const { \n mergedData, \n currentStep, \n resumedDraftData, \n registerStep, \n unregisterStep, \n updateStepData \n } = useFormSteps();\n\n const methods = useForm({\n resolver: schema ? zodResolver(schema) : undefined,\n defaultValues: mergedData,\n mode: 'onTouched',\n });\n\n const { reset, watch } = methods;\n\n // Sync form with mergedData when the step becomes active OR when a draft is resumed\n useEffect(() => {\n if (mergedData && Object.keys(mergedData).length > 0) {\n reset(mergedData, { keepDefaultValues: true });\n } else {\n reset({});\n }\n }, [currentStep, reset, resumedDraftData]);\n\n // Register step with context\n useEffect(() => {\n if (typeof index === 'number') {\n registerStep(index, label, schema);\n return () => unregisterStep(index);\n }\n }, [index, label, schema, registerStep, unregisterStep]);\n\n // Update context data when fields change\n useEffect(() => {\n const subscription = watch((value) => {\n if (typeof index === 'number') {\n updateStepData(index, value);\n }\n });\n return () => subscription.unsubscribe();\n }, [watch, index, updateStepData]);\n\n return (\n <FormProvider {...methods}>\n <View style={{ flex: 1 }}>\n {children}\n </View>\n </FormProvider>\n );\n};\n","import { useContext } from 'react';\nimport { useFormContext } from 'react-hook-form';\nimport { FormStepsContext } from '../context/FormStepsContext';\nimport { FormStepsContextType } from '../types';\n\n/**\n * Custom hook to access FormSteps context and internal form methods.\n * Must be used within a <Step> component to access form methods like register.\n */\nexport function useFormSteps(): FormStepsContextType & ReturnType<typeof useFormContext> {\n const context = useContext(FormStepsContext);\n const formMethods = useFormContext();\n\n if (!context) {\n throw new Error('useFormSteps must be used within a <FormSteps> provider');\n }\n\n return { ...context, ...formMethods };\n}\n"],"mappings":"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","names":["React","useState","useEffect","useContext","useRef","View","Animated","createContext","useState","useCallback","useMemo","useEffect","useRef","mergeStepData","allStepData","merged","a","b","index","getDiff","initial","current","diff","key","getChangedFieldsMap","changed","PREFIX","browserStoragePersistence","formKey","data","type","storage","e","ttl","raw","serializeFiles","data","base64","fileToBase64","files","result","key","deserializeFiles","dataUrlToFile","dt","f","file","resolve","reject","reader","error","dataUrl","filename","mimeType","arr","bstr","n","u8arr","debounce","jsx","FormStepsContext","createContext","FormStepsProvider","children","formKey","persistence","allowJump","unrestrictedNav","onStepEnter","onStepComplete","onClear","defaultValues","onSubmit","onDataChange","currentStep","setCurrentStep","useState","stepConfigs","setStepConfigs","allStepData","setAllStepData","completedSteps","setCompletedSteps","resumedDraftData","setResumedDraftData","isSubmitting","setIsSubmitting","isEditMode","registerStep","useCallback","index","label","schema","prev","unregisterStep","next","updateStepData","data","mergedData","useMemo","stepData","mergeStepData","result","key","val","useEffect","changedFields","getChangedFieldsMap","steps","a","b","idx","status","onAutoSaveRef","useRef","remoteSave","merged","e","triggerPersistence","stepIdx","completedOverride","stepsToSave","serializedData","serializeFiles","browserStoragePersistence","getAllErrors","errors","resumeDraft","draft","cleanData","deserializeFiles","newCompleted","i","clearDraft","goNext","isLastStep","nextCompleted","currentData","firstErrorStep","diff","goBack","goToStep","canJump","contextValue","useEffect","useRef","View","Text","TouchableOpacity","Animated","StyleSheet","jsx","jsxs","DraftBanner","title","description","resumeLabel","freshLabel","onResume","onFresh","onDismiss","style","slideAnim","opacityAnim","handleDismiss","styles","Fragment","jsx","jsxs","InnerContent","children","showBanner","draft","setShowBanner","setDraft","bannerConfig","Autofilldata","renderDraftBanner","transition","context","useContext","FormStepsContext","contextResume","useEffect","resumeDraft","clearDraft","currentStep","handleResume","handleFresh","handleDismiss","fadeAnim","useRef","Animated","slideAnim","animatedStyle","View","DraftBanner","React","child","childProps","isActive","FormSteps","props","useState","raw","savedDraft","e","stepCounter","childrenWithFixedIndex","FormStepsProvider","useEffect","View","useForm","FormProvider","zodResolver","useContext","useFormContext","useFormSteps","context","useContext","FormStepsContext","formMethods","useFormContext","jsx","Step","children","label","schema","index","mergedData","currentStep","resumedDraftData","registerStep","unregisterStep","updateStepData","useFormSteps","methods","useForm","zodResolver","reset","watch","useEffect","subscription","value","FormProvider","View"]}
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "react-form-steps",
3
+ "version": "1.0.0",
4
+ "description": "A production-ready multi-step form manager for React with persistence and edit mode support.",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "react-native": "./dist/index.native.js",
8
+ "types": "./dist/index.d.ts",
9
+ "bin": {
10
+ "react-form-steps": "dist/cli.js"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "assets"
15
+ ],
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "react-native": "./dist/index.native.js",
20
+ "import": "./dist/index.mjs",
21
+ "require": "./dist/index.js"
22
+ },
23
+ "./native": {
24
+ "types": "./dist/index.native.d.ts",
25
+ "import": "./dist/index.native.mjs",
26
+ "require": "./dist/index.native.js"
27
+ },
28
+ "./types": {
29
+ "types": "./dist/index.d.ts"
30
+ }
31
+ },
32
+ "scripts": {
33
+ "dev": "tsup --watch",
34
+ "build": "tsup",
35
+ "lint": "tsc --noEmit"
36
+ },
37
+ "keywords": [
38
+ "react",
39
+ "form",
40
+ "multi-step",
41
+ "wizard",
42
+ "react-hook-form",
43
+ "zod",
44
+ "persistence",
45
+ "draft"
46
+ ],
47
+ "author": "Antigravity",
48
+ "license": "MIT",
49
+ "dependencies": {
50
+ "lodash.debounce": "^4.0.8",
51
+ "lucide-react": "^0.363.0"
52
+ },
53
+ "peerDependencies": {
54
+ "react": ">=16.8.0",
55
+ "react-dom": ">=16.8.0",
56
+ "react-hook-form": ">=7.0.0",
57
+ "@hookform/resolvers": ">=3.0.0",
58
+ "zod": ">=3.0.0"
59
+ },
60
+ "peerDependenciesMeta": {
61
+ "react-dom": {
62
+ "optional": true
63
+ }
64
+ },
65
+ "devDependencies": {
66
+ "@types/lodash.debounce": "^4.0.9",
67
+ "@types/react": "^18.2.66",
68
+ "@types/react-dom": "^18.2.22",
69
+ "@types/react-native": "^0.72.8",
70
+ "react": "^18.2.0",
71
+ "react-dom": "^18.2.0",
72
+ "tsup": "^8.0.2",
73
+ "typescript": "^5.4.2"
74
+ },
75
+ "overrides": {
76
+ "@types/react": "^18.2.66"
77
+ }
78
+ }