@rachelallyson/hero-hook-form 2.13.0 → 2.14.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 CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [2.14.0] - 2026-01-29
6
+
7
+ ### Added
8
+
9
+ - **Dynamic custom fields in field arrays** – When each array item can be a different control type (e.g. member custom field values: one row is a date, another a dropdown, another short text), you can use **getItemFieldConfig** with **createCustomFieldConfigForItem** instead of wiring multiple conditionals yourself.
10
+ - **getItemFieldConfig** – Pass this to `createFieldArrayCustomConfig`; for each item you return a single field config (or `null`). The library renders one field per item for you.
11
+ - **createCustomFieldConfigForItem(name, def)** – Given a field path and a definition with `fieldType` (e.g. `'DATE'`, `'SHORT_TEXT'`, `'LONG_TEXT'`, `'NUMBER'`, `'DROPDOWN'`), `name` (label), and optional `options` for dropdowns (newline-separated string or `{ label, value }[]`), returns the right config so you don’t write a switch or five conditionals per item.
12
+ - Use it in ZodForm by adding a `createFieldArrayCustomConfig` with `getItemFieldConfig: ({ field, index }) => createCustomFieldConfigForItem(\`items.\${index}.value\`, { fieldType: def.fieldType, name: def.name, options: def.options })` after looking up the definition for that item (e.g. by `customFieldId`). See the **Custom Field Array Demo** in the example app for a full example.
13
+
5
14
  ## [2.13.0] - 2026-01-29
6
15
 
7
16
  ### Added
