@rachelallyson/hero-hook-form 2.11.0 → 2.13.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,32 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [2.13.0] - 2026-01-29
6
+
7
+ ### Added
8
+
9
+ - **ConfigProvider: autocomplete defaults** – `HeroHookFormDefaultsConfig` and `useHeroHookFormDefaults()` now support `defaults.autocomplete`. AutocompleteField spreads these defaults so global Autocomplete styling (color, size, variant, radius, labelPlacement) applies like Input, Textarea, and Select.
10
+ - **FieldArrayField: readOnly** – New `readOnly?: boolean` on `FieldArrayConfig`. When true, hides the add button, remove buttons, and reorder buttons so the field array is display-only.
11
+ - **StringArrayField: readOnly** – New `readOnly?: boolean` on `StringArrayFieldConfig`. When true, hides the input, add button, and remove buttons so the list is display-only.
12
+ - **AutocompleteFieldHandlers: store custom value from selection** – `createAutocompleteFieldHandlers` now allows `onSelectionChange` to return a value (string or number); that value is stored in the form field instead of the selected key (e.g. "store key, show label" by returning the display string).
13
+
14
+ ### Changed
15
+
16
+ - **AutocompleteField** – `AutocompleteItem` `textValue` now uses `item.label ?? String(item.value)` so options with labels display correctly.
17
+ - **StringArrayField** – Uses `useHeroHookFormDefaults()` for Input and Button; switched to HeroUI-style props (`onValueChange`, `onPress`, `isDisabled`). Enter key handling uses `onKeyDown` instead of deprecated `onKeyPress`.
18
+
19
+ ## [2.12.0] - 2026-01-28
20
+
21
+ ### Added
22
+
23
+ - **Custom option layout for autocomplete (renderItem)** – `FormFieldHelpers.autocomplete()` and the builder chain accept an optional 6th param `options?: { renderItem?: (item) => ReactNode }` so each option can show custom content (e.g. name + email + phone). Config supports `renderItem`; `FormField`, `ServerActionForm`, and `AdvancedFormBuilder` pass it through; `AutocompleteField` wraps custom children in `AutocompleteItem` so HeroUI receives valid listbox items. Use with static options or with `getOptions` for dynamic items + custom layout.
24
+ - **StringFieldConfig.renderItem** – Optional `renderItem?: (item: { label: string; value: string | number }) => ReactNode` for autocomplete fields.
25
+ - **Component tests** – Autocomplete: `renderItem` config and render (static and getOptions + renderItem).
26
+
27
+ ### Fixed
28
+
29
+ - **AutocompleteField custom children** – Custom `children` (renderItem) are now wrapped in `AutocompleteItem` before passing to HeroUI Autocomplete so the listbox receives valid items and the form renders correctly.
30
+
5
31
  ## [2.11.0] - 2026-01-28
6
32
 
7
33
  ### Added
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import React$1, { ComponentProps } from 'react';
1
+ import React$1, { ComponentProps, ReactNode } from 'react';
2
2
  import { Button } from '@heroui/react';
3
3
  import * as react_hook_form from 'react-hook-form';
4
4
  import { FieldValues, Path, RegisterOptions, ArrayPath, Control, UseFormReturn, FieldErrors, UseFormProps, SubmitHandler, DefaultValues, UseFormSetError, FieldPath, FieldArrayWithId } from 'react-hook-form';
@@ -90,6 +90,11 @@ interface StringFieldConfig<TFieldValues extends FieldValues> extends BaseFormFi
90
90
  label: string;
91
91
  value: string | number;
92
92
  }[];
93
+ /** Custom render for each autocomplete option (e.g. name + email + phone). When provided, used instead of default label-only. */
94
+ renderItem?: (item: {
95
+ label: string;
96
+ value: string | number;
97
+ }) => ReactNode;
93
98
  }
94
99
  interface BooleanFieldConfig<TFieldValues extends FieldValues> extends BaseFormFieldConfig<TFieldValues> {
95
100
  type: "checkbox" | "switch";
@@ -165,6 +170,8 @@ interface StringArrayFieldConfig<TFieldValues extends FieldValues> extends BaseF
165
170
  addButtonText?: string;
166
171
  /** Whether to show add button or use enter key */
167
172
  showAddButton?: boolean;
173
+ /** When true, only display items: no input, no add button, no remove buttons */
174
+ readOnly?: boolean;
168
175
  };
169
176
  }
170
177
  interface CustomFieldConfig<TFieldValues extends FieldValues> extends Omit<BaseFormFieldConfig<TFieldValues>, "name"> {
@@ -217,6 +224,8 @@ interface FieldArrayConfig<TFieldValues extends FieldValues> extends Omit<BaseFo
217
224
  };
