@rachelallyson/hero-hook-form 2.7.2 → 2.8.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.
- package/CHANGELOG.md +32 -0
- package/dist/index.d.ts +103 -3
- package/dist/index.js +202 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [2.8.0] - 2026-01-22
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **Field Array Memory Leak Fixes**: Comprehensive solution for Cypress Electron renderer memory issues
|
|
10
|
+
- `FormFieldHelpers.conditionalFieldArray()` - Memory-safe conditional field arrays that prevent register/unregister cycles
|
|
11
|
+
- `alwaysRegistered` prop for `FieldArrayField` - Keeps fields registered but conditionally renders UI
|
|
12
|
+
- `useLazyFieldRegistration` and `useLazyFieldArrayRegistration` hooks - Lazy registration for better initial memory usage
|
|
13
|
+
- `fieldArrayMemory` utilities - Memory cleanup helpers with garbage collection hints
|
|
14
|
+
- Cypress memory optimizations - `experimentalMemoryManagement` and reduced memory retention
|
|
15
|
+
|
|
16
|
+
- **Performance Monitoring**: Enhanced field array performance tracking
|
|
17
|
+
- Memory usage monitoring for field array operations
|
|
18
|
+
- Performance metrics collection for long-running tests
|
|
19
|
+
- Garbage collection suggestions for memory-intensive operations
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **Critical Bug Fix: Input Name Attributes**: Fixed missing `name` prop forwarding in `InputField` component
|
|
24
|
+
- `CoercedInput` now properly passes `name={field.name}` to HeroUI Input components
|
|
25
|
+
- Fixes accessibility issues where form inputs had no name attribute in DOM
|
|
26
|
+
- Critical for form automation tools, accessibility compliance, and proper form submission
|
|
27
|
+
- Affects all input types (text, email, password, tel, number, etc.) in ZodForm
|
|
28
|
+
|
|
29
|
+
### Internal
|
|
30
|
+
|
|
31
|
+
- **Architecture Cleanup: Simplified HeroUI Integration**: Removed redundant dual-build system and `/react` subpath
|
|
32
|
+
- Eliminated duplicate `react/fields/` components and `tsconfig.react.json`
|
|
33
|
+
- Streamlined to single `#ui` alias approach that works with both individual `@heroui/*` packages and `@heroui/react`
|
|
34
|
+
- `@heroui/react` re-exports all components, making separate configurations unnecessary
|
|
35
|
+
- Cleaner codebase with same functionality and flexibility for users
|
|
36
|
+
|
|
5
37
|
## [2.7.2] - 2026-01-21
|
|
6
38
|
|
|
7
39
|
### Fixed
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React$1, { ComponentProps } from 'react';
|
|
2
2
|
import { Button } from '@heroui/react';
|
|
3
3
|
import * as react_hook_form from 'react-hook-form';
|
|
4
|
-
import { FieldValues, Path, RegisterOptions, ArrayPath, Control, UseFormReturn, FieldErrors, UseFormProps, SubmitHandler, DefaultValues, UseFormSetError, FieldArrayWithId } from 'react-hook-form';
|
|
4
|
+
import { FieldValues, Path, RegisterOptions, ArrayPath, Control, UseFormReturn, FieldErrors, UseFormProps, SubmitHandler, DefaultValues, UseFormSetError, FieldPath, FieldArrayWithId } from 'react-hook-form';
|
|
5
5
|
export { UseFormReturn, useFormContext } from 'react-hook-form';
|
|
6
6
|
import * as zod from 'zod';
|
|
7
7
|
import { z, ZodSchema } from 'zod';
|
|
@@ -197,6 +197,8 @@ interface FieldArrayConfig<TFieldValues extends FieldValues> extends Omit<BaseFo
|
|
|
197
197
|
};
|
|
198
198
|
/** Function to create default item when adding new array item */
|
|
199
199
|
defaultItem?: () => any;
|
|
200
|
+
/** Whether this field array should always be registered (for conditional rendering) */
|
|
201
|
+
alwaysRegistered?: boolean;
|
|
200
202
|
/** Custom render function for array items */
|
|
201
203
|
renderItem?: (props: {
|
|
202
204
|
/** Item index (0-based) */
|
|
@@ -2597,6 +2599,41 @@ declare const FormFieldHelpers: {
|
|
|
2597
2599
|
* ```
|
|
2598
2600
|
*/
|
|
2599
2601
|
conditional: <T extends FieldValues = FieldValues>(name: Path<T>, condition: (formData: Partial<T>) => boolean, field: ZodFormFieldConfig<T>) => ZodFormFieldConfig<T>;
|
|
2602
|
+
/**
|
|
2603
|
+
* Create a conditional field array that avoids memory leaks in Cypress tests.
|
|
2604
|
+
*
|
|
2605
|
+
* This helper creates a field array that is always registered but conditionally
|
|
2606
|
+
* rendered, preventing the register/unregister cycles that cause memory
|
|
2607
|
+
* accumulation in Cypress Electron renderer.
|
|
2608
|
+
*
|
|
2609
|
+
* @param name - The field array name
|
|
2610
|
+
* @param condition - Function that determines if the field array should be visible
|
|
2611
|
+
* @param label - Display label for the field array
|
|
2612
|
+
* @param fields - Field configurations for array items
|
|
2613
|
+
* @param options - Additional field array options
|
|
2614
|
+
*
|
|
2615
|
+
* @example
|
|
2616
|
+
* ```tsx
|
|
2617
|
+
* // Memory-safe conditional field array for multiple choice options
|
|
2618
|
+
* FormFieldHelpers.conditionalFieldArray(
|
|
2619
|
+
* "choices",
|
|
2620
|
+
* (data) => data.questionType === 'MULTIPLE_CHOICE',
|
|
2621
|
+
* "Answer Choices",
|
|
2622
|
+
* [
|
|
2623
|
+
* FormFieldHelpers.input("text", "Choice Text"),
|
|
2624
|
+
* FormFieldHelpers.checkbox("isCorrect", "Correct Answer"),
|
|
2625
|
+
* ]
|
|
2626
|
+
* )
|
|
2627
|
+
* ```
|
|
2628
|
+
*/
|
|
2629
|
+
conditionalFieldArray: <T extends FieldValues = FieldValues>(name: ArrayPath<T>, condition: (formData: Partial<T>) => boolean, label: string, fields: ZodFormFieldConfig<T>[], options?: {
|
|
2630
|
+
min?: number;
|
|
2631
|
+
max?: number;
|
|
2632
|
+
addButtonText?: string;
|
|
2633
|
+
removeButtonText?: string;
|
|
2634
|
+
enableReordering?: boolean;
|
|
2635
|
+
defaultItem?: () => any;
|
|
2636
|
+
}) => ZodFormFieldConfig<T>;
|
|
2600
2637
|
/**
|
|
2601
2638
|
* Create a content field for headers, questions, or custom content between fields
|
|
2602
2639
|
*
|
|
@@ -3639,6 +3676,33 @@ declare function useTypeInferredForm<T extends FieldValues>(formConfig: {
|
|
|
3639
3676
|
fields: ZodFormFieldConfig<T>[];
|
|
3640
3677
|
}, options?: UseInferredFormOptions<T>): UseFormReturn<T>;
|
|
3641
3678
|
|
|
3679
|
+
/**
|
|
3680
|
+
* Hook for lazy field registration to reduce initial memory usage.
|
|
3681
|
+
*
|
|
3682
|
+
* This hook registers fields only when they become active (e.g., when a condition is met),
|
|
3683
|
+
* preventing the memory overhead of always-registered fields while avoiding
|
|
3684
|
+
* register/unregister cycles that cause memory leaks in Cypress.
|
|
3685
|
+
*
|
|
3686
|
+
* @param fieldName - The field name to potentially register
|
|
3687
|
+
* @param shouldRegister - Function that determines if the field should be registered
|
|
3688
|
+
* @param defaultValue - Default value for the field when registered
|
|
3689
|
+
* @param rules - Validation rules for the field
|
|
3690
|
+
*/
|
|
3691
|
+
declare function useLazyFieldRegistration<TFieldValues extends FieldValues>(fieldName: FieldPath<TFieldValues>, shouldRegister: () => boolean, defaultValue?: any, rules?: any): {
|
|
3692
|
+
currentValue: undefined;
|
|
3693
|
+
isRegistered: boolean;
|
|
3694
|
+
};
|
|
3695
|
+
/**
|
|
3696
|
+
* Hook for lazy field array registration.
|
|
3697
|
+
*
|
|
3698
|
+
* Similar to useLazyFieldRegistration but specifically for field arrays,
|
|
3699
|
+
* which have more complex registration requirements.
|
|
3700
|
+
*/
|
|
3701
|
+
declare function useLazyFieldArrayRegistration<TFieldValues extends FieldValues>(arrayName: FieldPath<TFieldValues>, shouldRegister: () => boolean, defaultValue?: any[]): {
|
|
3702
|
+
currentValue: any;
|
|
3703
|
+
isRegistered: boolean;
|
|
3704
|
+
};
|
|
3705
|
+
|
|
3642
3706
|
/**
|
|
3643
3707
|
* Props for the ConditionalField component.
|
|
3644
3708
|
*
|
|
@@ -3718,6 +3782,8 @@ declare function ContentField<TFieldValues extends FieldValues>({ config, form,
|
|
|
3718
3782
|
interface FieldArrayFieldProps<TFieldValues extends FieldValues> {
|
|
3719
3783
|
config: FieldArrayConfig<TFieldValues>;
|
|
3720
3784
|
className?: string;
|
|
3785
|
+
/** Whether this field array should always be registered (for conditional rendering) */
|
|
3786
|
+
alwaysRegistered?: boolean;
|
|
3721
3787
|
}
|
|
3722
3788
|
/**
|
|
3723
3789
|
* Field array component for dynamic repeating field groups.
|
|
@@ -3834,7 +3900,7 @@ interface FieldArrayFieldProps<TFieldValues extends FieldValues> {
|
|
|
3834
3900
|
* @see {@link createFieldArrayCustomConfig} for advanced custom rendering
|
|
3835
3901
|
* @category Fields
|
|
3836
3902
|
*/
|
|
3837
|
-
declare function FieldArrayField<TFieldValues extends FieldValues>({ className, config, }: FieldArrayFieldProps<TFieldValues>): React$1.JSX.Element | null;
|
|
3903
|
+
declare function FieldArrayField<TFieldValues extends FieldValues>({ alwaysRegistered, className, config, }: FieldArrayFieldProps<TFieldValues>): React$1.JSX.Element | null;
|
|
3838
3904
|
|
|
3839
3905
|
/**
|
|
3840
3906
|
* Props for the DynamicSectionField component.
|
|
@@ -4126,6 +4192,40 @@ interface CreateFieldArrayCustomConfigOptions<TFieldValues extends FieldValues>
|
|
|
4126
4192
|
*/
|
|
4127
4193
|
declare function createFieldArrayCustomConfig<TFieldValues extends FieldValues>(options: CreateFieldArrayCustomConfigOptions<TFieldValues>): CustomFieldConfig<TFieldValues>;
|
|
4128
4194
|
|
|
4195
|
+
/**
|
|
4196
|
+
* Memory management utilities for field arrays to prevent memory leaks
|
|
4197
|
+
* in Cypress tests and long-running applications.
|
|
4198
|
+
*/
|
|
4199
|
+
/**
|
|
4200
|
+
* Hook to clean up field array memory when component unmounts.
|
|
4201
|
+
* Helps prevent memory accumulation in Cypress Electron renderer.
|
|
4202
|
+
*/
|
|
4203
|
+
declare function useFieldArrayMemoryCleanup<TFieldValues extends FieldValues>(arrayName: FieldPath<TFieldValues>): {
|
|
4204
|
+
cleanup: () => void;
|
|
4205
|
+
};
|
|
4206
|
+
/**
|
|
4207
|
+
* Utility to force garbage collection hints for Cypress tests.
|
|
4208
|
+
* Only effective when experimentalMemoryManagement is enabled.
|
|
4209
|
+
*/
|
|
4210
|
+
declare function suggestGarbageCollection(): void;
|
|
4211
|
+
/**
|
|
4212
|
+
* Memory-safe field array operations that include cleanup hints.
|
|
4213
|
+
*/
|
|
4214
|
+
declare const memorySafeFieldArray: {
|
|
4215
|
+
/**
|
|
4216
|
+
* Add items to a field array with memory management.
|
|
4217
|
+
*/
|
|
4218
|
+
addItems: <TFieldValues extends FieldValues>(append: (value: any) => void, items: any[], onProgress?: (addedCount: number) => void) => void;
|
|
4219
|
+
/**
|
|
4220
|
+
* Clear entire field array with memory cleanup.
|
|
4221
|
+
*/
|
|
4222
|
+
clearArray: <TFieldValues extends FieldValues>(setValue: (name: FieldPath<TFieldValues>, value: any) => void, arrayName: FieldPath<TFieldValues>) => void;
|
|
4223
|
+
/**
|
|
4224
|
+
* Remove items from a field array with memory cleanup.
|
|
4225
|
+
*/
|
|
4226
|
+
removeItems: <TFieldValues extends FieldValues>(remove: (index: number) => void, indices: number[]) => void;
|
|
4227
|
+
};
|
|
4228
|
+
|
|
4129
4229
|
/**
|
|
4130
4230
|
* Common validation patterns for forms
|
|
4131
4231
|
*/
|
|
@@ -4209,4 +4309,4 @@ declare const validationUtils: {
|
|
|
4209
4309
|
}>;
|
|
4210
4310
|
};
|
|
4211
4311
|
|
|
4212
|
-
export { AdvancedFieldBuilder, type ArraySyncOptions, type ArraySyncResult, AutocompleteField, type AutocompleteFieldProps, type AutocompleteOption, type BaseFormFieldConfig, BasicFormBuilder, type BooleanFieldConfig, type ButtonDefaults, type CheckboxDefaults, CheckboxField, type CheckboxFieldProps, CheckboxGroupField, type CheckboxGroupFieldConfig, type CheckboxGroupFieldProps, type CommonFieldDefaults, CommonFields, ConditionalField, type ConditionalFieldConfig, type ConditionalFieldProps, type ConditionalValidation, ConfigurableForm, ContentField, type ContentFieldConfig, type CreateFieldArrayCustomConfigOptions, type CustomFieldConfig, DateField, type DateFieldConfig, type DateFieldProps, type DateInputDefaults, type DynamicSectionConfig, DynamicSectionField, type DynamicSectionFieldProps, type EnhancedFormState, FieldArrayBuilder, type FieldArrayConfig, FieldArrayField, type FieldArrayFieldProps, FieldArrayItemBuilder, type FieldBaseProps, type FieldCreationParams, type FieldGroup, FileField, type FileFieldConfig, type FileFieldProps, FontPickerField, type FontPickerFieldConfig, type FontPickerFieldProps, type FormConfig, FormField, type FormFieldConfig, FormFieldHelpers, type FormFieldType, type FormProps, FormProvider, FormStatus, type FormStatusProps, type FormStep, type FormSubmissionState, type FormTestUtils, FormToast, type FormToastProps, type FormValidationError, type HeroHookFormDefaultsConfig, HeroHookFormProvider, type HeroHookFormProviderProps, type InputDefaults, InputField, type InputFieldProps, type RadioFieldConfig, type RadioGroupDefaults, RadioGroupField, type RadioGroupFieldProps, type SelectDefaults, SelectField, type SelectFieldProps, ServerActionForm, type ServerFieldError, type ServerFormError, SimpleForm, type SimpleFormProps, type SliderDefaults, SliderField, type SliderFieldConfig, type SliderFieldProps, type StringArrayFieldConfig, type StringFieldConfig, SubmitButton, type SubmitButtonProps, type SwitchDefaults, SwitchField, type SwitchFieldProps, type TextareaDefaults, TextareaField, type TextareaFieldProps, TypeInferredBuilder, type UseDebouncedValidationOptions, type UseEnhancedFormStateOptions, type UseInferredFormOptions, type ValidationUtils, type WithControl, type WizardFormConfig, ZodForm, type ZodFormConfig, type ZodFormFieldConfig, applyServerErrors, asyncValidation, commonValidations, createAdvancedBuilder, createBasicFormBuilder, createDateSchema, createEmailSchema, createField, createFieldArrayBuilder, createFieldArrayCustomConfig, createFieldArrayItemBuilder, createFileSchema, createFormTestUtils, createFutureDateSchema, createMaxLengthSchema, createMinLengthSchema, createMockFormData, createMockFormErrors, createNestedPathBuilder, createNumberRangeSchema, createOptimizedFieldHandler, createPasswordSchema, createPastDateSchema, createPhoneSchema, createRequiredCheckboxSchema, createRequiredSchema, createTypeInferredBuilder, createUrlSchema, createZodFormConfig, crossFieldValidation, debounce, deepEqual, defineInferredForm, errorMessages, field, getFieldError, getFormErrors, hasFieldError, hasFormErrors, pathToString, serverValidation, shallowEqual, simulateFieldInput, simulateFormSubmission, syncArrays, throttle, useDebouncedFieldValidation, useDebouncedValidation, useEnhancedFormState, useFormHelper, useHeroForm, useHeroHookFormDefaults, useInferredForm, useMemoizedCallback, useMemoizedFieldProps, usePerformanceMonitor, useTypeInferredForm, useZodForm, validationPatterns, validationUtils, waitForFormState };
|
|
4312
|
+
export { AdvancedFieldBuilder, type ArraySyncOptions, type ArraySyncResult, AutocompleteField, type AutocompleteFieldProps, type AutocompleteOption, type BaseFormFieldConfig, BasicFormBuilder, type BooleanFieldConfig, type ButtonDefaults, type CheckboxDefaults, CheckboxField, type CheckboxFieldProps, CheckboxGroupField, type CheckboxGroupFieldConfig, type CheckboxGroupFieldProps, type CommonFieldDefaults, CommonFields, ConditionalField, type ConditionalFieldConfig, type ConditionalFieldProps, type ConditionalValidation, ConfigurableForm, ContentField, type ContentFieldConfig, type CreateFieldArrayCustomConfigOptions, type CustomFieldConfig, DateField, type DateFieldConfig, type DateFieldProps, type DateInputDefaults, type DynamicSectionConfig, DynamicSectionField, type DynamicSectionFieldProps, type EnhancedFormState, FieldArrayBuilder, type FieldArrayConfig, FieldArrayField, type FieldArrayFieldProps, FieldArrayItemBuilder, type FieldBaseProps, type FieldCreationParams, type FieldGroup, FileField, type FileFieldConfig, type FileFieldProps, FontPickerField, type FontPickerFieldConfig, type FontPickerFieldProps, type FormConfig, FormField, type FormFieldConfig, FormFieldHelpers, type FormFieldType, type FormProps, FormProvider, FormStatus, type FormStatusProps, type FormStep, type FormSubmissionState, type FormTestUtils, FormToast, type FormToastProps, type FormValidationError, type HeroHookFormDefaultsConfig, HeroHookFormProvider, type HeroHookFormProviderProps, type InputDefaults, InputField, type InputFieldProps, type RadioFieldConfig, type RadioGroupDefaults, RadioGroupField, type RadioGroupFieldProps, type SelectDefaults, SelectField, type SelectFieldProps, ServerActionForm, type ServerFieldError, type ServerFormError, SimpleForm, type SimpleFormProps, type SliderDefaults, SliderField, type SliderFieldConfig, type SliderFieldProps, type StringArrayFieldConfig, type StringFieldConfig, SubmitButton, type SubmitButtonProps, type SwitchDefaults, SwitchField, type SwitchFieldProps, type TextareaDefaults, TextareaField, type TextareaFieldProps, TypeInferredBuilder, type UseDebouncedValidationOptions, type UseEnhancedFormStateOptions, type UseInferredFormOptions, type ValidationUtils, type WithControl, type WizardFormConfig, ZodForm, type ZodFormConfig, type ZodFormFieldConfig, applyServerErrors, asyncValidation, commonValidations, createAdvancedBuilder, createBasicFormBuilder, createDateSchema, createEmailSchema, createField, createFieldArrayBuilder, createFieldArrayCustomConfig, createFieldArrayItemBuilder, createFileSchema, createFormTestUtils, createFutureDateSchema, createMaxLengthSchema, createMinLengthSchema, createMockFormData, createMockFormErrors, createNestedPathBuilder, createNumberRangeSchema, createOptimizedFieldHandler, createPasswordSchema, createPastDateSchema, createPhoneSchema, createRequiredCheckboxSchema, createRequiredSchema, createTypeInferredBuilder, createUrlSchema, createZodFormConfig, crossFieldValidation, debounce, deepEqual, defineInferredForm, errorMessages, field, getFieldError, getFormErrors, hasFieldError, hasFormErrors, memorySafeFieldArray, pathToString, serverValidation, shallowEqual, simulateFieldInput, simulateFormSubmission, suggestGarbageCollection, syncArrays, throttle, useDebouncedFieldValidation, useDebouncedValidation, useEnhancedFormState, useFieldArrayMemoryCleanup, useFormHelper, useHeroForm, useHeroHookFormDefaults, useInferredForm, useLazyFieldArrayRegistration, useLazyFieldRegistration, useMemoizedCallback, useMemoizedFieldProps, usePerformanceMonitor, useTypeInferredForm, useZodForm, validationPatterns, validationUtils, waitForFormState };
|
package/dist/index.js
CHANGED
|
@@ -530,6 +530,7 @@ import React9 from "react";
|
|
|
530
530
|
import { useFieldArray, useFormContext as useFormContext3 } from "react-hook-form";
|
|
531
531
|
import { Button as Button2 } from "@heroui/react";
|
|
532
532
|
function FieldArrayField({
|
|
533
|
+
alwaysRegistered = false,
|
|
533
534
|
className,
|
|
534
535
|
config
|
|
535
536
|
}) {
|
|
@@ -557,6 +558,9 @@ function FieldArrayField({
|
|
|
557
558
|
});
|
|
558
559
|
const canAdd = fields.length < max;
|
|
559
560
|
const canRemove = fields.length > min;
|
|
561
|
+
if (alwaysRegistered && fields.length === 0) {
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
560
564
|
const getFieldPath = (fieldName, itemIndex) => {
|
|
561
565
|
return `${String(name)}.${itemIndex}.${fieldName}`;
|
|
562
566
|
};
|
|
@@ -1614,7 +1618,8 @@ function FormFieldComponent({
|
|
|
1614
1618
|
FieldArrayField,
|
|
1615
1619
|
{
|
|
1616
1620
|
config: fieldArrayConfig,
|
|
1617
|
-
className: fieldArrayConfig.className
|
|
1621
|
+
className: fieldArrayConfig.className,
|
|
1622
|
+
alwaysRegistered: "alwaysRegistered" in fieldConfig ? fieldConfig.alwaysRegistered : false
|
|
1618
1623
|
}
|
|
1619
1624
|
);
|
|
1620
1625
|
}
|
|
@@ -3256,6 +3261,57 @@ var FormFieldHelpers = {
|
|
|
3256
3261
|
};
|
|
3257
3262
|
return config;
|
|
3258
3263
|
},
|
|
3264
|
+
/**
|
|
3265
|
+
* Create a conditional field array that avoids memory leaks in Cypress tests.
|
|
3266
|
+
*
|
|
3267
|
+
* This helper creates a field array that is always registered but conditionally
|
|
3268
|
+
* rendered, preventing the register/unregister cycles that cause memory
|
|
3269
|
+
* accumulation in Cypress Electron renderer.
|
|
3270
|
+
*
|
|
3271
|
+
* @param name - The field array name
|
|
3272
|
+
* @param condition - Function that determines if the field array should be visible
|
|
3273
|
+
* @param label - Display label for the field array
|
|
3274
|
+
* @param fields - Field configurations for array items
|
|
3275
|
+
* @param options - Additional field array options
|
|
3276
|
+
*
|
|
3277
|
+
* @example
|
|
3278
|
+
* ```tsx
|
|
3279
|
+
* // Memory-safe conditional field array for multiple choice options
|
|
3280
|
+
* FormFieldHelpers.conditionalFieldArray(
|
|
3281
|
+
* "choices",
|
|
3282
|
+
* (data) => data.questionType === 'MULTIPLE_CHOICE',
|
|
3283
|
+
* "Answer Choices",
|
|
3284
|
+
* [
|
|
3285
|
+
* FormFieldHelpers.input("text", "Choice Text"),
|
|
3286
|
+
* FormFieldHelpers.checkbox("isCorrect", "Correct Answer"),
|
|
3287
|
+
* ]
|
|
3288
|
+
* )
|
|
3289
|
+
* ```
|
|
3290
|
+
*/
|
|
3291
|
+
conditionalFieldArray: (name, condition, label, fields, options) => {
|
|
3292
|
+
const fieldArrayConfig = {
|
|
3293
|
+
addButtonText: options?.addButtonText ?? "Add Item",
|
|
3294
|
+
alwaysRegistered: true,
|
|
3295
|
+
defaultItem: options?.defaultItem,
|
|
3296
|
+
enableReordering: options?.enableReordering ?? false,
|
|
3297
|
+
fields,
|
|
3298
|
+
label,
|
|
3299
|
+
max: options?.max ?? 10,
|
|
3300
|
+
// This prevents register/unregister cycles
|
|
3301
|
+
min: options?.min ?? 0,
|
|
3302
|
+
name,
|
|
3303
|
+
removeButtonText: options?.removeButtonText ?? "Remove",
|
|
3304
|
+
type: "fieldArray"
|
|
3305
|
+
};
|
|
3306
|
+
const config = {
|
|
3307
|
+
condition,
|
|
3308
|
+
field: fieldArrayConfig,
|
|
3309
|
+
name,
|
|
3310
|
+
// ArrayPath extends Path, so this is safe
|
|
3311
|
+
type: "conditional"
|
|
3312
|
+
};
|
|
3313
|
+
return config;
|
|
3314
|
+
},
|
|
3259
3315
|
/**
|
|
3260
3316
|
* Create a content field for headers, questions, or custom content between fields
|
|
3261
3317
|
*
|
|
@@ -4475,8 +4531,74 @@ function useTypeInferredForm(formConfig, options = {}) {
|
|
|
4475
4531
|
return useInferredForm(formConfig.schema, formConfig.fields, options);
|
|
4476
4532
|
}
|
|
4477
4533
|
|
|
4534
|
+
// src/hooks/useLazyFieldRegistration.ts
|
|
4535
|
+
import { useEffect as useEffect3, useRef as useRef2, useState as useState4 } from "react";
|
|
4536
|
+
import { useFormContext as useFormContext6 } from "react-hook-form";
|
|
4537
|
+
function useLazyFieldRegistration(fieldName, shouldRegister, defaultValue, rules) {
|
|
4538
|
+
const { register, setValue, unregister, watch } = useFormContext6();
|
|
4539
|
+
const [isRegistered, setIsRegistered] = useState4(false);
|
|
4540
|
+
const hasCheckedRegistration = useRef2(false);
|
|
4541
|
+
const conditionMet = shouldRegister();
|
|
4542
|
+
useEffect3(() => {
|
|
4543
|
+
if (conditionMet && !isRegistered) {
|
|
4544
|
+
register(fieldName, rules);
|
|
4545
|
+
if (defaultValue !== void 0) {
|
|
4546
|
+
setValue(fieldName, defaultValue);
|
|
4547
|
+
}
|
|
4548
|
+
setIsRegistered(true);
|
|
4549
|
+
hasCheckedRegistration.current = true;
|
|
4550
|
+
} else if (!conditionMet && isRegistered && hasCheckedRegistration.current) {
|
|
4551
|
+
unregister(fieldName);
|
|
4552
|
+
setIsRegistered(false);
|
|
4553
|
+
}
|
|
4554
|
+
}, [
|
|
4555
|
+
conditionMet,
|
|
4556
|
+
fieldName,
|
|
4557
|
+
register,
|
|
4558
|
+
unregister,
|
|
4559
|
+
setValue,
|
|
4560
|
+
rules,
|
|
4561
|
+
defaultValue,
|
|
4562
|
+
isRegistered
|
|
4563
|
+
]);
|
|
4564
|
+
return {
|
|
4565
|
+
currentValue: isRegistered ? watch(fieldName) : void 0,
|
|
4566
|
+
isRegistered
|
|
4567
|
+
};
|
|
4568
|
+
}
|
|
4569
|
+
function useLazyFieldArrayRegistration(arrayName, shouldRegister, defaultValue = []) {
|
|
4570
|
+
const { setValue, watch } = useFormContext6();
|
|
4571
|
+
const [isRegistered, setIsRegistered] = useState4(false);
|
|
4572
|
+
const hasCheckedRegistration = useRef2(false);
|
|
4573
|
+
const conditionMet = shouldRegister();
|
|
4574
|
+
const currentValue = watch(arrayName);
|
|
4575
|
+
useEffect3(() => {
|
|
4576
|
+
if (conditionMet && !isRegistered) {
|
|
4577
|
+
if (!currentValue || !Array.isArray(currentValue)) {
|
|
4578
|
+
setValue(arrayName, defaultValue);
|
|
4579
|
+
}
|
|
4580
|
+
setIsRegistered(true);
|
|
4581
|
+
hasCheckedRegistration.current = true;
|
|
4582
|
+
} else if (!conditionMet && isRegistered && hasCheckedRegistration.current) {
|
|
4583
|
+
setValue(arrayName, []);
|
|
4584
|
+
setIsRegistered(false);
|
|
4585
|
+
}
|
|
4586
|
+
}, [
|
|
4587
|
+
conditionMet,
|
|
4588
|
+
arrayName,
|
|
4589
|
+
setValue,
|
|
4590
|
+
defaultValue,
|
|
4591
|
+
isRegistered,
|
|
4592
|
+
currentValue
|
|
4593
|
+
]);
|
|
4594
|
+
return {
|
|
4595
|
+
currentValue: isRegistered ? currentValue : [],
|
|
4596
|
+
isRegistered
|
|
4597
|
+
};
|
|
4598
|
+
}
|
|
4599
|
+
|
|
4478
4600
|
// src/utils/performance.ts
|
|
4479
|
-
import { useCallback as useCallback3, useMemo as useMemo2, useRef as
|
|
4601
|
+
import { useCallback as useCallback3, useMemo as useMemo2, useRef as useRef3 } from "react";
|
|
4480
4602
|
function debounce(func, delay) {
|
|
4481
4603
|
let timeoutId;
|
|
4482
4604
|
return (...args) => {
|
|
@@ -4495,7 +4617,7 @@ function throttle(func, limit) {
|
|
|
4495
4617
|
};
|
|
4496
4618
|
}
|
|
4497
4619
|
function useMemoizedCallback(callback, deps) {
|
|
4498
|
-
const callbackRef =
|
|
4620
|
+
const callbackRef = useRef3(callback);
|
|
4499
4621
|
callbackRef.current = callback;
|
|
4500
4622
|
return useCallback3(
|
|
4501
4623
|
((...args) => callbackRef.current(...args)),
|
|
@@ -4541,8 +4663,8 @@ function deepEqual(prevProps, nextProps) {
|
|
|
4541
4663
|
return true;
|
|
4542
4664
|
}
|
|
4543
4665
|
function usePerformanceMonitor(componentName, enabled = process.env.NODE_ENV === "development") {
|
|
4544
|
-
const renderCountRef =
|
|
4545
|
-
const lastRenderTimeRef =
|
|
4666
|
+
const renderCountRef = useRef3(0);
|
|
4667
|
+
const lastRenderTimeRef = useRef3(Date.now());
|
|
4546
4668
|
if (enabled) {
|
|
4547
4669
|
renderCountRef.current += 1;
|
|
4548
4670
|
const now = Date.now();
|
|
@@ -4702,6 +4824,76 @@ function createFieldArrayCustomConfig(options) {
|
|
|
4702
4824
|
};
|
|
4703
4825
|
}
|
|
4704
4826
|
|
|
4827
|
+
// src/utils/fieldArrayMemory.ts
|
|
4828
|
+
import { useFormContext as useFormContext7 } from "react-hook-form";
|
|
4829
|
+
function useFieldArrayMemoryCleanup(arrayName) {
|
|
4830
|
+
const { reset, unregister } = useFormContext7();
|
|
4831
|
+
const cleanup = () => {
|
|
4832
|
+
try {
|
|
4833
|
+
reset((formValues) => ({
|
|
4834
|
+
...formValues,
|
|
4835
|
+
[arrayName]: []
|
|
4836
|
+
}));
|
|
4837
|
+
setTimeout(() => {
|
|
4838
|
+
try {
|
|
4839
|
+
unregister(arrayName);
|
|
4840
|
+
} catch (error) {
|
|
4841
|
+
console.debug("Field array cleanup unregister failed:", error);
|
|
4842
|
+
}
|
|
4843
|
+
}, 0);
|
|
4844
|
+
} catch (error) {
|
|
4845
|
+
console.debug("Field array cleanup failed:", error);
|
|
4846
|
+
}
|
|
4847
|
+
};
|
|
4848
|
+
return { cleanup };
|
|
4849
|
+
}
|
|
4850
|
+
function suggestGarbageCollection() {
|
|
4851
|
+
if (typeof window !== "undefined" && "gc" in window) {
|
|
4852
|
+
try {
|
|
4853
|
+
window.gc();
|
|
4854
|
+
} catch (error) {
|
|
4855
|
+
}
|
|
4856
|
+
}
|
|
4857
|
+
}
|
|
4858
|
+
var memorySafeFieldArray = {
|
|
4859
|
+
/**
|
|
4860
|
+
* Add items to a field array with memory management.
|
|
4861
|
+
*/
|
|
4862
|
+
addItems: (append, items, onProgress) => {
|
|
4863
|
+
let addedCount = 0;
|
|
4864
|
+
const batchSize = 10;
|
|
4865
|
+
for (let i = 0; i < items.length; i += batchSize) {
|
|
4866
|
+
const batch = items.slice(i, i + batchSize);
|
|
4867
|
+
batch.forEach((item) => {
|
|
4868
|
+
append(item);
|
|
4869
|
+
addedCount++;
|
|
4870
|
+
});
|
|
4871
|
+
if (addedCount % batchSize === 0) {
|
|
4872
|
+
suggestGarbageCollection();
|
|
4873
|
+
onProgress?.(addedCount);
|
|
4874
|
+
}
|
|
4875
|
+
}
|
|
4876
|
+
suggestGarbageCollection();
|
|
4877
|
+
},
|
|
4878
|
+
/**
|
|
4879
|
+
* Clear entire field array with memory cleanup.
|
|
4880
|
+
*/
|
|
4881
|
+
clearArray: (setValue, arrayName) => {
|
|
4882
|
+
setValue(arrayName, []);
|
|
4883
|
+
suggestGarbageCollection();
|
|
4884
|
+
},
|
|
4885
|
+
/**
|
|
4886
|
+
* Remove items from a field array with memory cleanup.
|
|
4887
|
+
*/
|
|
4888
|
+
removeItems: (remove, indices) => {
|
|
4889
|
+
const sortedIndices = [...indices].sort((a, b) => b - a);
|
|
4890
|
+
sortedIndices.forEach((index) => {
|
|
4891
|
+
remove(index);
|
|
4892
|
+
});
|
|
4893
|
+
suggestGarbageCollection();
|
|
4894
|
+
}
|
|
4895
|
+
};
|
|
4896
|
+
|
|
4705
4897
|
// src/builders/validation-helpers.ts
|
|
4706
4898
|
import { z as z4 } from "zod";
|
|
4707
4899
|
var validationPatterns = {
|
|
@@ -4922,21 +5114,26 @@ export {
|
|
|
4922
5114
|
getFormErrors,
|
|
4923
5115
|
hasFieldError,
|
|
4924
5116
|
hasFormErrors,
|
|
5117
|
+
memorySafeFieldArray,
|
|
4925
5118
|
pathToString,
|
|
4926
5119
|
serverValidation,
|
|
4927
5120
|
shallowEqual,
|
|
4928
5121
|
simulateFieldInput,
|
|
4929
5122
|
simulateFormSubmission,
|
|
5123
|
+
suggestGarbageCollection,
|
|
4930
5124
|
syncArrays,
|
|
4931
5125
|
throttle,
|
|
4932
5126
|
useDebouncedFieldValidation,
|
|
4933
5127
|
useDebouncedValidation,
|
|
4934
5128
|
useEnhancedFormState,
|
|
5129
|
+
useFieldArrayMemoryCleanup,
|
|
4935
5130
|
useFormContext5 as useFormContext,
|
|
4936
5131
|
useFormHelper,
|
|
4937
5132
|
useHeroForm,
|
|
4938
5133
|
useHeroHookFormDefaults,
|
|
4939
5134
|
useInferredForm,
|
|
5135
|
+
useLazyFieldArrayRegistration,
|
|
5136
|
+
useLazyFieldRegistration,
|
|
4940
5137
|
useMemoizedCallback,
|
|
4941
5138
|
useMemoizedFieldProps,
|
|
4942
5139
|
usePerformanceMonitor,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rachelallyson/hero-hook-form",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.0",
|
|
4
4
|
"description": "Typed form helpers that combine React Hook Form and HeroUI components.",
|
|
5
5
|
"author": "Rachel Higley",
|
|
6
6
|
"homepage": "https://rachelallyson.github.io/hero-hook-form/",
|