package/dist/index.d.ts CHANGED
@@ -4179,6 +4179,65 @@ interface ArraySyncResult<TItem> {
4179
4179
  */
4180
4180
  declare function syncArrays<TItem>(options: ArraySyncOptions<TItem>): ArraySyncResult<TItem>;
4181
4181
 
4182
+ /**
4183
+ * Supported input types for dynamic custom fields (e.g. member custom field values).
4184
+ * Use with createCustomFieldConfigForItem so each array item can render the right control.
4185
+ */
4186
+ type CustomFieldInputType = "DATE" | "DROPDOWN" | "LONG_TEXT" | "NUMBER" | "SHORT_TEXT";
4187
+ /**
4188
+ * Minimal field definition for creating a single field config by type.
4189
+ * Matches common patterns where each array item has a type and label (e.g. customFieldId → field def).
4190
+ */
4191
+ interface CustomFieldDef {
4192
+ fieldType: CustomFieldInputType;
4193
+ name: string;
4194
+ /** For DROPDOWN: newline-separated string or array of { label, value } */
4195
+ options?: string | {
4196
+ label: string;
4197
+ value: string | number;
4198
+ }[];
4199
+ }
4200
+ /**
4201
+ * Create the right ZodFormFieldConfig for a single custom field by type.
4202
+ * Use inside createFieldArrayCustomConfig getItemFieldConfig (or renderItem) so each
4203
+ * array item renders one control (date, short text, long text, number, or dropdown)
4204
+ * instead of multiple conditionals.
4205
+ *
4206
+ * @param name - Form path for the value (e.g. `customFieldValues.${index}.value`)
4207
+ * @param def - Field definition with fieldType, name (label), and optional options for DROPDOWN
4208
+ * @returns ZodFormFieldConfig for use with FormField
4209
+ *
4210
+ * @example
4211
+ * ```tsx
4212
+ * createFieldArrayCustomConfig({
4213
+ * name: 'customFieldValues',
4214
+ * getItemFieldConfig: ({ field, form, index }) => {
4215
+ * const fieldDef = fields.find(f => f.id === field.customFieldId);
4216
+ * if (!fieldDef) return null;
4217
+ * return createCustomFieldConfigForItem(
4218
+ * `customFieldValues.${index}.value`,
4219
+ * { fieldType: fieldDef.fieldType, name: fieldDef.name, options: fieldDef.options }
4220
+ * );
4221
+ * },
4222
+ * defaultItem: () => ({ customFieldId: '', value: '' }),
4223
+ * });
4224
+ * ```
4225
+ */
4226
+ declare function createCustomFieldConfigForItem<T extends FieldValues>(name: Path<T>, def: CustomFieldDef): ZodFormFieldConfig<T>;
4227
+ /** Props passed to renderItem or getItemFieldConfig for each array item */
4228
+ interface FieldArrayItemRenderProps<TFieldValues extends FieldValues, TArrayPath extends ArrayPath<TFieldValues>> {
4229
+ index: number;
4230
+ field: FieldArrayWithId<TFieldValues, TArrayPath>;
4231
+ fields: FieldArrayWithId<TFieldValues, TArrayPath>[];
4232
+ form: UseFormReturn<TFieldValues>;
4233
+ control: Control<TFieldValues>;
4234
+ errors: FieldErrors<TFieldValues>;
4235
+ canMoveUp: boolean;
4236
+ canMoveDown: boolean;
4237
+ onMoveUp: () => void;
4238
+ onMoveDown: () => void;
4239
+ onRemove: () => void;
4240
+ }
4182
4241
  /**
4183
4242
  * Options for creating a custom field array config
4184
4243
  *
@@ -4189,20 +4248,15 @@ interface CreateFieldArrayCustomConfigOptions<TFieldValues extends FieldValues>
4189
4248
  name: ArrayPath<TFieldValues>;
4190
4249
  /** Optional label for the field array */
4191
4250
  label?: string;
4192
- /** Render function for each array item */
4193
- renderItem: (props: {
4194
- index: number;
4195
- field: FieldArrayWithId<TFieldValues, ArrayPath<TFieldValues>>;
4196
- fields: FieldArrayWithId<TFieldValues, ArrayPath<TFieldValues>>[];
4197
- form: UseFormReturn<TFieldValues>;
4198
- control: Control<TFieldValues>;
4199
- errors: FieldErrors<TFieldValues>;
4200
- canMoveUp: boolean;
4201
- canMoveDown: boolean;
4202
- onMoveUp: () => void;
4203
- onMoveDown: () => void;
4204
- onRemove: () => void;
4205
- }) => React$1.ReactNode;
4251
+ /**
4252
+ * Return a single field config for this item; the array helper will render one FormField.
4253
+ * Use with createCustomFieldConfigForItem when each item's control type depends on item data
4254
+ * (e.g. custom fields: look up field def by customFieldId, then createCustomFieldConfigForItem).
4255
+ * When provided, renderItem is optional.
4256
+ */
4257
+ getItemFieldConfig?: (props: FieldArrayItemRenderProps<TFieldValues, ArrayPath<TFieldValues>>) => ZodFormFieldConfig<TFieldValues> | null;
4258
+ /** Render function for each array item (optional if getItemFieldConfig is provided) */
4259
+ renderItem?: (props: FieldArrayItemRenderProps<TFieldValues, ArrayPath<TFieldValues>>) => React$1.ReactNode;
4206
4260
  /** Optional render function for add button */
4207
4261
  renderAddButton?: (props: {
4208
4262
  onAdd: () => void;
@@ -4379,4 +4433,4 @@ declare const validationUtils: {
4379
4433
  }>;
4380
4434
  };
4381
4435
 
4382
- export { AdvancedFieldBuilder, type ArraySyncOptions, type ArraySyncResult, type AutocompleteDefaults, AutocompleteField, type AutocompleteFieldProps, type AutocompleteOption, type AutocompletePassthroughProps, type BaseFormFieldConfig, BasicFormBuilder, type BooleanFieldConfig, type ButtonDefaults, type CheckboxDefaults, CheckboxField, type CheckboxFieldProps, CheckboxGroupField, type CheckboxGroupFieldConfig, type CheckboxGroupFieldProps, type CheckboxGroupPassthroughProps, type CheckboxPassthroughProps, 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 DateInputPassthroughProps, 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, type FileInputPassthroughProps, 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 InputPassthroughProps, type RadioFieldConfig, type RadioGroupDefaults, RadioGroupField, type RadioGroupFieldProps, type RadioGroupPassthroughProps, type SelectDefaults, SelectField, type SelectFieldProps, type SelectPassthroughProps, ServerActionForm, type ServerFieldError, type ServerFormError, SimpleForm, type SimpleFormProps, type SliderDefaults, SliderField, type SliderFieldConfig, type SliderFieldProps, type SliderPassthroughProps, type StringArrayFieldConfig, type StringFieldConfig, SubmitButton, type SubmitButtonProps, type SwitchDefaults, SwitchField, type SwitchFieldProps, type SwitchPassthroughProps, type TextareaDefaults, TextareaField, type TextareaFieldProps, type TextareaPassthroughProps, 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 };
4436
+ export { AdvancedFieldBuilder, type ArraySyncOptions, type ArraySyncResult, type AutocompleteDefaults, AutocompleteField, type AutocompleteFieldProps, type AutocompleteOption, type AutocompletePassthroughProps, type BaseFormFieldConfig, BasicFormBuilder, type BooleanFieldConfig, type ButtonDefaults, type CheckboxDefaults, CheckboxField, type CheckboxFieldProps, CheckboxGroupField, type CheckboxGroupFieldConfig, type CheckboxGroupFieldProps, type CheckboxGroupPassthroughProps, type CheckboxPassthroughProps, type CommonFieldDefaults, CommonFields, ConditionalField, type ConditionalFieldConfig, type ConditionalFieldProps, type ConditionalValidation, ConfigurableForm, ContentField, type ContentFieldConfig, type CreateFieldArrayCustomConfigOptions, type CustomFieldConfig, type CustomFieldDef, type CustomFieldInputType, DateField, type DateFieldConfig, type DateFieldProps, type DateInputDefaults, type DateInputPassthroughProps, type DynamicSectionConfig, DynamicSectionField, type DynamicSectionFieldProps, type EnhancedFormState, FieldArrayBuilder, type FieldArrayConfig, FieldArrayField, type FieldArrayFieldProps, FieldArrayItemBuilder, type FieldArrayItemRenderProps, type FieldBaseProps, type FieldCreationParams, type FieldGroup, FileField, type FileFieldConfig, type FileFieldProps, type FileInputPassthroughProps, 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 InputPassthroughProps, type RadioFieldConfig, type RadioGroupDefaults, RadioGroupField, type RadioGroupFieldProps, type RadioGroupPassthroughProps, type SelectDefaults, SelectField, type SelectFieldProps, type SelectPassthroughProps, ServerActionForm, type ServerFieldError, type ServerFormError, SimpleForm, type SimpleFormProps, type SliderDefaults, SliderField, type SliderFieldConfig, type SliderFieldProps, type SliderPassthroughProps, type StringArrayFieldConfig, type StringFieldConfig, SubmitButton, type SubmitButtonProps, type SwitchDefaults, SwitchField, type SwitchFieldProps, type SwitchPassthroughProps, type TextareaDefaults, TextareaField, type TextareaFieldProps, type TextareaPassthroughProps, TypeInferredBuilder, type UseDebouncedValidationOptions, type UseEnhancedFormStateOptions, type UseInferredFormOptions, type ValidationUtils, type WithControl, type WizardFormConfig, ZodForm, type ZodFormConfig, type ZodFormFieldConfig, applyServerErrors, asyncValidation, commonValidations, createAdvancedBuilder, createBasicFormBuilder, createCustomFieldConfigForItem, 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
@@ -5785,11 +5785,40 @@ function syncArrays(options) {
5785
5785
  import React27 from "react";
5786
5786
  import { useFieldArray as useFieldArray2 } from "react-hook-form";
5787
5787
  import { Button as Button6 } from "@heroui/react";
5788
+ function parseDropdownOptions(options) {
5789
+ if (options == null) return [];
5790
+ if (Array.isArray(options)) return options;
5791
+ return options.split("\n").map((o) => o.trim()).filter((o) => o.length > 0).map((o) => ({ label: o, value: o }));
5792
+ }
5793
+ function createCustomFieldConfigForItem(name, def) {
5794
+ const { fieldType, name: label, options } = def;
5795
+ switch (fieldType) {
5796
+ case "DATE":
5797
+ return FormFieldHelpers.date(name, label, { granularity: "day" });
5798
+ case "SHORT_TEXT":
5799
+ return FormFieldHelpers.input(name, label);
5800
+ case "LONG_TEXT":
5801
+ return FormFieldHelpers.textarea(name, label);
5802
+ case "NUMBER":
5803
+ return FormFieldHelpers.input(name, label, { type: "number" });
5804
+ case "DROPDOWN":
5805
+ return FormFieldHelpers.select(
5806
+ name,
5807
+ label,
5808
+ parseDropdownOptions(options)
5809
+ );
5810
+ default: {
5811
+ void fieldType;
5812
+ return FormFieldHelpers.input(name, label);
5813
+ }
5814
+ }
5815
+ }
5788
5816
  function createFieldArrayCustomConfig(options) {
5789
5817
  const {
5790
5818
  className,
5791
5819
  defaultItem,
5792
5820
  enableReordering = false,
5821
+ getItemFieldConfig,
5793
5822
  label,
5794
5823
  max = 10,
5795
5824
  min = 0,
@@ -5797,6 +5826,11 @@ function createFieldArrayCustomConfig(options) {
5797
5826
  renderAddButton,
5798
5827
  renderItem
5799
5828
  } = options;
5829
+ if (!getItemFieldConfig && !renderItem) {
5830
+ throw new Error(
5831
+ "createFieldArrayCustomConfig: provide either getItemFieldConfig or renderItem"
5832
+ );
5833
+ }
5800
5834
  return {
5801
5835
  className,
5802
5836
  label,
@@ -5834,23 +5868,40 @@ function createFieldArrayCustomConfig(options) {
5834
5868
  move(index, index + 1);
5835
5869
  }
5836
5870
  };
5871
+ const submissionState = {
5872
+ error: void 0,
5873
+ isSubmitted: form.formState.isSubmitted,
5874
+ isSubmitting: form.formState.isSubmitting,
5875
+ isSuccess: false
5876
+ };
5877
+ const itemProps = (index) => ({
5878
+ canMoveDown: enableReordering && index < fields.length - 1,
5879
+ canMoveUp: enableReordering && index > 0,
5880
+ control,
5881
+ errors,
5882
+ field: fields[index],
5883
+ fields,
5884
+ form,
5885
+ index,
5886
+ onMoveDown: () => handleMoveDown(index),
5887
+ onMoveUp: () => handleMoveUp(index),
5888
+ onRemove: () => handleRemove(index)
5889
+ });
5837
5890
  return /* @__PURE__ */ React27.createElement("div", { className }, /* @__PURE__ */ React27.createElement("div", { className: "space-y-4" }, fields.map((field2, index) => {
5838
- const canMoveUp = enableReordering && index > 0;
5839
- const canMoveDown = enableReordering && index < fields.length - 1;
5840
- return /* @__PURE__ */ React27.createElement(React27.Fragment, { key: field2.id }, renderItem({
5841
- canMoveDown,
5842
- canMoveUp,
5843
- control,
5844
- errors,
5845
- // fields from useFieldArray are already typed correctly
5846
- field: field2,
5847
- fields,
5848
- form,
5849
- index,
5850
- onMoveDown: () => handleMoveDown(index),
5851
- onMoveUp: () => handleMoveUp(index),
5852
- onRemove: () => handleRemove(index)
5853
- }));
5891
+ if (getItemFieldConfig) {
5892
+ const config = getItemFieldConfig(itemProps(index));
5893
+ if (!config) return /* @__PURE__ */ React27.createElement(React27.Fragment, { key: field2.id });
5894
+ return /* @__PURE__ */ React27.createElement(
5895
+ FormField,
5896
+ {
5897
+ key: field2.id,
5898
+ config,
5899
+ form,
5900
+ submissionState
5901
+ }
5902
+ );
5903
+ }
5904
+ return /* @__PURE__ */ React27.createElement(React27.Fragment, { key: field2.id }, renderItem(itemProps(index)));
5854
5905
  }), fields.length === 0 && renderAddButton ? /* @__PURE__ */ React27.createElement("div", { className: "text-center py-8 text-gray-500" }, /* @__PURE__ */ React27.createElement("p", null, "No ", label?.toLowerCase() || "items", " added yet."), renderAddButton({ canAdd, onAdd: handleAdd })) : null, fields.length > 0 && renderAddButton ? renderAddButton({ canAdd, onAdd: handleAdd }) : canAdd && /* @__PURE__ */ React27.createElement(
5855
5906
  Button6,
5856
5907
  {
@@ -6121,6 +6172,7 @@ export {
6121
6172
  commonValidations,
6122
6173
  createAdvancedBuilder,
6123
6174
  createBasicFormBuilder,
6175
+ createCustomFieldConfigForItem,
6124
6176
  createDateSchema,
6125
6177
  createEmailSchema,
6126
6178
  createField,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rachelallyson/hero-hook-form",
3
- "version": "2.13.0",
3
+ "version": "2.14.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/",