218
225
  /** Function to create default item when adding new array item */
219
226
  defaultItem?: () => any;
227
+ /** When true, hide add button, remove buttons, and reorder buttons (display-only) */
228
+ readOnly?: boolean;
220
229
  /** Whether this field array should always be registered (for conditional rendering) */
221
230
  alwaysRegistered?: boolean;
222
231
  /** Custom render function for array items */
@@ -1761,7 +1770,7 @@ declare function useFormHelper<T extends FieldValues>({ defaultValues, methods,
1761
1770
  * @category Hooks
1762
1771
  */
1763
1772
  declare function useHeroForm<TFieldValues extends FieldValues>(): {
1764
- defaults: Required<Pick<HeroHookFormDefaultsConfig, "input" | "select" | "textarea" | "switch" | "radioGroup" | "checkbox" | "slider" | "dateInput" | "submitButton">>;
1773
+ defaults: Required<Pick<HeroHookFormDefaultsConfig, "input" | "select" | "textarea" | "switch" | "radioGroup" | "checkbox" | "slider" | "autocomplete" | "dateInput" | "submitButton">>;
1765
1774
  watch: react_hook_form.UseFormWatch<TFieldValues>;
1766
1775
  getValues: react_hook_form.UseFormGetValues<TFieldValues>;
1767
1776
  getFieldState: react_hook_form.UseFormGetFieldState<TFieldValues>;
@@ -1800,6 +1809,8 @@ type TextareaDefaults = Partial<Omit<TextareaProps, "value" | "onValueChange" |
1800
1809
  type CheckboxDefaults = Partial<Omit<React$1.ComponentProps<typeof Checkbox>, "isSelected" | "onValueChange" | "isInvalid" | "errorMessage" | "isDisabled">>;
1801
1810
  type RadioGroupDefaults = Partial<Omit<React$1.ComponentProps<typeof RadioGroup>, "value" | "onValueChange" | "label">>;
1802
1811
  type SelectDefaults = Partial<Omit<SelectProps, "selectedKeys" | "onSelectionChange" | "label" | "isInvalid" | "errorMessage" | "isDisabled">>;
1812
+ type AutocompleteProps = React$1.ComponentProps<typeof Autocomplete>;
1813
+ type AutocompleteDefaults = Partial<Omit<AutocompleteProps, "selectedKey" | "inputValue" | "label" | "isInvalid" | "errorMessage" | "isDisabled" | "items" | "defaultItems" | "children">>;
1803
1814
  type DateInputProps = React$1.ComponentProps<typeof DateInput>;
1804
1815
  type DateInputDefaults = Partial<Omit<DateInputProps, "value" | "onChange" | "label" | "isInvalid" | "errorMessage" | "isDisabled">>;
1805
1816
  type SliderDefaults = Partial<Omit<React$1.ComponentProps<typeof Slider>, "value" | "onValueChange" | "label" | "isInvalid" | "errorMessage" | "isDisabled">>;
@@ -1807,6 +1818,7 @@ type SwitchDefaults = Partial<Omit<React$1.ComponentProps<typeof Switch>, "isSel
1807
1818
  type ButtonDefaults = Partial<Omit<React$1.ComponentProps<typeof Button$1>, "type" | "isLoading">>;
1808
1819
  interface HeroHookFormDefaultsConfig {
1809
1820
  common?: CommonFieldDefaults;
1821
+ autocomplete?: AutocompleteDefaults;
1810
1822
  input?: InputDefaults;
1811
1823
  textarea?: TextareaDefaults;
1812
1824
  checkbox?: CheckboxDefaults;
@@ -1822,7 +1834,7 @@ interface HeroHookFormProviderProps {
1822
1834
  defaults?: HeroHookFormDefaultsConfig;
1823
1835
  }
1824
1836
  declare function HeroHookFormProvider(props: HeroHookFormProviderProps): React$1.JSX.Element;
1825
- declare function useHeroHookFormDefaults(): Required<Pick<HeroHookFormDefaultsConfig, "input" | "textarea" | "checkbox" | "radioGroup" | "select" | "dateInput" | "slider" | "switch" | "submitButton">>;
1837
+ declare function useHeroHookFormDefaults(): Required<Pick<HeroHookFormDefaultsConfig, "input" | "textarea" | "checkbox" | "radioGroup" | "select" | "autocomplete" | "dateInput" | "slider" | "switch" | "submitButton">>;
1826
1838
 
1827
1839
  interface FormProps<TFieldValues extends FieldValues> {
1828
1840
  methods: UseFormReturn<TFieldValues>;
@@ -2433,6 +2445,7 @@ declare class BasicFormBuilder<T extends FieldValues> {
2433
2445
  }[]): this;
2434
2446
  /**
2435
2447
  * Add an autocomplete field (static options array or dynamic getOptions getter).
2448
+ * Optional options.renderItem for custom option layout (e.g. name + email + phone).
2436
2449
  */
2437
2450
  autocomplete(name: Path<T>, label: string, items: {
2438
2451
  label: string;
@@ -2440,7 +2453,12 @@ declare class BasicFormBuilder<T extends FieldValues> {
2440
2453
  }[] | (() => {
2441
2454
  label: string;
2442
2455
  value: string | number;
2443
- }[]), placeholder?: string): this;
2456
+ }[]), placeholder?: string, options?: {
2457
+ renderItem?: (item: {
2458
+ label: string;
2459
+ value: string | number;
2460
+ }) => React$1.ReactNode;
2461
+ }): this;
2444
2462
  /**
2445
2463
  * Add a checkbox field
2446
2464
  */
@@ -2557,6 +2575,11 @@ declare const FormFieldHelpers: {
2557
2575
  * FormFieldHelpers.autocomplete("personId", "Person", () => people.map(p => ({ label: p.name, value: p.id })), "Search people", {
2558
2576
  * onInputChange: (q) => fetchPeople(q).then(setPeople),
2559
2577
  * })
2578
+ *
2579
+ * // Custom option layout (e.g. name + email + phone per option)
2580
+ * FormFieldHelpers.autocomplete("personId", "Person", getOptions, "Search", undefined, {
2581
+ * renderItem: (item) => <div><strong>{item.label}</strong><br /><small>{getSubtitle(item.value)}</small></div>,
2582
+ * })
2560
2583
  * ```
2561
2584
  */
2562
2585
  autocomplete: <T extends FieldValues>(name: Path<T>, label: string, items: {
@@ -2565,7 +2588,13 @@ declare const FormFieldHelpers: {
2565
2588
  }[] | (() => {
2566
2589
  label: string;
2567
2590
  value: string | number;
2568
- }[]), placeholder?: string, autocompleteProps?: AutocompletePassthroughProps) => ZodFormFieldConfig<T>;
2591
+ }[]), placeholder?: string, autocompleteProps?: AutocompletePassthroughProps, options?: {
2592
+ /** Custom render for each option (e.g. name + email + phone). When provided, used instead of default label-only. */
2593
+ renderItem?: (item: {
2594
+ label: string;
2595
+ value: string | number;
2596
+ }) => React$1.ReactNode;
2597
+ }) => ZodFormFieldConfig<T>;
2569
2598
  /**
2570
2599
  * Create a checkbox field
2571
2600
  *
@@ -3012,6 +3041,10 @@ type FieldCreationParams<T extends FieldValues> = {
3012
3041
  value: string | number;
3013
3042
  }[];
3014
3043
  props?: Record<string, unknown>;
3044
+ renderItem?: (item: {
3045
+ label: string;
3046
+ value: string | number;
3047
+ }) => React$1.ReactNode;
3015
3048
  } | {
3016
3049
  type: "checkbox";
3017
3050
  name: Path<T>;
@@ -4346,4 +4379,4 @@ declare const validationUtils: {
4346
4379
  }>;
4347
4380
  };
4348
4381
 
4349
- export { AdvancedFieldBuilder, type ArraySyncOptions, type ArraySyncResult, 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 };
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 };
package/dist/index.js CHANGED
@@ -88,9 +88,170 @@ import React19 from "react";
88
88
  import { get, useWatch as useWatch3 } from "react-hook-form";
89
89
 
90
90
  // src/fields/AutocompleteField.tsx
91
- import React from "react";
91
+ import React2 from "react";
92
92
  import { Controller } from "react-hook-form";
93
93
 
94
+ // src/providers/ConfigProvider.tsx
95
+ import React, { createContext, useContext, useMemo } from "react";
96
+ var DefaultsContext = createContext(null);
97
+ function HeroHookFormProvider(props) {
98
+ const value = useMemo(() => props.defaults ?? {}, [props.defaults]);
99
+ return /* @__PURE__ */ React.createElement(DefaultsContext.Provider, { value }, props.children);
100
+ }
101
+ function extractInputCommon(common) {
102
+ const result = {};
103
+ if (common.color !== void 0) {
104
+ const color = common.color;
105
+ result.color = color;
106
+ }
107
+ if (common.size !== void 0) {
108
+ const size = common.size;
109
+ result.size = size;
110
+ }
111
+ if (common.variant !== void 0) {
112
+ const variant = common.variant;
113
+ result.variant = variant;
114
+ }
115
+ if (common.radius !== void 0) {
116
+ const radius = common.radius;
117
+ result.radius = radius;
118
+ }
119
+ if (common.labelPlacement !== void 0) {
120
+ const labelPlacement = common.labelPlacement;
121
+ result.labelPlacement = labelPlacement;
122
+ }
123
+ return result;
124
+ }
125
+ function extractTextareaCommon(common) {
126
+ const result = {};
127
+ if (common.color !== void 0) {
128
+ const color = common.color;
129
+ result.color = color;
130
+ }
131
+ if (common.size !== void 0) {
132
+ const size = common.size;
133
+ result.size = size;
134
+ }
135
+ if (common.variant !== void 0) {
136
+ const variant = common.variant;
137
+ result.variant = variant;
138
+ }
139
+ if (common.radius !== void 0) {
140
+ const radius = common.radius;
141
+ result.radius = radius;
142
+ }
143
+ if (common.labelPlacement !== void 0) {
144
+ const labelPlacement = common.labelPlacement;
145
+ result.labelPlacement = labelPlacement;
146
+ }
147
+ return result;
148
+ }
149
+ function extractSelectCommon(common) {
150
+ const result = {};
151
+ if (common.color !== void 0) {
152
+ const color = common.color;
153
+ result.color = color;
154
+ }
155
+ if (common.size !== void 0) {
156
+ const size = common.size;
157
+ result.size = size;
158
+ }
159
+ if (common.variant !== void 0) {
160
+ const variant = common.variant;
161
+ result.variant = variant;
162
+ }
163
+ if (common.radius !== void 0) {
164
+ const radius = common.radius;
165
+ result.radius = radius;
166
+ }
167
+ if (common.labelPlacement !== void 0) {
168
+ const labelPlacement = common.labelPlacement;
169
+ result.labelPlacement = labelPlacement;
170
+ }
171
+ return result;
172
+ }
173
+ function extractAutocompleteCommon(common) {
174
+ const result = {};
175
+ if (common.color !== void 0) {
176
+ const color = common.color;
177
+ result.color = color;
178
+ }
179
+ if (common.size !== void 0) {
180
+ const size = common.size;
181
+ result.size = size;
182
+ }
183
+ if (common.variant !== void 0) {
184
+ const variant = common.variant;
185
+ result.variant = variant;
186
+ }
187
+ if (common.radius !== void 0) {
188
+ const radius = common.radius;
189
+ result.radius = radius;
190
+ }
191
+ if (common.labelPlacement !== void 0) {
192
+ const labelPlacement = common.labelPlacement;
193
+ result.labelPlacement = labelPlacement;
194
+ }
195
+ return result;
196
+ }
197
+ function extractDateInputCommon(common) {
198
+ const result = {};
199
+ if (common.color !== void 0) {
200
+ const color = common.color;
201
+ result.color = color;
202
+ }
203
+ if (common.size !== void 0) {
204
+ const size = common.size;
205
+ result.size = size;
206
+ }
207
+ if (common.variant !== void 0) {
208
+ const variant = common.variant;
209
+ result.variant = variant;
210
+ }
211
+ if (common.radius !== void 0) {
212
+ const radius = common.radius;
213
+ result.radius = radius;
214
+ }
215
+ if (common.labelPlacement !== void 0) {
216
+ const labelPlacement = common.labelPlacement;
217
+ result.labelPlacement = labelPlacement;
218
+ }
219
+ return result;
220
+ }
221
+ function useHeroHookFormDefaults() {
222
+ const cfg = useContext(DefaultsContext) ?? {};
223
+ const common = cfg.common ?? {};
224
+ const commonInput = extractInputCommon(common);
225
+ const commonTextarea = extractTextareaCommon(common);
226
+ const commonSelect = extractSelectCommon(common);
227
+ const commonAutocomplete = extractAutocompleteCommon(common);
228
+ const commonDateInput = extractDateInputCommon(common);
229
+ const dateInputBase = {
230
+ radius: "md",
231
+ size: "md",
232
+ variant: "bordered"
233
+ };
234
+ return {
235
+ autocomplete: {
236
+ ...commonAutocomplete,
237
+ ...cfg.autocomplete ?? {}
238
+ },
239
+ checkbox: cfg.checkbox ?? {},
240
+ dateInput: {
241
+ ...dateInputBase,
242
+ ...commonDateInput,
243
+ ...cfg.dateInput ?? {}
244
+ },
245
+ input: { ...commonInput, ...cfg.input ?? {} },
246
+ radioGroup: cfg.radioGroup ?? {},
247
+ select: { ...commonSelect, ...cfg.select ?? {} },
248
+ slider: cfg.slider ?? {},
249
+ submitButton: cfg.submitButton ?? {},
250
+ switch: cfg.switch ?? {},
251
+ textarea: { ...commonTextarea, ...cfg.textarea ?? {} }
252
+ };
253
+ }
254
+
94
255
  // src/utils/fieldHandlers.ts
95
256
  function createBlurHandler(userHandler, formHandler) {
96
257
  return (e) => {
@@ -235,10 +396,12 @@ function createAutocompleteFieldHandlers(props, field2, fieldLevel, options) {
235
396
  const allowsCustomValue = options?.allowsCustomValue ?? false;
236
397
  const handleSelectionChange = (key) => {
237
398
  const next = key ?? "";
238
- if (props?.onSelectionChange) {
239
- props.onSelectionChange(key);
399
+ const valueToStore = typeof props?.onSelectionChange === "function" ? props.onSelectionChange(key) : void 0;
400
+ if (valueToStore !== void 0) {
401
+ field2.onChange(valueToStore);
402
+ } else {
403
+ field2.onChange(next);
240
404
  }
241
- field2.onChange(next);
242
405
  };
243
406
  const handleInputChange = (value) => {
244
407
  const strValue = value != null ? String(value) : "";
@@ -305,7 +468,8 @@ function AutocompleteField(props) {
305
468
  placeholder,
306
469
  rules
307
470
  } = props;
308
- return /* @__PURE__ */ React.createElement(
471
+ const defaults = useHeroHookFormDefaults();
472
+ return /* @__PURE__ */ React2.createElement(
309
473
  Controller,
310
474
  {
311
475
  control,
@@ -322,9 +486,10 @@ function AutocompleteField(props) {
322
486
  { isDisabled, label },
323
487
  { allowsCustomValue }
324
488
  );
325
- return /* @__PURE__ */ React.createElement("div", { className }, /* @__PURE__ */ React.createElement(
489
+ return /* @__PURE__ */ React2.createElement("div", { className }, /* @__PURE__ */ React2.createElement(
326
490
  Autocomplete,
327
491
  {
492
+ ...defaults.autocomplete,
328
493
  ...rest,
329
494
  ...defaultItems && {
330
495
  defaultItems
@@ -338,11 +503,20 @@ function AutocompleteField(props) {
338
503
  inputValue: shouldShowInputValue ? field2.value ?? "" : void 0,
339
504
  items
340
505
  },
341
- children ? children : (item) => /* @__PURE__ */ React.createElement(
506
+ children ? (item) => /* @__PURE__ */ React2.createElement(
507
+ AutocompleteItem,
508
+ {
509
+ key: String(item.value),
510
+ textValue: item.label ?? String(item.value),
511
+ description: item.description,
512
+ isDisabled: item.disabled
513
+ },
514
+ children(item)
515
+ ) : (item) => /* @__PURE__ */ React2.createElement(
342
516
  AutocompleteItem,
343
517
  {
344
518
  key: String(item.value),
345
- textValue: String(item.value),
519
+ textValue: item.label ?? String(item.value),
346
520
  description: item.description,
347
521
  isDisabled: item.disabled
348
522
  },
@@ -358,140 +532,6 @@ function AutocompleteField(props) {
358
532
  // src/fields/CheckboxField.tsx
359
533
  import React3 from "react";
360
534
  import { Controller as Controller2 } from "react-hook-form";
361
-
362
- // src/providers/ConfigProvider.tsx
363
- import React2, { createContext, useContext, useMemo } from "react";
364
- var DefaultsContext = createContext(null);
365
- function HeroHookFormProvider(props) {
366
- const value = useMemo(() => props.defaults ?? {}, [props.defaults]);
367
- return /* @__PURE__ */ React2.createElement(DefaultsContext.Provider, { value }, props.children);
368
- }
369
- function extractInputCommon(common) {
370
- const result = {};
371
- if (common.color !== void 0) {
372
- const color = common.color;
373
- result.color = color;
374
- }
375
- if (common.size !== void 0) {
376
- const size = common.size;
377
- result.size = size;
378
- }
379
- if (common.variant !== void 0) {
380
- const variant = common.variant;
381
- result.variant = variant;
382
- }
383
- if (common.radius !== void 0) {
384
- const radius = common.radius;
385
- result.radius = radius;
386
- }
387
- if (common.labelPlacement !== void 0) {
388
- const labelPlacement = common.labelPlacement;
389
- result.labelPlacement = labelPlacement;
390
- }
391
- return result;
392
- }
393
- function extractTextareaCommon(common) {
394
- const result = {};
395
- if (common.color !== void 0) {
396
- const color = common.color;
397
- result.color = color;
398
- }
399
- if (common.size !== void 0) {
400
- const size = common.size;
401
- result.size = size;
402
- }
403
- if (common.variant !== void 0) {
404
- const variant = common.variant;
405
- result.variant = variant;
406
- }
407
- if (common.radius !== void 0) {
408
- const radius = common.radius;
409
- result.radius = radius;
410
- }
411
- if (common.labelPlacement !== void 0) {
412
- const labelPlacement = common.labelPlacement;
413
- result.labelPlacement = labelPlacement;
414
- }
415
- return result;
416
- }
417
- function extractSelectCommon(common) {
418
- const result = {};
419
- if (common.color !== void 0) {
420
- const color = common.color;
421
- result.color = color;
422
- }
423
- if (common.size !== void 0) {
424
- const size = common.size;
425
- result.size = size;
426
- }
427
- if (common.variant !== void 0) {
428
- const variant = common.variant;
429
- result.variant = variant;
430
- }
431
- if (common.radius !== void 0) {
432
- const radius = common.radius;
433
- result.radius = radius;
434
- }
435
- if (common.labelPlacement !== void 0) {
436
- const labelPlacement = common.labelPlacement;
437
- result.labelPlacement = labelPlacement;
438
- }
439
- return result;
440
- }
441
- function extractDateInputCommon(common) {
442
- const result = {};
443
- if (common.color !== void 0) {
444
- const color = common.color;
445
- result.color = color;
446
- }
447
- if (common.size !== void 0) {
448
- const size = common.size;
449
- result.size = size;
450
- }
451
- if (common.variant !== void 0) {
452
- const variant = common.variant;
453
- result.variant = variant;
454
- }
455
- if (common.radius !== void 0) {
456
- const radius = common.radius;
457
- result.radius = radius;
458
- }
459
- if (common.labelPlacement !== void 0) {
460
- const labelPlacement = common.labelPlacement;
461
- result.labelPlacement = labelPlacement;
462
- }
463
- return result;
464
- }
465
- function useHeroHookFormDefaults() {
466
- const cfg = useContext(DefaultsContext) ?? {};
467
- const common = cfg.common ?? {};
468
- const commonInput = extractInputCommon(common);
469
- const commonTextarea = extractTextareaCommon(common);
470
- const commonSelect = extractSelectCommon(common);
471
- const commonDateInput = extractDateInputCommon(common);
472
- const dateInputBase = {
473
- radius: "md",
474
- size: "md",
475
- variant: "bordered"
476
- };
477
- return {
478
- checkbox: cfg.checkbox ?? {},
479
- dateInput: {
480
- ...dateInputBase,
481
- ...commonDateInput,
482
- ...cfg.dateInput ?? {}
483
- },
484
- input: { ...commonInput, ...cfg.input ?? {} },
485
- radioGroup: cfg.radioGroup ?? {},
486
- select: { ...commonSelect, ...cfg.select ?? {} },
487
- slider: cfg.slider ?? {},
488
- submitButton: cfg.submitButton ?? {},
489
- switch: cfg.switch ?? {},
490
- textarea: { ...commonTextarea, ...cfg.textarea ?? {} }
491
- };
492
- }
493
-
494
- // src/fields/CheckboxField.tsx
495
535
  function CheckboxField(props) {
496
536
  const {
497
537
  checkboxProps,
@@ -1446,6 +1486,7 @@ function FieldArrayField({
1446
1486
  max = 10,
1447
1487
  min = 0,
1448
1488
  name,
1489
+ readOnly = false,
1449
1490
  removeButtonText = "Remove",
1450
1491
  renderAddButton,
1451
1492
  renderItem,
@@ -1562,7 +1603,7 @@ function FieldArrayField({
1562
1603
  key: field2.id,
1563
1604
  className: "border border-gray-200 rounded-lg p-4 space-y-4"
1564
1605
  },
1565
- /* @__PURE__ */ React9.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React9.createElement("h4", { className: "text-sm font-medium text-gray-700" }, config.label, " #", index + 1), /* @__PURE__ */ React9.createElement("div", { className: "flex gap-2" }, enableReordering && /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(
1606
+ /* @__PURE__ */ React9.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React9.createElement("h4", { className: "text-sm font-medium text-gray-700" }, config.label, " #", index + 1), !readOnly && /* @__PURE__ */ React9.createElement("div", { className: "flex gap-2" }, enableReordering && /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(
1566
1607
  Button2,
1567
1608
  {
1568
1609
  size: "sm",
@@ -1599,6 +1640,9 @@ function FieldArrayField({
1599
1640
  });
1600
1641
  };
1601
1642
  const renderAddButtonElement = () => {
1643
+ if (readOnly) {
1644
+ return null;
1645
+ }
1602
1646
  if (renderAddButton) {
1603
1647
  return renderAddButton({
1604
1648
  canAdd,
@@ -2067,10 +2111,12 @@ function StringArrayInput(props) {
2067
2111
  maxItems,
2068
2112
  minItems,
2069
2113
  placeholder = "Add item...",
2114
+ readOnly = false,
2070
2115
  showAddButton = true,
2071
2116
  transformItem = (item) => item.trim(),
2072
2117
  validateItem = () => true
2073
2118
  } = stringArrayProps || {};
2119
+ const defaults = useHeroHookFormDefaults();
2074
2120
  const canAddMore = !maxItems || items.length < maxItems;
2075
2121
  const handleAddItem = () => {
2076
2122
  const trimmedValue = inputValue.trim();
@@ -2087,33 +2133,32 @@ function StringArrayInput(props) {
2087
2133
  const newItems = items.filter((_, index) => index !== indexToRemove);
2088
2134
  field2.onChange(newItems);
2089
2135
  };
2090
- const handleKeyPress = (e) => {
2091
- if (e.key === "Enter") {
2092
- e.preventDefault();
2093
- if (!showAddButton) {
2094
- handleAddItem();
2095
- }
2096
- }
2097
- };
2098
- return /* @__PURE__ */ React16.createElement("div", { className: "space-y-2" }, label && /* @__PURE__ */ React16.createElement("label", { className: "block text-sm font-medium text-foreground" }, label, minItems !== void 0 && /* @__PURE__ */ React16.createElement("span", { className: "text-xs text-muted-foreground ml-1" }, "(min ", minItems, ")"), maxItems !== void 0 && /* @__PURE__ */ React16.createElement("span", { className: "text-xs text-muted-foreground ml-1" }, "(max ", maxItems, ")")), description && /* @__PURE__ */ React16.createElement("p", { className: "text-sm text-muted-foreground" }, description), canAddMore && /* @__PURE__ */ React16.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React16.createElement(
2136
+ return /* @__PURE__ */ React16.createElement("div", { className: "space-y-2" }, label && /* @__PURE__ */ React16.createElement("label", { className: "block text-sm font-medium text-foreground" }, label, minItems !== void 0 && /* @__PURE__ */ React16.createElement("span", { className: "text-xs text-muted-foreground ml-1" }, "(min ", minItems, ")"), maxItems !== void 0 && /* @__PURE__ */ React16.createElement("span", { className: "text-xs text-muted-foreground ml-1" }, "(max ", maxItems, ")")), description && /* @__PURE__ */ React16.createElement("p", { className: "text-sm text-muted-foreground" }, description), !readOnly && canAddMore && /* @__PURE__ */ React16.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React16.createElement(
2099
2137
  Input,
2100
2138
  {
2139
+ ...defaults.input,
2101
2140
  className: "flex-1",
2102
- disabled,
2141
+ isInvalid: !!errorMessage,
2142
+ errorMessage,
2143
+ isDisabled: disabled,
2103
2144
  placeholder,
2104
2145
  value: inputValue,
2105
- onChange: (e) => setInputValue(e.target.value),
2106
- onKeyPress: handleKeyPress,
2107
- isInvalid: !!errorMessage,
2108
- errorMessage
2146
+ onValueChange: setInputValue,
2147
+ onKeyDown: (e) => {
2148
+ if (e.key === "Enter") {
2149
+ e.preventDefault();
2150
+ if (!showAddButton) handleAddItem();
2151
+ }
2152
+ }
2109
2153
  }
2110
2154
  ), showAddButton && /* @__PURE__ */ React16.createElement(
2111
2155
  Button,
2112
2156
  {
2157
+ ...defaults.submitButton,
2113
2158
  type: "button",
2114
2159
  variant: "flat",
2115
- onClick: handleAddItem,
2116
- disabled: disabled || !inputValue.trim(),
2160
+ onPress: handleAddItem,
2161
+ isDisabled: disabled || !inputValue.trim(),
2117
2162
  size: "sm"
2118
2163
  },
2119
2164
  addButtonText
@@ -2124,14 +2169,15 @@ function StringArrayInput(props) {
2124
2169
  className: "flex items-center gap-2 p-2 bg-content2 rounded-md"
2125
2170
  },
2126
2171
  /* @__PURE__ */ React16.createElement("span", { className: "flex-1 text-sm" }, item),
2127
- /* @__PURE__ */ React16.createElement(
2172
+ !readOnly && /* @__PURE__ */ React16.createElement(
2128
2173
  Button,
2129
2174
  {
2175
+ ...defaults.submitButton,
2130
2176
  type: "button",
2131
2177
  variant: "light",
2132
2178
  size: "sm",
2133
- onClick: () => handleRemoveItem(index),
2134
- disabled,
2179
+ onPress: () => handleRemoveItem(index),
2180
+ isDisabled: disabled,
2135
2181
  className: "min-w-[60px]"
2136
2182
  },
2137
2183
  "Remove"
@@ -2336,6 +2382,7 @@ function FormFieldComponent({
2336
2382
  }
2337
2383
  case "autocomplete": {
2338
2384
  const autocompleteOptions = "getOptions" in fieldConfig && typeof fieldConfig.getOptions === "function" ? fieldConfig.getOptions() : "options" in fieldConfig && fieldConfig.options ? fieldConfig.options : [];
2385
+ const autocompleteRenderItem = "renderItem" in fieldConfig && typeof fieldConfig.renderItem === "function" ? (item) => /* @__PURE__ */ React19.createElement(React19.Fragment, null, fieldConfig.renderItem(item)) : void 0;
2339
2386
  return /* @__PURE__ */ React19.createElement(
2340
2387
  AutocompleteField,
2341
2388
  {
@@ -2347,7 +2394,8 @@ function FormFieldComponent({
2347
2394
  label: opt.label,
2348
2395
  value: String(opt.value)
2349
2396
  })),
2350
- autocompleteProps: "autocompleteProps" in fieldConfig ? fieldConfig.autocompleteProps : void 0
2397
+ autocompleteProps: "autocompleteProps" in fieldConfig ? fieldConfig.autocompleteProps : void 0,
2398
+ children: autocompleteRenderItem
2351
2399
  }
2352
2400
  );
2353
2401
  }
@@ -3145,7 +3193,7 @@ function ServerActionField({
3145
3193
  onInputChange: setValue,
3146
3194
  items
3147
3195
  },
3148
- items.map((item) => /* @__PURE__ */ React21.createElement(AutocompleteItem, { key: String(item.value) }, item.label))
3196
+ items.map((item) => /* @__PURE__ */ React21.createElement(AutocompleteItem, { key: String(item.value) }, typeof stringConfig.renderItem === "function" ? stringConfig.renderItem(item) : item.label))
3149
3197
  );
3150
3198
  }
3151
3199
  case "slider": {
@@ -4051,14 +4099,16 @@ var BasicFormBuilder = class {
4051
4099
  }
4052
4100
  /**
4053
4101
  * Add an autocomplete field (static options array or dynamic getOptions getter).
4102
+ * Optional options.renderItem for custom option layout (e.g. name + email + phone).
4054
4103
  */
4055
- autocomplete(name, label, items, placeholder) {
4104
+ autocomplete(name, label, items, placeholder, options) {
4056
4105
  const isGetter = typeof items === "function";
4057
4106
  this.fields.push({
4058
4107
  autocompleteProps: placeholder ? { placeholder } : void 0,
4059
4108
  label,
4060
4109
  name,
4061
4110
  ...isGetter ? { getOptions: items } : { options: items },
4111
+ ...options?.renderItem && { renderItem: options.renderItem },
4062
4112
  type: "autocomplete"
4063
4113
  });
4064
4114
  return this;
@@ -4151,9 +4201,14 @@ var FormFieldHelpers = {
4151
4201
  * FormFieldHelpers.autocomplete("personId", "Person", () => people.map(p => ({ label: p.name, value: p.id })), "Search people", {
4152
4202
  * onInputChange: (q) => fetchPeople(q).then(setPeople),
4153
4203
  * })
4204
+ *
4205
+ * // Custom option layout (e.g. name + email + phone per option)
4206
+ * FormFieldHelpers.autocomplete("personId", "Person", getOptions, "Search", undefined, {
4207
+ * renderItem: (item) => <div><strong>{item.label}</strong><br /><small>{getSubtitle(item.value)}</small></div>,
4208
+ * })
4154
4209
  * ```
4155
4210
  */
4156
- autocomplete: (name, label, items, placeholder, autocompleteProps) => {
4211
+ autocomplete: (name, label, items, placeholder, autocompleteProps, options) => {
4157
4212
  const isGetter = typeof items === "function";
4158
4213
  return {
4159
4214
  autocompleteProps: {
@@ -4161,6 +4216,7 @@ var FormFieldHelpers = {
4161
4216
  ...autocompleteProps
4162
4217
  },
4163
4218
  ...isGetter ? { getOptions: items } : { options: items },
4219
+ ...options?.renderItem && { renderItem: options.renderItem },
4164
4220
  label,
4165
4221
  name,
4166
4222
  type: "autocomplete"
@@ -4838,6 +4894,7 @@ function createFieldFromParams(params) {
4838
4894
  label: params.label,
4839
4895
  name: params.name,
4840
4896
  ...typeof params.getOptions === "function" ? { getOptions: params.getOptions } : { options: params.options },
4897
+ ...params.renderItem && { renderItem: params.renderItem },
4841
4898
  type: "autocomplete"
4842
4899
  };
4843
4900
  case "content":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rachelallyson/hero-hook-form",
3
- "version": "2.11.0",
3
+ "version": "2.13.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/",