@rachelallyson/hero-hook-form 2.10.0 → 2.12.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 +24 -0
- package/dist/index.d.ts +60 -15
- package/dist/index.js +56 -29
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [2.12.0] - 2026-01-28
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **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.
|
|
10
|
+
- **StringFieldConfig.renderItem** – Optional `renderItem?: (item: { label: string; value: string | number }) => ReactNode` for autocomplete fields.
|
|
11
|
+
- **Component tests** – Autocomplete: `renderItem` config and render (static and getOptions + renderItem).
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **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.
|
|
16
|
+
|
|
17
|
+
## [2.11.0] - 2026-01-28
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **Dynamic options for autocomplete** – `FormFieldHelpers.autocomplete()` and the builder chain now accept either a static options array or a getter function `() => options`. Use a getter for API-driven autocomplete (e.g. PCO Person, search-as-you-type): the getter is called each render so items stay in sync with state. Config supports `getOptions`; `FormField`, `ServerActionForm`, and `AdvancedFormBuilder` resolve items from `getOptions()` when present, else `options`. No need for `FormFieldHelpers.custom` when you only need dynamic items.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- **FormFieldHelpers.autocomplete** – Third parameter can be `options[]` or `() => options[]`; JSDoc documents dynamic usage with `onInputChange`.
|
|
26
|
+
- **StringFieldConfig** – Added optional `getOptions?: () => { label: string; value: string | number }[]` for autocomplete fields.
|
|
27
|
+
- **AutocompleteField JSDoc** – Notes dynamic options via getter + `onInputChange`.
|
|
28
|
+
|
|
5
29
|
## [2.10.0] - 2026-01-28
|
|
6
30
|
|
|
7
31
|
### 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';
|
|
@@ -80,10 +80,21 @@ interface StringFieldConfig<TFieldValues extends FieldValues> extends BaseFormFi
|
|
|
80
80
|
textareaProps?: TextareaPassthroughProps;
|
|
81
81
|
selectProps?: SelectPassthroughProps;
|
|
82
82
|
autocompleteProps?: AutocompletePassthroughProps;
|
|
83
|
+
/** Static options for autocomplete/select. Omit when using getOptions for dynamic items. */
|
|
83
84
|
options?: {
|
|
84
85
|
label: string;
|
|
85
86
|
value: string | number;
|
|
86
87
|
}[];
|
|
88
|
+
/** Dynamic options for autocomplete: called each render to get current items (e.g. from API/state). */
|
|
89
|
+
getOptions?: () => {
|
|
90
|
+
label: string;
|
|
91
|
+
value: string | number;
|
|
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;
|
|
87
98
|
}
|
|
88
99
|
interface BooleanFieldConfig<TFieldValues extends FieldValues> extends BaseFormFieldConfig<TFieldValues> {
|
|
89
100
|
type: "checkbox" | "switch";
|
|
@@ -635,7 +646,9 @@ type AutocompleteFieldProps<TFieldValues extends FieldValues, TValue extends str
|
|
|
635
646
|
*
|
|
636
647
|
* This component provides a type-safe autocomplete field with validation support,
|
|
637
648
|
* error handling, and accessibility features. It supports both static option lists
|
|
638
|
-
* and async loading via the items prop or children render function.
|
|
649
|
+
* and async loading via the items prop or children render function. For dynamic
|
|
650
|
+
* options (e.g. API search), use FormFieldHelpers.autocomplete with a getter:
|
|
651
|
+
* () => people.map(p => ({ label: p.name, value: p.id })) and onInputChange to fetch.
|
|
639
652
|
*
|
|
640
653
|
* @template TFieldValues - The form data type
|
|
641
654
|
* @template TValue - The value type for the autocomplete field (string or number)
|
|
@@ -2424,12 +2437,21 @@ declare class BasicFormBuilder<T extends FieldValues> {
|
|
|
2424
2437
|
value: string | number;
|
|
2425
2438
|
}[]): this;
|
|
2426
2439
|
/**
|
|
2427
|
-
* Add an autocomplete field
|
|
2440
|
+
* Add an autocomplete field (static options array or dynamic getOptions getter).
|
|
2441
|
+
* Optional options.renderItem for custom option layout (e.g. name + email + phone).
|
|
2428
2442
|
*/
|
|
2429
2443
|
autocomplete(name: Path<T>, label: string, items: {
|
|
2430
2444
|
label: string;
|
|
2431
2445
|
value: string | number;
|
|
2432
|
-
}[]
|
|
2446
|
+
}[] | (() => {
|
|
2447
|
+
label: string;
|
|
2448
|
+
value: string | number;
|
|
2449
|
+
}[]), placeholder?: string, options?: {
|
|
2450
|
+
renderItem?: (item: {
|
|
2451
|
+
label: string;
|
|
2452
|
+
value: string | number;
|
|
2453
|
+
}) => React$1.ReactNode;
|
|
2454
|
+
}): this;
|
|
2433
2455
|
/**
|
|
2434
2456
|
* Add a checkbox field
|
|
2435
2457
|
*/
|
|
@@ -2530,27 +2552,42 @@ declare function inputHelper<T extends FieldValues>(name: Path<T>, label: string
|
|
|
2530
2552
|
declare function inputHelper<T extends FieldValues>(name: Path<T>, label: string, type: "text" | "email" | "tel" | "password", inputProps: InputPassthroughProps): ZodFormFieldConfig<T>;
|
|
2531
2553
|
declare const FormFieldHelpers: {
|
|
2532
2554
|
/**
|
|
2533
|
-
* Create an autocomplete field
|
|
2555
|
+
* Create an autocomplete field with static or dynamic options.
|
|
2556
|
+
*
|
|
2557
|
+
* Pass an array for a fixed list, or a getter function for dynamic/API-driven options
|
|
2558
|
+
* (e.g. search-as-you-type). The getter is called each render so it sees current state;
|
|
2559
|
+
* use with autocompleteProps.onInputChange to fetch options when the user types.
|
|
2534
2560
|
*
|
|
2535
2561
|
* @example
|
|
2536
2562
|
* ```tsx
|
|
2537
|
-
* //
|
|
2538
|
-
* FormFieldHelpers.autocomplete("country", "Country", options)
|
|
2563
|
+
* // Static
|
|
2564
|
+
* FormFieldHelpers.autocomplete("country", "Country", options, "Search countries", { allowsCustomValue: true })
|
|
2539
2565
|
*
|
|
2540
|
-
* //
|
|
2541
|
-
*
|
|
2566
|
+
* // Dynamic (e.g. PCO Person)
|
|
2567
|
+
* const [people, setPeople] = useState([]);
|
|
2568
|
+
* FormFieldHelpers.autocomplete("personId", "Person", () => people.map(p => ({ label: p.name, value: p.id })), "Search people", {
|
|
2569
|
+
* onInputChange: (q) => fetchPeople(q).then(setPeople),
|
|
2570
|
+
* })
|
|
2542
2571
|
*
|
|
2543
|
-
* //
|
|
2544
|
-
* FormFieldHelpers.autocomplete("
|
|
2545
|
-
*
|
|
2546
|
-
* allowsCustomValue: true
|
|
2572
|
+
* // Custom option layout (e.g. name + email + phone per option)
|
|
2573
|
+
* FormFieldHelpers.autocomplete("personId", "Person", getOptions, "Search", undefined, {
|
|
2574
|
+
* renderItem: (item) => <div><strong>{item.label}</strong><br /><small>{getSubtitle(item.value)}</small></div>,
|
|
2547
2575
|
* })
|
|
2548
2576
|
* ```
|
|
2549
2577
|
*/
|
|
2550
2578
|
autocomplete: <T extends FieldValues>(name: Path<T>, label: string, items: {
|
|
2551
2579
|
label: string;
|
|
2552
2580
|
value: string | number;
|
|
2553
|
-
}[]
|
|
2581
|
+
}[] | (() => {
|
|
2582
|
+
label: string;
|
|
2583
|
+
value: string | number;
|
|
2584
|
+
}[]), placeholder?: string, autocompleteProps?: AutocompletePassthroughProps, options?: {
|
|
2585
|
+
/** Custom render for each option (e.g. name + email + phone). When provided, used instead of default label-only. */
|
|
2586
|
+
renderItem?: (item: {
|
|
2587
|
+
label: string;
|
|
2588
|
+
value: string | number;
|
|
2589
|
+
}) => React$1.ReactNode;
|
|
2590
|
+
}) => ZodFormFieldConfig<T>;
|
|
2554
2591
|
/**
|
|
2555
2592
|
* Create a checkbox field
|
|
2556
2593
|
*
|
|
@@ -2988,11 +3025,19 @@ type FieldCreationParams<T extends FieldValues> = {
|
|
|
2988
3025
|
type: "autocomplete";
|
|
2989
3026
|
name: Path<T>;
|
|
2990
3027
|
label: string;
|
|
2991
|
-
options
|
|
3028
|
+
options?: {
|
|
3029
|
+
label: string;
|
|
3030
|
+
value: string | number;
|
|
3031
|
+
}[];
|
|
3032
|
+
getOptions?: () => {
|
|
2992
3033
|
label: string;
|
|
2993
3034
|
value: string | number;
|
|
2994
3035
|
}[];
|
|
2995
3036
|
props?: Record<string, unknown>;
|
|
3037
|
+
renderItem?: (item: {
|
|
3038
|
+
label: string;
|
|
3039
|
+
value: string | number;
|
|
3040
|
+
}) => React$1.ReactNode;
|
|
2996
3041
|
} | {
|
|
2997
3042
|
type: "checkbox";
|
|
2998
3043
|
name: Path<T>;
|
package/dist/index.js
CHANGED
|
@@ -338,7 +338,16 @@ function AutocompleteField(props) {
|
|
|
338
338
|
inputValue: shouldShowInputValue ? field2.value ?? "" : void 0,
|
|
339
339
|
items
|
|
340
340
|
},
|
|
341
|
-
children ?
|
|
341
|
+
children ? (item) => /* @__PURE__ */ React.createElement(
|
|
342
|
+
AutocompleteItem,
|
|
343
|
+
{
|
|
344
|
+
key: String(item.value),
|
|
345
|
+
textValue: String(item.value),
|
|
346
|
+
description: item.description,
|
|
347
|
+
isDisabled: item.disabled
|
|
348
|
+
},
|
|
349
|
+
children(item)
|
|
350
|
+
) : (item) => /* @__PURE__ */ React.createElement(
|
|
342
351
|
AutocompleteItem,
|
|
343
352
|
{
|
|
344
353
|
key: String(item.value),
|
|
@@ -2335,6 +2344,8 @@ function FormFieldComponent({
|
|
|
2335
2344
|
);
|
|
2336
2345
|
}
|
|
2337
2346
|
case "autocomplete": {
|
|
2347
|
+
const autocompleteOptions = "getOptions" in fieldConfig && typeof fieldConfig.getOptions === "function" ? fieldConfig.getOptions() : "options" in fieldConfig && fieldConfig.options ? fieldConfig.options : [];
|
|
2348
|
+
const autocompleteRenderItem = "renderItem" in fieldConfig && typeof fieldConfig.renderItem === "function" ? (item) => /* @__PURE__ */ React19.createElement(React19.Fragment, null, fieldConfig.renderItem(item)) : void 0;
|
|
2338
2349
|
return /* @__PURE__ */ React19.createElement(
|
|
2339
2350
|
AutocompleteField,
|
|
2340
2351
|
{
|
|
@@ -2342,11 +2353,12 @@ function FormFieldComponent({
|
|
|
2342
2353
|
name: fieldConfig.name,
|
|
2343
2354
|
control,
|
|
2344
2355
|
defaultValue: "defaultValue" in fieldConfig ? fieldConfig.defaultValue : void 0,
|
|
2345
|
-
items:
|
|
2356
|
+
items: autocompleteOptions.map((opt) => ({
|
|
2346
2357
|
label: opt.label,
|
|
2347
2358
|
value: String(opt.value)
|
|
2348
2359
|
})),
|
|
2349
|
-
autocompleteProps: "autocompleteProps" in fieldConfig ? fieldConfig.autocompleteProps : void 0
|
|
2360
|
+
autocompleteProps: "autocompleteProps" in fieldConfig ? fieldConfig.autocompleteProps : void 0,
|
|
2361
|
+
children: autocompleteRenderItem
|
|
2350
2362
|
}
|
|
2351
2363
|
);
|
|
2352
2364
|
}
|
|
@@ -3120,10 +3132,11 @@ function ServerActionField({
|
|
|
3120
3132
|
}
|
|
3121
3133
|
case "autocomplete": {
|
|
3122
3134
|
const stringConfig = fieldConfig;
|
|
3123
|
-
const
|
|
3135
|
+
const rawItems = typeof stringConfig.getOptions === "function" ? stringConfig.getOptions() : stringConfig.options ?? [];
|
|
3136
|
+
const items = rawItems.map((opt) => ({
|
|
3124
3137
|
label: opt.label,
|
|
3125
3138
|
value: String(opt.value)
|
|
3126
|
-
}))
|
|
3139
|
+
}));
|
|
3127
3140
|
return /* @__PURE__ */ React21.createElement(
|
|
3128
3141
|
Autocomplete,
|
|
3129
3142
|
{
|
|
@@ -3143,7 +3156,7 @@ function ServerActionField({
|
|
|
3143
3156
|
onInputChange: setValue,
|
|
3144
3157
|
items
|
|
3145
3158
|
},
|
|
3146
|
-
items.map((item) => /* @__PURE__ */ React21.createElement(AutocompleteItem, { key: String(item.value) }, item.label))
|
|
3159
|
+
items.map((item) => /* @__PURE__ */ React21.createElement(AutocompleteItem, { key: String(item.value) }, typeof stringConfig.renderItem === "function" ? stringConfig.renderItem(item) : item.label))
|
|
3147
3160
|
);
|
|
3148
3161
|
}
|
|
3149
3162
|
case "slider": {
|
|
@@ -4048,14 +4061,17 @@ var BasicFormBuilder = class {
|
|
|
4048
4061
|
return this;
|
|
4049
4062
|
}
|
|
4050
4063
|
/**
|
|
4051
|
-
* Add an autocomplete field
|
|
4064
|
+
* Add an autocomplete field (static options array or dynamic getOptions getter).
|
|
4065
|
+
* Optional options.renderItem for custom option layout (e.g. name + email + phone).
|
|
4052
4066
|
*/
|
|
4053
|
-
autocomplete(name, label, items, placeholder) {
|
|
4067
|
+
autocomplete(name, label, items, placeholder, options) {
|
|
4068
|
+
const isGetter = typeof items === "function";
|
|
4054
4069
|
this.fields.push({
|
|
4055
4070
|
autocompleteProps: placeholder ? { placeholder } : void 0,
|
|
4056
4071
|
label,
|
|
4057
4072
|
name,
|
|
4058
|
-
options: items,
|
|
4073
|
+
...isGetter ? { getOptions: items } : { options: items },
|
|
4074
|
+
...options?.renderItem && { renderItem: options.renderItem },
|
|
4059
4075
|
type: "autocomplete"
|
|
4060
4076
|
});
|
|
4061
4077
|
return this;
|
|
@@ -4132,33 +4148,43 @@ function inputHelper(name, label, typeOrProps, inputProps) {
|
|
|
4132
4148
|
}
|
|
4133
4149
|
var FormFieldHelpers = {
|
|
4134
4150
|
/**
|
|
4135
|
-
* Create an autocomplete field
|
|
4151
|
+
* Create an autocomplete field with static or dynamic options.
|
|
4152
|
+
*
|
|
4153
|
+
* Pass an array for a fixed list, or a getter function for dynamic/API-driven options
|
|
4154
|
+
* (e.g. search-as-you-type). The getter is called each render so it sees current state;
|
|
4155
|
+
* use with autocompleteProps.onInputChange to fetch options when the user types.
|
|
4136
4156
|
*
|
|
4137
4157
|
* @example
|
|
4138
4158
|
* ```tsx
|
|
4139
|
-
* //
|
|
4140
|
-
* FormFieldHelpers.autocomplete("country", "Country", options)
|
|
4159
|
+
* // Static
|
|
4160
|
+
* FormFieldHelpers.autocomplete("country", "Country", options, "Search countries", { allowsCustomValue: true })
|
|
4141
4161
|
*
|
|
4142
|
-
* //
|
|
4143
|
-
*
|
|
4162
|
+
* // Dynamic (e.g. PCO Person)
|
|
4163
|
+
* const [people, setPeople] = useState([]);
|
|
4164
|
+
* FormFieldHelpers.autocomplete("personId", "Person", () => people.map(p => ({ label: p.name, value: p.id })), "Search people", {
|
|
4165
|
+
* onInputChange: (q) => fetchPeople(q).then(setPeople),
|
|
4166
|
+
* })
|
|
4144
4167
|
*
|
|
4145
|
-
* //
|
|
4146
|
-
* FormFieldHelpers.autocomplete("
|
|
4147
|
-
*
|
|
4148
|
-
* allowsCustomValue: true
|
|
4168
|
+
* // Custom option layout (e.g. name + email + phone per option)
|
|
4169
|
+
* FormFieldHelpers.autocomplete("personId", "Person", getOptions, "Search", undefined, {
|
|
4170
|
+
* renderItem: (item) => <div><strong>{item.label}</strong><br /><small>{getSubtitle(item.value)}</small></div>,
|
|
4149
4171
|
* })
|
|
4150
4172
|
* ```
|
|
4151
4173
|
*/
|
|
4152
|
-
autocomplete: (name, label, items, placeholder, autocompleteProps) =>
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4174
|
+
autocomplete: (name, label, items, placeholder, autocompleteProps, options) => {
|
|
4175
|
+
const isGetter = typeof items === "function";
|
|
4176
|
+
return {
|
|
4177
|
+
autocompleteProps: {
|
|
4178
|
+
...placeholder && { placeholder },
|
|
4179
|
+
...autocompleteProps
|
|
4180
|
+
},
|
|
4181
|
+
...isGetter ? { getOptions: items } : { options: items },
|
|
4182
|
+
...options?.renderItem && { renderItem: options.renderItem },
|
|
4183
|
+
label,
|
|
4184
|
+
name,
|
|
4185
|
+
type: "autocomplete"
|
|
4186
|
+
};
|
|
4187
|
+
},
|
|
4162
4188
|
/**
|
|
4163
4189
|
* Create a checkbox field
|
|
4164
4190
|
*
|
|
@@ -4830,7 +4856,8 @@ function createFieldFromParams(params) {
|
|
|
4830
4856
|
autocompleteProps: params.props,
|
|
4831
4857
|
label: params.label,
|
|
4832
4858
|
name: params.name,
|
|
4833
|
-
options: params.options,
|
|
4859
|
+
...typeof params.getOptions === "function" ? { getOptions: params.getOptions } : { options: params.options },
|
|
4860
|
+
...params.renderItem && { renderItem: params.renderItem },
|
|
4834
4861
|
type: "autocomplete"
|
|
4835
4862
|
};
|
|
4836
4863
|
case "content":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rachelallyson/hero-hook-form",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.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/",
|