@rachelallyson/hero-hook-form 2.6.0 → 2.7.2

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.
@@ -1,3989 +0,0 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
- // src/components/Form.tsx
9
- import React18 from "react";
10
- import { Button as Button3 } from "@heroui/react";
11
-
12
- // src/hooks/useFormHelper.ts
13
- import { useState } from "react";
14
- import { useForm } from "react-hook-form";
15
- function useFormHelper({
16
- defaultValues,
17
- methods,
18
- onError,
19
- onSubmit,
20
- onSuccess
21
- }) {
22
- const [submissionState, setSubmissionState] = useState({
23
- isSubmitted: false,
24
- isSubmitting: false,
25
- isSuccess: false
26
- });
27
- const form = methods ?? useForm({ defaultValues });
28
- const handleSubmit = async () => {
29
- setSubmissionState((prev) => ({
30
- ...prev,
31
- error: void 0,
32
- isSubmitting: true
33
- }));
34
- try {
35
- await form.handleSubmit(async (formData) => {
36
- await onSubmit(formData);
37
- })();
38
- setSubmissionState({
39
- isSubmitted: true,
40
- isSubmitting: false,
41
- isSuccess: true
42
- });
43
- onSuccess?.(form.getValues());
44
- } catch (error) {
45
- const errorMessage = error instanceof Error ? error.message : "An error occurred";
46
- setSubmissionState({
47
- error: errorMessage,
48
- isSubmitted: true,
49
- isSubmitting: false,
50
- isSuccess: false
51
- });
52
- onError?.({
53
- message: errorMessage
54
- });
55
- }
56
- };
57
- const resetForm = () => {
58
- form.reset();
59
- setSubmissionState({
60
- isSubmitted: false,
61
- isSubmitting: false,
62
- isSuccess: false
63
- });
64
- };
65
- return {
66
- error: submissionState.error,
67
- form,
68
- handleSubmit,
69
- isSubmitted: submissionState.isSubmitted,
70
- isSubmitting: submissionState.isSubmitting,
71
- isSuccess: submissionState.isSuccess,
72
- resetForm,
73
- submissionState
74
- };
75
- }
76
-
77
- // src/components/FormField.tsx
78
- import React17 from "react";
79
- import { useWatch as useWatch3 } from "react-hook-form";
80
-
81
- // src/fields/AutocompleteField.tsx
82
- import React from "react";
83
- import { Controller } from "react-hook-form";
84
-
85
- // src/ui/react.ts
86
- import {
87
- Autocomplete,
88
- AutocompleteItem,
89
- Button,
90
- Checkbox,
91
- DateInput,
92
- DatePicker,
93
- Input,
94
- Radio,
95
- RadioGroup,
96
- Select,
97
- SelectItem,
98
- Slider,
99
- Spinner,
100
- Switch,
101
- Textarea
102
- } from "@heroui/react";
103
-
104
- // src/fields/AutocompleteField.tsx
105
- function AutocompleteField(props) {
106
- const {
107
- autocompleteProps,
108
- children,
109
- className,
110
- control,
111
- description,
112
- isDisabled,
113
- items,
114
- label,
115
- name,
116
- placeholder,
117
- rules
118
- } = props;
119
- return /* @__PURE__ */ React.createElement(
120
- Controller,
121
- {
122
- control,
123
- name,
124
- render: ({ field: field2, fieldState }) => {
125
- const selectedKey = field2.value;
126
- const hasSelectedValue = selectedKey != null && selectedKey !== "";
127
- const allowsCustomValue = autocompleteProps?.allowsCustomValue ?? false;
128
- const shouldShowInputValue = allowsCustomValue || !hasSelectedValue;
129
- return /* @__PURE__ */ React.createElement("div", { className }, /* @__PURE__ */ React.createElement(
130
- Autocomplete,
131
- {
132
- ...autocompleteProps,
133
- description,
134
- errorMessage: fieldState.error?.message,
135
- isDisabled,
136
- isInvalid: Boolean(fieldState.error),
137
- label,
138
- placeholder,
139
- selectedKey: allowsCustomValue ? void 0 : hasSelectedValue ? String(selectedKey) : void 0,
140
- inputValue: shouldShowInputValue ? field2.value ?? "" : void 0,
141
- onSelectionChange: (key) => {
142
- const next = key ?? "";
143
- field2.onChange(next);
144
- },
145
- onInputChange: (value) => {
146
- if (allowsCustomValue) {
147
- field2.onChange(value);
148
- }
149
- },
150
- items
151
- },
152
- children ? children : (item) => /* @__PURE__ */ React.createElement(
153
- AutocompleteItem,
154
- {
155
- key: String(item.value),
156
- textValue: String(item.value),
157
- description: item.description,
158
- isDisabled: item.disabled
159
- },
160
- item.label
161
- )
162
- ));
163
- },
164
- rules
165
- }
166
- );
167
- }
168
-
169
- // src/fields/CheckboxField.tsx
170
- import React3 from "react";
171
- import { Controller as Controller2 } from "react-hook-form";
172
-
173
- // src/providers/ConfigProvider.tsx
174
- import React2, { createContext, useContext, useMemo } from "react";
175
- var DefaultsContext = createContext(null);
176
- function HeroHookFormProvider(props) {
177
- const value = useMemo(() => props.defaults ?? {}, [props.defaults]);
178
- return /* @__PURE__ */ React2.createElement(DefaultsContext.Provider, { value }, props.children);
179
- }
180
- function useHeroHookFormDefaults() {
181
- const cfg = useContext(DefaultsContext) ?? {};
182
- const common = cfg.common ?? {};
183
- const commonInput = {
184
- ...common.color !== void 0 ? { color: common.color } : {},
185
- ...common.size !== void 0 ? { size: common.size } : {},
186
- ...common.variant !== void 0 ? { variant: common.variant } : {},
187
- ...common.radius !== void 0 ? { radius: common.radius } : {},
188
- ...common.labelPlacement !== void 0 ? {
189
- labelPlacement: common.labelPlacement
190
- } : {}
191
- };
192
- const commonTextarea = {
193
- ...common.color !== void 0 ? { color: common.color } : {},
194
- ...common.size !== void 0 ? { size: common.size } : {},
195
- ...common.variant !== void 0 ? { variant: common.variant } : {},
196
- ...common.radius !== void 0 ? { radius: common.radius } : {},
197
- ...common.labelPlacement !== void 0 ? {
198
- labelPlacement: common.labelPlacement
199
- } : {}
200
- };
201
- const commonSelect = {
202
- ...common.color !== void 0 ? { color: common.color } : {},
203
- ...common.size !== void 0 ? { size: common.size } : {},
204
- ...common.variant !== void 0 ? { variant: common.variant } : {},
205
- ...common.radius !== void 0 ? { radius: common.radius } : {},
206
- ...common.labelPlacement !== void 0 ? {
207
- labelPlacement: common.labelPlacement
208
- } : {}
209
- };
210
- const commonCheckbox = {
211
- ...common.color !== void 0 ? {
212
- color: common.color
213
- } : {},
214
- ...common.size !== void 0 ? { size: common.size } : {}
215
- };
216
- const commonRadioGroup = {
217
- ...common.color !== void 0 ? {
218
- color: common.color
219
- } : {},
220
- ...common.size !== void 0 ? { size: common.size } : {}
221
- };
222
- const commonDateInput = {
223
- ...common.color !== void 0 ? {
224
- color: common.color
225
- } : {},
226
- ...common.size !== void 0 ? { size: common.size } : {},
227
- ...common.variant !== void 0 ? {
228
- variant: common.variant
229
- } : {},
230
- ...common.radius !== void 0 ? {
231
- radius: common.radius
232
- } : {}
233
- };
234
- const commonSlider = {
235
- ...common.color !== void 0 ? { color: common.color } : {},
236
- ...common.size !== void 0 ? { size: common.size } : {}
237
- };
238
- const commonSwitch = {
239
- ...common.color !== void 0 ? { color: common.color } : {},
240
- ...common.size !== void 0 ? { size: common.size } : {}
241
- };
242
- const commonButton = {
243
- ...common.color !== void 0 ? { color: common.color } : {},
244
- ...common.size !== void 0 ? { size: common.size } : {}
245
- };
246
- return {
247
- checkbox: { ...commonCheckbox, ...cfg.checkbox ?? {} },
248
- dateInput: { ...commonDateInput, ...cfg.dateInput ?? {} },
249
- input: { ...commonInput, ...cfg.input ?? {} },
250
- radioGroup: { ...commonRadioGroup, ...cfg.radioGroup ?? {} },
251
- select: { ...commonSelect, ...cfg.select ?? {} },
252
- slider: { ...commonSlider, ...cfg.slider ?? {} },
253
- submitButton: { ...commonButton, ...cfg.submitButton ?? {} },
254
- switch: { ...commonSwitch, ...cfg.switch ?? {} },
255
- textarea: { ...commonTextarea, ...cfg.textarea ?? {} }
256
- };
257
- }
258
-
259
- // src/fields/CheckboxField.tsx
260
- function CheckboxField(props) {
261
- const {
262
- checkboxProps,
263
- className,
264
- control,
265
- description,
266
- isDisabled,
267
- label,
268
- name,
269
- rules
270
- } = props;
271
- const defaults = useHeroHookFormDefaults();
272
- return /* @__PURE__ */ React3.createElement(
273
- Controller2,
274
- {
275
- control,
276
- name,
277
- render: ({ field: field2, fieldState }) => /* @__PURE__ */ React3.createElement("div", { className }, /* @__PURE__ */ React3.createElement(
278
- Checkbox,
279
- {
280
- ...defaults.checkbox,
281
- ...checkboxProps,
282
- isDisabled,
283
- isInvalid: Boolean(fieldState.error),
284
- isSelected: Boolean(field2.value),
285
- onBlur: field2.onBlur,
286
- onValueChange: (val) => field2.onChange(val)
287
- },
288
- label
289
- ), description ? /* @__PURE__ */ React3.createElement("p", { className: "text-small text-default-400" }, description) : null, fieldState.error?.message ? /* @__PURE__ */ React3.createElement("p", { className: "text-tiny text-danger mt-1" }, fieldState.error.message) : null),
290
- rules
291
- }
292
- );
293
- }
294
-
295
- // src/fields/ConditionalField.tsx
296
- import React4 from "react";
297
- import { useWatch, useFormContext } from "react-hook-form";
298
- function ConditionalField({
299
- className,
300
- config,
301
- control
302
- }) {
303
- const { condition, field: field2 } = config;
304
- const form = useFormContext();
305
- const formValues = useWatch({ control });
306
- const shouldShow = condition(formValues);
307
- if (!shouldShow) {
308
- return null;
309
- }
310
- return /* @__PURE__ */ React4.createElement("div", { className }, /* @__PURE__ */ React4.createElement(
311
- FormField,
312
- {
313
- config: field2,
314
- form,
315
- submissionState: {
316
- error: void 0,
317
- isSubmitted: false,
318
- isSubmitting: false,
319
- isSuccess: false
320
- }
321
- }
322
- ));
323
- }
324
-
325
- // src/fields/ContentField.tsx
326
- import React5 from "react";
327
- function ContentField({
328
- config,
329
- form,
330
- submissionState
331
- }) {
332
- if (config.render) {
333
- return /* @__PURE__ */ React5.createElement("div", { className: config.className }, config.render({
334
- errors: form.formState.errors,
335
- form,
336
- isSubmitting: submissionState.isSubmitting
337
- }));
338
- }
339
- return /* @__PURE__ */ React5.createElement("div", { className: config.className }, config.title && /* @__PURE__ */ React5.createElement("h3", { className: "text-lg font-semibold text-foreground mb-2" }, config.title), config.description && /* @__PURE__ */ React5.createElement("p", { className: "text-sm text-muted-foreground" }, config.description));
340
- }
341
-
342
- // src/fields/DateField.tsx
343
- import React6 from "react";
344
- import { Controller as Controller3 } from "react-hook-form";
345
- function CoercedDateInput(props) {
346
- const { dateProps, description, disabled, errorMessage, field: field2, label } = props;
347
- const defaults = useHeroHookFormDefaults();
348
- return /* @__PURE__ */ React6.createElement(
349
- DateInput,
350
- {
351
- ...defaults.dateInput,
352
- ...dateProps,
353
- description,
354
- errorMessage,
355
- isDisabled: disabled,
356
- isInvalid: Boolean(errorMessage),
357
- label,
358
- value: field2.value ?? null,
359
- onBlur: field2.onBlur,
360
- onChange: field2.onChange
361
- }
362
- );
363
- }
364
- function DateField(props) {
365
- const {
366
- className,
367
- control,
368
- dateProps,
369
- description,
370
- isDisabled,
371
- label,
372
- name,
373
- rules,
374
- transform
375
- } = props;
376
- return /* @__PURE__ */ React6.createElement(
377
- Controller3,
378
- {
379
- control,
380
- name,
381
- render: ({ field: field2, fieldState }) => /* @__PURE__ */ React6.createElement("div", { className }, /* @__PURE__ */ React6.createElement(
382
- CoercedDateInput,
383
- {
384
- dateProps,
385
- description,
386
- disabled: isDisabled,
387
- errorMessage: fieldState.error?.message,
388
- field: {
389
- ...field2,
390
- onChange: (value) => field2.onChange(transform ? transform(value) : value)
391
- },
392
- label
393
- }
394
- )),
395
- rules
396
- }
397
- );
398
- }
399
-
400
- // src/fields/DynamicSectionField.tsx
401
- import React7 from "react";
402
- import { useWatch as useWatch2, useFormContext as useFormContext2 } from "react-hook-form";
403
- function DynamicSectionField({
404
- className,
405
- config,
406
- control
407
- }) {
408
- const { condition, description, fields, title } = config;
409
- const form = useFormContext2();
410
- const formValues = useWatch2({ control });
411
- const shouldShow = condition(formValues);
412
- if (!shouldShow) {
413
- return null;
414
- }
415
- return /* @__PURE__ */ React7.createElement("div", { className }, (title || description) && /* @__PURE__ */ React7.createElement("div", { className: "mb-6" }, title && /* @__PURE__ */ React7.createElement("h3", { className: "text-lg font-semibold text-gray-900 mb-2" }, title), description && /* @__PURE__ */ React7.createElement("p", { className: "text-sm text-gray-600" }, description)), /* @__PURE__ */ React7.createElement("div", { className: "space-y-4" }, fields.map((fieldConfig, index) => /* @__PURE__ */ React7.createElement(
416
- FormField,
417
- {
418
- key: `${fieldConfig.name}-${index}`,
419
- config: fieldConfig,
420
- form,
421
- submissionState: {
422
- error: void 0,
423
- isSubmitted: false,
424
- isSubmitting: false,
425
- isSuccess: false
426
- }
427
- }
428
- ))));
429
- }
430
-
431
- // src/fields/FieldArrayField.tsx
432
- import React8 from "react";
433
- import { useFieldArray, useFormContext as useFormContext3 } from "react-hook-form";
434
- import { Button as Button2 } from "@heroui/react";
435
- function FieldArrayField({
436
- className,
437
- config
438
- }) {
439
- const {
440
- addButtonText = "Add Item",
441
- fields: fieldConfigs,
442
- max = 10,
443
- min = 0,
444
- name,
445
- removeButtonText = "Remove"
446
- } = config;
447
- const form = useFormContext3();
448
- if (!form || !form.control) {
449
- return null;
450
- }
451
- const { control } = form;
452
- const { append, fields, remove } = useFieldArray({
453
- control,
454
- name
455
- // FieldArray name
456
- });
457
- const canAdd = fields.length < max;
458
- const canRemove = fields.length > min;
459
- const handleAdd = () => {
460
- if (canAdd) {
461
- const defaultValues = fieldConfigs.reduce((acc, fieldConfig) => {
462
- const fieldName = fieldConfig.name;
463
- if (fieldConfig.type === "checkbox" || fieldConfig.type === "switch") {
464
- acc[fieldName] = false;
465
- } else if (fieldConfig.type === "slider") {
466
- acc[fieldName] = 0;
467
- } else {
468
- acc[fieldName] = "";
469
- }
470
- return acc;
471
- }, {});
472
- append(defaultValues);
473
- }
474
- };
475
- const handleRemove = (index) => {
476
- if (canRemove) {
477
- remove(index);
478
- }
479
- };
480
- return /* @__PURE__ */ React8.createElement("div", { className }, /* @__PURE__ */ React8.createElement("div", { className: "space-y-4" }, fields.map((field2, index) => /* @__PURE__ */ React8.createElement(
481
- "div",
482
- {
483
- key: field2.id,
484
- className: "border border-gray-200 rounded-lg p-4 space-y-4"
485
- },
486
- /* @__PURE__ */ React8.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React8.createElement("h4", { className: "text-sm font-medium text-gray-700" }, config.label, " #", index + 1), canRemove && /* @__PURE__ */ React8.createElement(
487
- Button2,
488
- {
489
- size: "sm",
490
- variant: "light",
491
- color: "danger",
492
- startContent: "\u{1F5D1}\uFE0F",
493
- onPress: () => handleRemove(index),
494
- "aria-label": `${removeButtonText} ${config.label} ${index + 1}`
495
- },
496
- removeButtonText
497
- )),
498
- /* @__PURE__ */ React8.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, fieldConfigs.map((fieldConfig) => /* @__PURE__ */ React8.createElement(
499
- FormField,
500
- {
501
- key: `${fieldConfig.name}-${index}`,
502
- config: {
503
- ...fieldConfig,
504
- name: `${name}.${index}.${fieldConfig.name}`
505
- },
506
- form,
507
- submissionState: {
508
- error: void 0,
509
- isSubmitted: false,
510
- isSubmitting: false,
511
- isSuccess: false
512
- }
513
- }
514
- )))
515
- )), canAdd && /* @__PURE__ */ React8.createElement(
516
- Button2,
517
- {
518
- variant: "bordered",
519
- startContent: "\u2795",
520
- onPress: handleAdd,
521
- className: "w-full"
522
- },
523
- addButtonText
524
- ), fields.length === 0 && /* @__PURE__ */ React8.createElement("div", { className: "text-center py-8 text-gray-500" }, /* @__PURE__ */ React8.createElement("p", null, "No ", config.label?.toLowerCase(), " added yet."), /* @__PURE__ */ React8.createElement(
525
- Button2,
526
- {
527
- variant: "bordered",
528
- startContent: "\u2795",
529
- onPress: handleAdd,
530
- className: "mt-2"
531
- },
532
- addButtonText
533
- ))));
534
- }
535
-
536
- // src/fields/FileField.tsx
537
- import React9 from "react";
538
- import { Controller as Controller4 } from "react-hook-form";
539
- function CoercedFileInput(props) {
540
- const {
541
- accept,
542
- description,
543
- disabled,
544
- errorMessage,
545
- field: field2,
546
- fileProps,
547
- label,
548
- multiple
549
- } = props;
550
- const defaults = useHeroHookFormDefaults();
551
- return /* @__PURE__ */ React9.createElement(
552
- Input,
553
- {
554
- ...defaults.input,
555
- ...fileProps,
556
- accept,
557
- description,
558
- errorMessage,
559
- isDisabled: disabled,
560
- isInvalid: Boolean(errorMessage),
561
- label,
562
- multiple,
563
- type: "file",
564
- value: field2.value ? "" : "",
565
- onBlur: field2.onBlur,
566
- onChange: (e) => {
567
- const target = e.target;
568
- field2.onChange(target.files);
569
- }
570
- }
571
- );
572
- }
573
- function FileField(props) {
574
- const {
575
- accept,
576
- className,
577
- control,
578
- description,
579
- fileProps,
580
- isDisabled,
581
- label,
582
- multiple = false,
583
- name,
584
- rules,
585
- transform
586
- } = props;
587
- return /* @__PURE__ */ React9.createElement(
588
- Controller4,
589
- {
590
- control,
591
- name,
592
- render: ({ field: field2, fieldState }) => /* @__PURE__ */ React9.createElement("div", { className }, /* @__PURE__ */ React9.createElement(
593
- CoercedFileInput,
594
- {
595
- accept,
596
- description,
597
- disabled: isDisabled,
598
- errorMessage: fieldState.error?.message,
599
- field: {
600
- ...field2,
601
- onChange: (value) => field2.onChange(transform ? transform(value) : value)
602
- },
603
- fileProps,
604
- label,
605
- multiple
606
- }
607
- )),
608
- rules
609
- }
610
- );
611
- }
612
-
613
- // src/fields/FontPickerField.tsx
614
- import React10 from "react";
615
- import { Controller as Controller5 } from "react-hook-form";
616
- var FontPickerComponent = null;
617
- var fontPickerLoaded = false;
618
- var fontPickerLoading = false;
619
- var loadingCallbacks = [];
620
- function FontPickerField(props) {
621
- const {
622
- className,
623
- control,
624
- description,
625
- fontPickerProps,
626
- isDisabled,
627
- label,
628
- name,
629
- rules
630
- } = props;
631
- const [fontPickerState, setFontPickerState] = React10.useState({
632
- component: FontPickerComponent,
633
- error: null,
634
- loading: false
635
- });
636
- React10.useEffect(() => {
637
- if (fontPickerLoaded && FontPickerComponent) {
638
- setFontPickerState({
639
- component: FontPickerComponent,
640
- error: null,
641
- loading: false
642
- });
643
- return;
644
- }
645
- if (fontPickerLoading) {
646
- setFontPickerState((prev) => ({ ...prev, loading: true }));
647
- const callback = () => {
648
- if (fontPickerLoaded && FontPickerComponent) {
649
- setFontPickerState({
650
- component: FontPickerComponent,
651
- error: null,
652
- loading: false
653
- });
654
- } else {
655
- setFontPickerState({
656
- component: null,
657
- error: "Font picker package not found",
658
- loading: false
659
- });
660
- }
661
- };
662
- loadingCallbacks.push(callback);
663
- return;
664
- }
665
- const loadFontPicker = async () => {
666
- fontPickerLoading = true;
667
- setFontPickerState((prev) => ({ ...prev, loading: true }));
668
- try {
669
- const fontPickerModule = await import("@rachelallyson/heroui-font-picker");
670
- FontPickerComponent = fontPickerModule.FontPicker || fontPickerModule.default;
671
- fontPickerLoaded = true;
672
- fontPickerLoading = false;
673
- setFontPickerState({
674
- component: FontPickerComponent,
675
- error: null,
676
- loading: false
677
- });
678
- loadingCallbacks.forEach((callback) => callback());
679
- loadingCallbacks.length = 0;
680
- } catch {
681
- fontPickerLoading = false;
682
- setFontPickerState({
683
- component: null,
684
- error: "Font picker package not found",
685
- loading: false
686
- });
687
- loadingCallbacks.forEach((callback) => callback());
688
- loadingCallbacks.length = 0;
689
- }
690
- };
691
- void loadFontPicker();
692
- }, []);
693
- if (fontPickerState.loading) {
694
- return /* @__PURE__ */ React10.createElement("div", { className }, /* @__PURE__ */ React10.createElement("div", { className: "space-y-2" }, label && /* @__PURE__ */ React10.createElement("label", { className: "block text-sm font-medium text-foreground" }, label), description && /* @__PURE__ */ React10.createElement("p", { className: "text-sm text-muted-foreground" }, description), /* @__PURE__ */ React10.createElement("div", { className: "p-4 border border-default-200 bg-default-50 rounded-medium" }, /* @__PURE__ */ React10.createElement("p", { className: "text-default-600 text-sm" }, "Loading font picker..."))));
695
- }
696
- if (!fontPickerState.component) {
697
- return /* @__PURE__ */ React10.createElement("div", { className }, /* @__PURE__ */ React10.createElement("div", { className: "space-y-2" }, label && /* @__PURE__ */ React10.createElement("label", { className: "block text-sm font-medium text-foreground" }, label), description && /* @__PURE__ */ React10.createElement("p", { className: "text-sm text-muted-foreground" }, description), /* @__PURE__ */ React10.createElement("div", { className: "p-4 border border-warning-200 bg-warning-50 rounded-medium" }, /* @__PURE__ */ React10.createElement("p", { className: "text-warning-800 text-sm" }, "Font picker requires the @rachelallyson/heroui-font-picker package. Please install it as a peer dependency for advanced font selection features."))));
698
- }
699
- return /* @__PURE__ */ React10.createElement(
700
- Controller5,
701
- {
702
- control,
703
- name,
704
- render: ({ field: field2, fieldState }) => /* @__PURE__ */ React10.createElement(
705
- fontPickerState.component,
706
- {
707
- label,
708
- description,
709
- value: field2.value ?? "",
710
- onSelectionChange: (value) => field2.onChange(value),
711
- errorMessage: fieldState.error?.message,
712
- isDisabled,
713
- className,
714
- ...fontPickerProps
715
- }
716
- ),
717
- rules
718
- }
719
- );
720
- }
721
-
722
- // src/fields/InputField.tsx
723
- import React11 from "react";
724
- import { Controller as Controller6 } from "react-hook-form";
725
- function CoercedInput(props) {
726
- const { description, disabled, errorMessage, field: field2, inputProps, label } = props;
727
- const defaults = useHeroHookFormDefaults();
728
- return /* @__PURE__ */ React11.createElement(
729
- Input,
730
- {
731
- ...defaults.input,
732
- ...inputProps,
733
- description,
734
- errorMessage,
735
- isDisabled: disabled,
736
- isInvalid: Boolean(errorMessage),
737
- label,
738
- value: field2.value ?? "",
739
- onBlur: field2.onBlur,
740
- onValueChange: field2.onChange
741
- }
742
- );
743
- }
744
- var InputField = React11.memo(
745
- (props) => {
746
- const {
747
- className,
748
- control,
749
- description,
750
- inputProps,
751
- isDisabled,
752
- label,
753
- name,
754
- rules,
755
- transform
756
- } = props;
757
- return /* @__PURE__ */ React11.createElement(
758
- Controller6,
759
- {
760
- control,
761
- name,
762
- render: ({ field: field2, fieldState }) => /* @__PURE__ */ React11.createElement("div", { className }, /* @__PURE__ */ React11.createElement(
763
- CoercedInput,
764
- {
765
- description,
766
- disabled: isDisabled,
767
- errorMessage: fieldState.error?.message,
768
- field: {
769
- ...field2,
770
- onChange: (value) => {
771
- if (inputProps?.type === "number") {
772
- const numValue = value === "" ? void 0 : Number(value);
773
- field2.onChange(
774
- transform ? transform(String(numValue)) : numValue
775
- );
776
- } else {
777
- field2.onChange(transform ? transform(value) : value);
778
- }
779
- }
780
- },
781
- inputProps,
782
- label
783
- }
784
- )),
785
- rules
786
- }
787
- );
788
- }
789
- );
790
-
791
- // src/fields/RadioGroupField.tsx
792
- import React12 from "react";
793
- import { Controller as Controller7 } from "react-hook-form";
794
- function RadioGroupField(props) {
795
- const {
796
- className,
797
- control,
798
- description,
799
- isDisabled,
800
- label,
801
- name,
802
- options,
803
- radioGroupProps,
804
- rules
805
- } = props;
806
- const defaults = useHeroHookFormDefaults();
807
- return /* @__PURE__ */ React12.createElement(
808
- Controller7,
809
- {
810
- control,
811
- name,
812
- render: ({ field: field2, fieldState }) => /* @__PURE__ */ React12.createElement("div", { className }, /* @__PURE__ */ React12.createElement(
813
- RadioGroup,
814
- {
815
- ...defaults.radioGroup,
816
- ...radioGroupProps,
817
- description,
818
- isDisabled,
819
- isInvalid: Boolean(fieldState.error),
820
- label,
821
- value: String(field2.value ?? ""),
822
- onBlur: field2.onBlur,
823
- onValueChange: (val) => field2.onChange(val)
824
- },
825
- options.map((opt) => /* @__PURE__ */ React12.createElement(
826
- Radio,
827
- {
828
- key: String(opt.value),
829
- isDisabled: opt.disabled,
830
- value: String(opt.value)
831
- },
832
- opt.label
833
- ))
834
- ), fieldState.error?.message ? /* @__PURE__ */ React12.createElement("p", { className: "text-tiny text-danger mt-1" }, fieldState.error.message) : null),
835
- rules
836
- }
837
- );
838
- }
839
-
840
- // src/fields/SelectField.tsx
841
- import React13 from "react";
842
- import { Controller as Controller8 } from "react-hook-form";
843
- function SelectField(props) {
844
- const {
845
- className,
846
- control,
847
- description,
848
- isDisabled,
849
- label,
850
- name,
851
- options,
852
- placeholder,
853
- rules,
854
- selectProps
855
- } = props;
856
- const defaults = useHeroHookFormDefaults();
857
- return /* @__PURE__ */ React13.createElement(
858
- Controller8,
859
- {
860
- control,
861
- name,
862
- render: ({ field: field2, fieldState }) => {
863
- const selectedKey = field2.value;
864
- return /* @__PURE__ */ React13.createElement("div", { className }, /* @__PURE__ */ React13.createElement(
865
- Select,
866
- {
867
- ...defaults.select,
868
- ...selectProps,
869
- description,
870
- errorMessage: fieldState.error?.message,
871
- isDisabled,
872
- isInvalid: Boolean(fieldState.error),
873
- label,
874
- placeholder,
875
- selectedKeys: selectedKey != null ? /* @__PURE__ */ new Set([String(selectedKey)]) : /* @__PURE__ */ new Set(),
876
- onSelectionChange: (keys) => {
877
- const keyArray = Array.from(keys);
878
- const next = keyArray[0] ?? "";
879
- field2.onChange(next);
880
- }
881
- },
882
- options.map((opt) => /* @__PURE__ */ React13.createElement(
883
- SelectItem,
884
- {
885
- key: String(opt.value),
886
- isDisabled: opt.disabled,
887
- textValue: String(opt.value)
888
- },
889
- opt.label
890
- ))
891
- ));
892
- },
893
- rules
894
- }
895
- );
896
- }
897
-
898
- // src/fields/SliderField.tsx
899
- import React14 from "react";
900
- import { Controller as Controller9 } from "react-hook-form";
901
- function CoercedSlider(props) {
902
- const { description, disabled, errorMessage, field: field2, label, sliderProps } = props;
903
- const defaults = useHeroHookFormDefaults();
904
- return /* @__PURE__ */ React14.createElement(
905
- Slider,
906
- {
907
- ...defaults.slider,
908
- ...sliderProps,
909
- description,
910
- errorMessage,
911
- isDisabled: disabled,
912
- isInvalid: Boolean(errorMessage),
913
- label,
914
- value: field2.value ?? 0,
915
- onBlur: field2.onBlur,
916
- onValueChange: field2.onChange
917
- }
918
- );
919
- }
920
- function SliderField(props) {
921
- const {
922
- className,
923
- control,
924
- description,
925
- isDisabled,
926
- label,
927
- name,
928
- rules,
929
- sliderProps,
930
- transform
931
- } = props;
932
- return /* @__PURE__ */ React14.createElement(
933
- Controller9,
934
- {
935
- control,
936
- name,
937
- render: ({ field: field2, fieldState }) => /* @__PURE__ */ React14.createElement("div", { className }, /* @__PURE__ */ React14.createElement(
938
- CoercedSlider,
939
- {
940
- description,
941
- disabled: isDisabled,
942
- errorMessage: fieldState.error?.message,
943
- field: {
944
- ...field2,
945
- onChange: (value) => field2.onChange(transform ? transform(value) : value)
946
- },
947
- label,
948
- sliderProps
949
- }
950
- )),
951
- rules
952
- }
953
- );
954
- }
955
-
956
- // src/fields/SwitchField.tsx
957
- import React15 from "react";
958
- import { Controller as Controller10 } from "react-hook-form";
959
- function SwitchField(props) {
960
- const {
961
- className,
962
- control,
963
- description,
964
- isDisabled,
965
- label,
966
- name,
967
- rules,
968
- switchProps
969
- } = props;
970
- const defaults = useHeroHookFormDefaults();
971
- return /* @__PURE__ */ React15.createElement(
972
- Controller10,
973
- {
974
- control,
975
- name,
976
- render: ({ field: field2, fieldState }) => /* @__PURE__ */ React15.createElement("div", { className }, /* @__PURE__ */ React15.createElement(
977
- Switch,
978
- {
979
- ...defaults.switch,
980
- ...switchProps,
981
- isDisabled,
982
- isSelected: Boolean(field2.value),
983
- onBlur: field2.onBlur,
984
- onValueChange: (val) => field2.onChange(val)
985
- },
986
- label
987
- ), description ? /* @__PURE__ */ React15.createElement("p", { className: "text-small text-default-400" }, description) : null, fieldState.error?.message ? /* @__PURE__ */ React15.createElement("p", { className: "text-tiny text-danger mt-1" }, fieldState.error.message) : null),
988
- rules
989
- }
990
- );
991
- }
992
-
993
- // src/fields/TextareaField.tsx
994
- import React16 from "react";
995
- import { Controller as Controller11 } from "react-hook-form";
996
- function TextareaField(props) {
997
- const {
998
- className,
999
- control,
1000
- description,
1001
- isDisabled,
1002
- label,
1003
- name,
1004
- rules,
1005
- textareaProps
1006
- } = props;
1007
- const defaults = useHeroHookFormDefaults();
1008
- return /* @__PURE__ */ React16.createElement(
1009
- Controller11,
1010
- {
1011
- control,
1012
- name,
1013
- render: ({ field: field2, fieldState }) => /* @__PURE__ */ React16.createElement("div", { className }, /* @__PURE__ */ React16.createElement(
1014
- Textarea,
1015
- {
1016
- ...defaults.textarea,
1017
- ...textareaProps,
1018
- description,
1019
- errorMessage: fieldState.error?.message,
1020
- isDisabled,
1021
- isInvalid: Boolean(fieldState.error),
1022
- label,
1023
- value: field2.value ?? "",
1024
- onBlur: field2.onBlur,
1025
- onValueChange: field2.onChange
1026
- }
1027
- )),
1028
- rules
1029
- }
1030
- );
1031
- }
1032
-
1033
- // src/components/FormField.tsx
1034
- var FormField = React17.memo(
1035
- ({
1036
- config,
1037
- form,
1038
- submissionState
1039
- }) => {
1040
- if (!form || !form.control) {
1041
- return null;
1042
- }
1043
- const { control } = form;
1044
- const watchedValues = useWatch3({ control });
1045
- if (config.type === "content") {
1046
- return /* @__PURE__ */ React17.createElement(
1047
- ContentField,
1048
- {
1049
- config,
1050
- form,
1051
- submissionState
1052
- }
1053
- );
1054
- }
1055
- if (config.condition && !config.condition(watchedValues)) {
1056
- return null;
1057
- }
1058
- if (config.dependsOn) {
1059
- const dependentValue = watchedValues[config.dependsOn];
1060
- if (config.dependsOnValue !== void 0 && dependentValue !== config.dependsOnValue) {
1061
- return null;
1062
- }
1063
- }
1064
- const baseProps = {
1065
- ariaDescribedBy: config.ariaDescribedBy,
1066
- ariaLabel: config.ariaLabel,
1067
- className: config.className,
1068
- description: config.description,
1069
- isDisabled: config.isDisabled ?? submissionState.isSubmitting,
1070
- label: config.label,
1071
- name: config.name,
1072
- rules: config.rules
1073
- };
1074
- switch (config.type) {
1075
- case "input":
1076
- return /* @__PURE__ */ React17.createElement(
1077
- InputField,
1078
- {
1079
- ...baseProps,
1080
- control,
1081
- defaultValue: config.defaultValue,
1082
- inputProps: config.inputProps
1083
- }
1084
- );
1085
- case "textarea":
1086
- return /* @__PURE__ */ React17.createElement(
1087
- TextareaField,
1088
- {
1089
- ...baseProps,
1090
- control,
1091
- defaultValue: config.defaultValue,
1092
- textareaProps: config.textareaProps
1093
- }
1094
- );
1095
- case "select":
1096
- return /* @__PURE__ */ React17.createElement(
1097
- SelectField,
1098
- {
1099
- ...baseProps,
1100
- control,
1101
- defaultValue: config.defaultValue,
1102
- options: (config.options ?? []).map((opt) => ({
1103
- label: opt.label,
1104
- value: String(opt.value)
1105
- })),
1106
- selectProps: config.selectProps
1107
- }
1108
- );
1109
- case "autocomplete":
1110
- return /* @__PURE__ */ React17.createElement(
1111
- AutocompleteField,
1112
- {
1113
- ...baseProps,
1114
- control,
1115
- defaultValue: config.defaultValue,
1116
- items: (config.options ?? []).map((opt) => ({
1117
- label: opt.label,
1118
- value: String(opt.value)
1119
- })),
1120
- autocompleteProps: config.autocompleteProps
1121
- }
1122
- );
1123
- case "checkbox":
1124
- return /* @__PURE__ */ React17.createElement(
1125
- CheckboxField,
1126
- {
1127
- ...baseProps,
1128
- checkboxProps: config.checkboxProps,
1129
- control,
1130
- defaultValue: config.defaultValue
1131
- }
1132
- );
1133
- case "radio":
1134
- return /* @__PURE__ */ React17.createElement(
1135
- RadioGroupField,
1136
- {
1137
- ...baseProps,
1138
- control,
1139
- defaultValue: config.defaultValue,
1140
- options: (config.radioOptions ?? []).map((opt) => ({
1141
- label: opt.label,
1142
- value: String(opt.value)
1143
- })),
1144
- radioGroupProps: config.radioProps
1145
- }
1146
- );
1147
- case "switch":
1148
- return /* @__PURE__ */ React17.createElement(
1149
- SwitchField,
1150
- {
1151
- ...baseProps,
1152
- control,
1153
- defaultValue: config.defaultValue,
1154
- switchProps: config.switchProps
1155
- }
1156
- );
1157
- case "slider":
1158
- return /* @__PURE__ */ React17.createElement(
1159
- SliderField,
1160
- {
1161
- ...baseProps,
1162
- control,
1163
- defaultValue: config.defaultValue,
1164
- sliderProps: config.sliderProps
1165
- }
1166
- );
1167
- case "date":
1168
- return /* @__PURE__ */ React17.createElement(
1169
- DateField,
1170
- {
1171
- ...baseProps,
1172
- control,
1173
- dateProps: config.dateProps,
1174
- defaultValue: config.defaultValue
1175
- }
1176
- );
1177
- case "file":
1178
- return /* @__PURE__ */ React17.createElement(
1179
- FileField,
1180
- {
1181
- ...baseProps,
1182
- accept: config.accept,
1183
- control,
1184
- defaultValue: config.defaultValue,
1185
- fileProps: config.fileProps,
1186
- multiple: config.multiple
1187
- }
1188
- );
1189
- case "fontPicker":
1190
- return /* @__PURE__ */ React17.createElement(
1191
- FontPickerField,
1192
- {
1193
- ...baseProps,
1194
- control,
1195
- defaultValue: config.defaultValue,
1196
- fontPickerProps: config.fontPickerProps
1197
- }
1198
- );
1199
- case "custom":
1200
- return config.render({
1201
- control,
1202
- errors: form.formState.errors,
1203
- form,
1204
- isSubmitting: submissionState.isSubmitting,
1205
- name: config.name
1206
- });
1207
- case "conditional":
1208
- return /* @__PURE__ */ React17.createElement(
1209
- ConditionalField,
1210
- {
1211
- config,
1212
- control,
1213
- className: config.className
1214
- }
1215
- );
1216
- case "fieldArray":
1217
- return /* @__PURE__ */ React17.createElement(
1218
- FieldArrayField,
1219
- {
1220
- config,
1221
- className: config.className
1222
- }
1223
- );
1224
- case "dynamicSection":
1225
- return /* @__PURE__ */ React17.createElement(
1226
- DynamicSectionField,
1227
- {
1228
- config,
1229
- control,
1230
- className: config.className
1231
- }
1232
- );
1233
- default: {
1234
- const fieldType = config.type;
1235
- console.warn(`Unknown field type: ${fieldType}`);
1236
- return null;
1237
- }
1238
- }
1239
- }
1240
- );
1241
-
1242
- // src/components/Form.tsx
1243
- function ConfigurableForm({
1244
- className,
1245
- columns = 1,
1246
- defaultValues,
1247
- fields,
1248
- layout = "vertical",
1249
- onError,
1250
- onSubmit,
1251
- onSuccess,
1252
- resetButtonText = "Reset",
1253
- showResetButton = false,
1254
- spacing = "4",
1255
- submitButtonProps = {},
1256
- submitButtonText = "Submit",
1257
- subtitle,
1258
- title
1259
- }) {
1260
- const {
1261
- error,
1262
- form,
1263
- handleSubmit,
1264
- isSubmitted,
1265
- isSubmitting,
1266
- isSuccess,
1267
- resetForm,
1268
- submissionState
1269
- } = useFormHelper({
1270
- defaultValues,
1271
- onError,
1272
- onSubmit,
1273
- onSuccess
1274
- });
1275
- const renderFields = () => {
1276
- if (layout === "grid") {
1277
- return /* @__PURE__ */ React18.createElement(
1278
- "div",
1279
- {
1280
- className: `grid gap-${spacing} ${columns === 1 ? "grid-cols-1" : columns === 2 ? "grid-cols-1 md:grid-cols-2" : "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"}`
1281
- },
1282
- fields.map((field2) => /* @__PURE__ */ React18.createElement(
1283
- FormField,
1284
- {
1285
- key: field2.name,
1286
- config: field2,
1287
- form,
1288
- submissionState
1289
- }
1290
- ))
1291
- );
1292
- }
1293
- if (layout === "horizontal") {
1294
- return /* @__PURE__ */ React18.createElement("div", { className: `grid gap-${spacing} grid-cols-1 md:grid-cols-2` }, fields.map((field2) => /* @__PURE__ */ React18.createElement(
1295
- FormField,
1296
- {
1297
- key: field2.name,
1298
- config: field2,
1299
- form,
1300
- submissionState
1301
- }
1302
- )));
1303
- }
1304
- return /* @__PURE__ */ React18.createElement("div", { className: `space-y-${spacing}` }, fields.map((field2) => /* @__PURE__ */ React18.createElement(
1305
- FormField,
1306
- {
1307
- key: field2.name,
1308
- config: field2,
1309
- form,
1310
- submissionState
1311
- }
1312
- )));
1313
- };
1314
- const handleFormSubmit = (e) => {
1315
- e.preventDefault();
1316
- void handleSubmit();
1317
- };
1318
- return /* @__PURE__ */ React18.createElement("form", { className, role: "form", onSubmit: handleFormSubmit }, title && /* @__PURE__ */ React18.createElement("div", { className: "mb-6" }, /* @__PURE__ */ React18.createElement("h2", { className: "text-xl font-semibold text-foreground mb-2" }, title), subtitle && /* @__PURE__ */ React18.createElement("p", { className: "text-sm text-muted-foreground" }, subtitle)), isSubmitted && isSuccess && /* @__PURE__ */ React18.createElement(
1319
- "div",
1320
- {
1321
- className: "mb-6 p-4 bg-success-50 border border-success-200 rounded-lg",
1322
- "data-testid": "success-message"
1323
- },
1324
- /* @__PURE__ */ React18.createElement("p", { className: "text-success-800 font-medium" }, "Success!"),
1325
- /* @__PURE__ */ React18.createElement("p", { className: "text-success-700 text-sm mt-1" }, "Your request has been processed successfully.")
1326
- ), error && /* @__PURE__ */ React18.createElement(
1327
- "div",
1328
- {
1329
- className: "mb-6 p-4 bg-danger-50 border border-danger-200 rounded-lg",
1330
- "data-testid": "error-message"
1331
- },
1332
- /* @__PURE__ */ React18.createElement("p", { className: "text-danger-800 font-medium" }, "Error"),
1333
- /* @__PURE__ */ React18.createElement("p", { className: "text-danger-700 text-sm mt-1" }, error)
1334
- ), renderFields(), /* @__PURE__ */ React18.createElement("div", { className: "mt-6 flex gap-3 justify-end" }, /* @__PURE__ */ React18.createElement(
1335
- Button3,
1336
- {
1337
- color: "primary",
1338
- isDisabled: isSubmitting,
1339
- isLoading: isSubmitting,
1340
- type: "submit",
1341
- ...submitButtonProps
1342
- },
1343
- submitButtonText
1344
- ), showResetButton && /* @__PURE__ */ React18.createElement(
1345
- Button3,
1346
- {
1347
- isDisabled: isSubmitting,
1348
- type: "button",
1349
- variant: "bordered",
1350
- onPress: resetForm
1351
- },
1352
- resetButtonText
1353
- )));
1354
- }
1355
-
1356
- // src/components/ServerActionForm.tsx
1357
- import React19 from "react";
1358
- import { useActionState } from "react";
1359
- function ServerActionForm({
1360
- action,
1361
- className,
1362
- clientValidationSchema,
1363
- columns = 1,
1364
- defaultValues,
1365
- fields,
1366
- initialState,
1367
- layout = "vertical",
1368
- onError,
1369
- onSuccess,
1370
- resetButtonText = "Reset",
1371
- showResetButton = false,
1372
- spacing = "4",
1373
- submitButtonProps = {},
1374
- submitButtonText = "Submit",
1375
- subtitle,
1376
- title
1377
- }) {
1378
- const [state, formAction, pending] = useActionState(
1379
- action,
1380
- initialState ?? { errors: void 0, message: void 0, success: false }
1381
- );
1382
- const formRef = React19.useRef(null);
1383
- const [clientErrors, setClientErrors] = React19.useState({});
1384
- const lastSubmittedFormData = React19.useRef(null);
1385
- React19.useEffect(() => {
1386
- if (state && (state.errors || state.message && !state.success)) {
1387
- onError?.({
1388
- errors: state.errors,
1389
- message: state.message
1390
- });
1391
- }
1392
- }, [state, onError]);
1393
- React19.useEffect(() => {
1394
- if (state?.success && lastSubmittedFormData.current) {
1395
- onSuccess?.(lastSubmittedFormData.current);
1396
- }
1397
- }, [state?.success, onSuccess]);
1398
- const handleReset = () => {
1399
- formRef.current?.reset();
1400
- setClientErrors({});
1401
- };
1402
- const handleSubmit = async (e) => {
1403
- e.preventDefault();
1404
- if (clientValidationSchema) {
1405
- const formData2 = new FormData(e.currentTarget);
1406
- const values = {};
1407
- formData2.forEach((val, key) => {
1408
- if (val === "on") {
1409
- values[key] = true;
1410
- } else if (val === "") {
1411
- if (!values[key]) {
1412
- values[key] = false;
1413
- }
1414
- } else {
1415
- values[key] = val;
1416
- }
1417
- });
1418
- const result = clientValidationSchema.safeParse(values);
1419
- if (!result.success) {
1420
- const errors = {};
1421
- result.error.issues.forEach((issue) => {
1422
- const path = issue.path.join(".");
1423
- errors[path] = issue.message;
1424
- });
1425
- setClientErrors((prev) => ({ ...prev, ...errors }));
1426
- return;
1427
- }
1428
- setClientErrors({});
1429
- }
1430
- const formData = new FormData(e.currentTarget);
1431
- lastSubmittedFormData.current = formData;
1432
- formAction(formData);
1433
- };
1434
- const renderFields = () => {
1435
- if (layout === "grid") {
1436
- return /* @__PURE__ */ React19.createElement(
1437
- "div",
1438
- {
1439
- className: `grid gap-${spacing} ${columns === 1 ? "grid-cols-1" : columns === 2 ? "grid-cols-1 md:grid-cols-2" : "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"}`
1440
- },
1441
- fields.map((field2) => /* @__PURE__ */ React19.createElement(
1442
- ServerActionField,
1443
- {
1444
- key: field2.name,
1445
- clientErrors,
1446
- defaultValues,
1447
- errors: state?.errors,
1448
- field: field2
1449
- }
1450
- ))
1451
- );
1452
- }
1453
- if (layout === "horizontal") {
1454
- return /* @__PURE__ */ React19.createElement("div", { className: `grid gap-${spacing} grid-cols-1 md:grid-cols-2` }, fields.map((field2) => /* @__PURE__ */ React19.createElement(
1455
- ServerActionField,
1456
- {
1457
- key: field2.name,
1458
- clientErrors,
1459
- defaultValues,
1460
- errors: state?.errors,
1461
- field: field2
1462
- }
1463
- )));
1464
- }
1465
- return /* @__PURE__ */ React19.createElement("div", { className: `space-y-${spacing}` }, fields.map((field2) => /* @__PURE__ */ React19.createElement(
1466
- ServerActionField,
1467
- {
1468
- key: field2.name,
1469
- clientErrors,
1470
- defaultValues,
1471
- errors: state?.errors,
1472
- field: field2
1473
- }
1474
- )));
1475
- };
1476
- return /* @__PURE__ */ React19.createElement(
1477
- "form",
1478
- {
1479
- ref: formRef,
1480
- className,
1481
- role: "form",
1482
- onSubmit: handleSubmit
1483
- },
1484
- title && /* @__PURE__ */ React19.createElement("div", { className: "mb-6" }, /* @__PURE__ */ React19.createElement("h2", { className: "text-xl font-semibold text-foreground mb-2" }, title), subtitle && /* @__PURE__ */ React19.createElement("p", { className: "text-sm text-muted-foreground" }, subtitle)),
1485
- state?.success && !pending && /* @__PURE__ */ React19.createElement(
1486
- "div",
1487
- {
1488
- className: "mb-6 p-4 bg-success-50 border border-success-200 rounded-lg",
1489
- "data-testid": "success-message"
1490
- },
1491
- /* @__PURE__ */ React19.createElement("p", { className: "text-success-800 font-medium" }, "Success!"),
1492
- state.message && /* @__PURE__ */ React19.createElement("p", { className: "text-success-700 text-sm mt-1" }, state.message)
1493
- ),
1494
- state?.message && !state.success && /* @__PURE__ */ React19.createElement(
1495
- "div",
1496
- {
1497
- className: "mb-6 p-4 bg-danger-50 border border-danger-200 rounded-lg",
1498
- "data-testid": "error-message"
1499
- },
1500
- /* @__PURE__ */ React19.createElement("p", { className: "text-danger-800 font-medium" }, "Error"),
1501
- /* @__PURE__ */ React19.createElement("p", { className: "text-danger-700 text-sm mt-1" }, state.message)
1502
- ),
1503
- renderFields(),
1504
- /* @__PURE__ */ React19.createElement("div", { className: "mt-6 flex gap-3 justify-end" }, /* @__PURE__ */ React19.createElement(
1505
- Button,
1506
- {
1507
- color: "primary",
1508
- isDisabled: pending,
1509
- isLoading: pending,
1510
- type: "submit",
1511
- ...submitButtonProps
1512
- },
1513
- submitButtonText
1514
- ), showResetButton && /* @__PURE__ */ React19.createElement(
1515
- Button,
1516
- {
1517
- isDisabled: pending,
1518
- type: "button",
1519
- variant: "bordered",
1520
- onPress: handleReset
1521
- },
1522
- resetButtonText
1523
- ))
1524
- );
1525
- }
1526
- function ServerActionField({
1527
- clientErrors,
1528
- defaultValues,
1529
- errors,
1530
- field: field2
1531
- }) {
1532
- if (field2.type === "content") {
1533
- const contentField2 = field2;
1534
- if (contentField2.render) {
1535
- return /* @__PURE__ */ React19.createElement("div", { className: contentField2.className }, contentField2.render({
1536
- errors: {},
1537
- form: null,
1538
- isSubmitting: false
1539
- }));
1540
- }
1541
- return /* @__PURE__ */ React19.createElement("div", { className: contentField2.className }, contentField2.title && /* @__PURE__ */ React19.createElement("h3", { className: "text-lg font-semibold text-foreground mb-2" }, contentField2.title), contentField2.description && /* @__PURE__ */ React19.createElement("p", { className: "text-sm text-muted-foreground" }, contentField2.description));
1542
- }
1543
- const fieldName = field2.name;
1544
- const fieldErrors = errors?.[fieldName];
1545
- const clientError = clientErrors?.[fieldName];
1546
- const errorMessage = clientError || (fieldErrors && fieldErrors.length > 0 ? fieldErrors[0] : void 0);
1547
- const getDefaultValue = () => {
1548
- const fromProps = defaultValues?.[fieldName];
1549
- const fromField = field2.defaultValue;
1550
- if (fromProps !== void 0 && fromProps !== null) {
1551
- return typeof fromProps === "string" ? fromProps : String(fromProps);
1552
- }
1553
- if (fromField !== void 0 && fromField !== null) {
1554
- return typeof fromField === "string" ? fromField : String(fromField);
1555
- }
1556
- return "";
1557
- };
1558
- const getDefaultChecked = () => {
1559
- const fromProps = defaultValues?.[fieldName];
1560
- const fromField = field2.defaultValue;
1561
- if (fromProps !== void 0 && fromProps !== null) {
1562
- return typeof fromProps === "boolean" ? fromProps : false;
1563
- }
1564
- if (fromField !== void 0 && fromField !== null) {
1565
- return typeof fromField === "boolean" ? fromField : false;
1566
- }
1567
- return false;
1568
- };
1569
- const [value, setValue] = React19.useState(getDefaultValue);
1570
- const [checked, setChecked] = React19.useState(getDefaultChecked);
1571
- React19.useEffect(() => {
1572
- const newDefaultValue = defaultValues?.[fieldName];
1573
- if (newDefaultValue !== void 0 && newDefaultValue !== null) {
1574
- if (field2.type === "checkbox") {
1575
- setChecked(
1576
- typeof newDefaultValue === "boolean" ? newDefaultValue : false
1577
- );
1578
- } else {
1579
- setValue(
1580
- typeof newDefaultValue === "string" ? newDefaultValue : String(newDefaultValue)
1581
- );
1582
- }
1583
- }
1584
- }, [defaultValues, fieldName, field2.type]);
1585
- React19.useEffect(() => {
1586
- const hiddenInput = document.querySelector(
1587
- `input[type="hidden"][name="${fieldName}"]`
1588
- );
1589
- if (hiddenInput) {
1590
- if (field2.type === "checkbox") {
1591
- hiddenInput.value = checked ? "on" : "";
1592
- } else {
1593
- hiddenInput.value = value;
1594
- }
1595
- }
1596
- }, [value, checked, fieldName, field2.type]);
1597
- switch (field2.type) {
1598
- case "input": {
1599
- const inputType = field2.inputProps?.type || "text";
1600
- return /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement("input", { type: "hidden", name: fieldName, value }), /* @__PURE__ */ React19.createElement(
1601
- Input,
1602
- {
1603
- ...field2.inputProps,
1604
- "data-field-name": fieldName,
1605
- type: inputType,
1606
- label: field2.label,
1607
- description: field2.description,
1608
- isDisabled: field2.isDisabled,
1609
- isInvalid: Boolean(errorMessage),
1610
- errorMessage,
1611
- value,
1612
- onValueChange: setValue
1613
- }
1614
- ));
1615
- }
1616
- case "textarea": {
1617
- return /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement("input", { type: "hidden", name: fieldName, value }), /* @__PURE__ */ React19.createElement(
1618
- Textarea,
1619
- {
1620
- ...field2.textareaProps,
1621
- "data-field-name": fieldName,
1622
- label: field2.label,
1623
- description: field2.description,
1624
- isDisabled: field2.isDisabled,
1625
- isInvalid: Boolean(errorMessage),
1626
- errorMessage,
1627
- value,
1628
- onValueChange: setValue
1629
- }
1630
- ));
1631
- }
1632
- case "checkbox": {
1633
- return /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement("input", { type: "hidden", name: fieldName, value: checked ? "on" : "" }), /* @__PURE__ */ React19.createElement(
1634
- Checkbox,
1635
- {
1636
- ...field2.checkboxProps,
1637
- "data-field-name": fieldName,
1638
- isDisabled: field2.isDisabled,
1639
- isSelected: checked,
1640
- onValueChange: setChecked,
1641
- isInvalid: Boolean(errorMessage),
1642
- errorMessage
1643
- },
1644
- field2.label
1645
- ));
1646
- }
1647
- case "select": {
1648
- const options = field2.options || [];
1649
- return /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement("input", { type: "hidden", name: fieldName, value }), /* @__PURE__ */ React19.createElement(
1650
- Select,
1651
- {
1652
- ...field2.selectProps,
1653
- "data-field-name": fieldName,
1654
- label: field2.label,
1655
- description: field2.description,
1656
- isDisabled: field2.isDisabled,
1657
- isInvalid: Boolean(errorMessage),
1658
- errorMessage,
1659
- selectedKeys: value ? [value] : [],
1660
- onSelectionChange: (keys) => {
1661
- const selectedValue = Array.from(keys)[0];
1662
- setValue(selectedValue || "");
1663
- }
1664
- },
1665
- options.map(
1666
- (option) => /* @__PURE__ */ React19.createElement(SelectItem, { key: String(option.value) }, option.label)
1667
- )
1668
- ));
1669
- }
1670
- default:
1671
- return /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement("input", { type: "hidden", name: fieldName, value }), /* @__PURE__ */ React19.createElement(
1672
- Input,
1673
- {
1674
- "data-field-name": fieldName,
1675
- label: field2.label,
1676
- description: field2.description,
1677
- isDisabled: field2.isDisabled,
1678
- isInvalid: Boolean(errorMessage),
1679
- errorMessage,
1680
- value,
1681
- onValueChange: setValue
1682
- }
1683
- ));
1684
- }
1685
- }
1686
-
1687
- // src/hooks/useHeroForm.ts
1688
- import { useFormContext as useFormContext4 } from "react-hook-form";
1689
- function useHeroForm() {
1690
- const form = useFormContext4();
1691
- const defaults = useHeroHookFormDefaults();
1692
- return {
1693
- // All React Hook Form methods and state
1694
- ...form,
1695
- // Hero Hook Form styling defaults
1696
- defaults
1697
- };
1698
- }
1699
-
1700
- // src/providers/FormProvider.tsx
1701
- import React20 from "react";
1702
- import { FormProvider as RHFProvider } from "react-hook-form";
1703
- function FormProvider(props) {
1704
- return /* @__PURE__ */ React20.createElement(RHFProvider, { ...props.methods }, /* @__PURE__ */ React20.createElement(
1705
- "form",
1706
- {
1707
- className: props.className,
1708
- id: props.id,
1709
- noValidate: props.noValidate,
1710
- onSubmit: (event) => void props.methods.handleSubmit(props.onSubmit)(event)
1711
- },
1712
- props.children
1713
- ));
1714
- }
1715
-
1716
- // src/submit/SubmitButton.tsx
1717
- import React21 from "react";
1718
- function SubmitButton(props) {
1719
- const ctx = useFormContext5();
1720
- const loading = props.isLoading ?? ctx.formState.isSubmitting;
1721
- const enhancedState = props.enhancedState;
1722
- const isDisabledFromProps = props.buttonProps?.isDisabled ?? false;
1723
- const isDisabled = Boolean(isDisabledFromProps) || Boolean(loading);
1724
- const defaults = useHeroHookFormDefaults();
1725
- const getButtonContent = () => {
1726
- if (enhancedState?.isSuccess) {
1727
- return /* @__PURE__ */ React21.createElement("span", { className: "inline-flex items-center gap-2" }, "\u2705", props.successText || "Success!");
1728
- }
1729
- if (loading) {
1730
- return /* @__PURE__ */ React21.createElement("span", { className: "inline-flex items-center gap-2" }, "\u23F3", props.loadingText || "Submitting...");
1731
- }
1732
- return props.children;
1733
- };
1734
- const getButtonColor = () => {
1735
- if (enhancedState?.isSuccess) {
1736
- return "success";
1737
- }
1738
- if (enhancedState?.isError) {
1739
- return "danger";
1740
- }
1741
- return props.buttonProps?.color || defaults.submitButton.color;
1742
- };
1743
- return /* @__PURE__ */ React21.createElement(
1744
- Button,
1745
- {
1746
- type: "submit",
1747
- ...defaults.submitButton,
1748
- ...props.buttonProps,
1749
- isDisabled,
1750
- color: getButtonColor()
1751
- },
1752
- getButtonContent()
1753
- );
1754
- }
1755
-
1756
- // src/utils/applyServerErrors.ts
1757
- function applyServerErrors(setError, serverError) {
1758
- if (!serverError.fieldErrors?.length) return;
1759
- for (const err of serverError.fieldErrors) {
1760
- setError(err.path, { message: err.message, type: err.type });
1761
- }
1762
- }
1763
-
1764
- // src/utils/testing.ts
1765
- function createFormTestUtils(form) {
1766
- return {
1767
- /**
1768
- * Get a field by name
1769
- */
1770
- getField: (name) => {
1771
- return {
1772
- error: form.formState.errors[name],
1773
- isDirty: !!form.formState.dirtyFields[name],
1774
- isTouched: !!form.formState.touchedFields[name],
1775
- value: form.getValues(name)
1776
- };
1777
- },
1778
- /**
1779
- * Get the current form state
1780
- */
1781
- getFormState: () => ({
1782
- errors: form.formState.errors,
1783
- isSubmitted: form.formState.isSubmitted,
1784
- isSubmitting: form.formState.isSubmitting,
1785
- isSuccess: form.formState.isSubmitSuccessful,
1786
- values: form.getValues()
1787
- }),
1788
- /**
1789
- * Reset the form
1790
- */
1791
- resetForm: () => {
1792
- form.reset();
1793
- },
1794
- /**
1795
- * Set a field value
1796
- */
1797
- setFieldValue: (name, value) => {
1798
- form.setValue(name, value, { shouldValidate: true });
1799
- },
1800
- /**
1801
- * Submit the form
1802
- */
1803
- submitForm: async () => {
1804
- const isValid = await form.trigger();
1805
- if (isValid) {
1806
- await form.handleSubmit(() => {
1807
- })();
1808
- }
1809
- },
1810
- /**
1811
- * Trigger validation for a field or all fields
1812
- */
1813
- triggerValidation: async (name) => {
1814
- if (name) {
1815
- return await form.trigger(name);
1816
- }
1817
- return await form.trigger();
1818
- }
1819
- };
1820
- }
1821
- function createMockFormData(overrides = {}) {
1822
- return {
1823
- agreeToTerms: true,
1824
- confirmPassword: "password123",
1825
- email: "test@example.com",
1826
- firstName: "John",
1827
- lastName: "Doe",
1828
- password: "password123",
1829
- phone: "123-456-7890",
1830
- ...overrides
1831
- };
1832
- }
1833
- function createMockFormErrors(overrides = {}) {
1834
- return {
1835
- email: { message: "Invalid email address", type: "pattern" },
1836
- password: { message: "Password is too short", type: "minLength" },
1837
- ...overrides
1838
- };
1839
- }
1840
- function waitForFormState(form, condition, timeout = 5e3) {
1841
- return new Promise((resolve, reject) => {
1842
- const startTime = Date.now();
1843
- const checkState = () => {
1844
- if (condition(form.formState)) {
1845
- resolve();
1846
- return;
1847
- }
1848
- if (Date.now() - startTime > timeout) {
1849
- reject(new Error("Timeout waiting for form state"));
1850
- return;
1851
- }
1852
- setTimeout(checkState, 100);
1853
- };
1854
- checkState();
1855
- });
1856
- }
1857
- function simulateFieldInput(form, name, value) {
1858
- form.setValue(name, value);
1859
- void form.trigger(name);
1860
- }
1861
- function simulateFormSubmission(form, onSubmit) {
1862
- return form.handleSubmit(onSubmit)();
1863
- }
1864
- function hasFormErrors(form) {
1865
- return Object.keys(form.formState.errors).length > 0;
1866
- }
1867
- function getFormErrors(form) {
1868
- return Object.values(form.formState.errors).map(
1869
- (error) => error.message
1870
- );
1871
- }
1872
- function hasFieldError(form, name) {
1873
- return !!form.formState.errors[name];
1874
- }
1875
- function getFieldError(form, name) {
1876
- const error = form.formState.errors[name];
1877
- return error?.message;
1878
- }
1879
-
1880
- // src/utils/validation.ts
1881
- import { z } from "zod";
1882
- var createMinLengthSchema = (min, fieldName) => z.string().min(min, `${fieldName} must be at least ${min} characters`);
1883
- var createMaxLengthSchema = (max, fieldName) => z.string().max(max, `${fieldName} must be no more than ${max} characters`);
1884
- var createEmailSchema = () => z.email("Please enter a valid email address");
1885
- var createRequiredSchema = (fieldName) => z.string().min(1, `${fieldName} is required`);
1886
- var createUrlSchema = () => z.string().url("Please enter a valid URL");
1887
- var createPhoneSchema = () => z.string().regex(/^[+]?[1-9][\d]{0,15}$/, "Please enter a valid phone number");
1888
- var createPasswordSchema = (minLength = 8) => z.string().min(minLength, `Password must be at least ${minLength} characters`).regex(
1889
- /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/,
1890
- "Password must contain at least one uppercase letter, one lowercase letter, and one number"
1891
- );
1892
- var createNumberRangeSchema = (min, max, fieldName) => z.number().min(min, `${fieldName} must be at least ${min}`).max(max, `${fieldName} must be no more than ${max}`);
1893
- var createDateSchema = (fieldName) => z.date({ message: `${fieldName} is required` });
1894
- var createFutureDateSchema = (fieldName) => z.date({ message: `${fieldName} is required` }).refine((date) => date > /* @__PURE__ */ new Date(), {
1895
- message: `${fieldName} must be in the future`
1896
- });
1897
- var createPastDateSchema = (fieldName) => z.date({ message: `${fieldName} is required` }).refine((date) => date < /* @__PURE__ */ new Date(), {
1898
- message: `${fieldName} must be in the past`
1899
- });
1900
- var createFileSchema = (maxSizeInMB = 5, allowedTypes = ["image/jpeg", "image/png", "image/gif"]) => z.instanceof(File).refine(
1901
- (file) => file.size <= maxSizeInMB * 1024 * 1024,
1902
- `File size must be less than ${maxSizeInMB}MB`
1903
- ).refine(
1904
- (file) => allowedTypes.includes(file.type),
1905
- `File type must be one of: ${allowedTypes.join(", ")}`
1906
- );
1907
- var createRequiredCheckboxSchema = (fieldName) => z.boolean().refine((val) => val === true, {
1908
- message: `You must agree to ${fieldName}`
1909
- });
1910
- var crossFieldValidation = {
1911
- /**
1912
- * Conditional required field validation
1913
- */
1914
- conditionalRequired: (field2, conditionField, conditionValue) => {
1915
- return z.object({
1916
- [conditionField]: z.any(),
1917
- [field2]: z.string()
1918
- }).refine(
1919
- (data) => {
1920
- if (data[conditionField] === conditionValue) {
1921
- return data[field2] && data[field2].trim().length > 0;
1922
- }
1923
- return true;
1924
- },
1925
- {
1926
- message: "This field is required",
1927
- path: [field2]
1928
- }
1929
- );
1930
- },
1931
- /**
1932
- * Date range validation
1933
- */
1934
- dateRange: (startField, endField) => {
1935
- return z.object({
1936
- [endField]: z.string(),
1937
- [startField]: z.string()
1938
- }).refine(
1939
- (data) => {
1940
- const startDate = new Date(data[startField]);
1941
- const endDate = new Date(data[endField]);
1942
- return startDate < endDate;
1943
- },
1944
- {
1945
- message: "End date must be after start date",
1946
- path: [endField]
1947
- }
1948
- );
1949
- },
1950
- /**
1951
- * Password confirmation validation
1952
- */
1953
- passwordConfirmation: (passwordField, confirmField) => {
1954
- return z.object({
1955
- [confirmField]: z.string(),
1956
- [passwordField]: z.string()
1957
- }).refine((data) => data[passwordField] === data[confirmField], {
1958
- message: "Passwords do not match",
1959
- path: [confirmField]
1960
- });
1961
- }
1962
- };
1963
- var commonValidations = {
1964
- confirmPassword: (passwordField, confirmField) => crossFieldValidation.passwordConfirmation(passwordField, confirmField),
1965
- date: (fieldName) => createDateSchema(fieldName),
1966
- email: createEmailSchema(),
1967
- file: (maxSizeInMB, allowedTypes) => createFileSchema(maxSizeInMB, allowedTypes),
1968
- futureDate: (fieldName) => createFutureDateSchema(fieldName),
1969
- maxLength: (max, fieldName) => createMaxLengthSchema(max, fieldName),
1970
- minLength: (min, fieldName) => createMinLengthSchema(min, fieldName),
1971
- numberRange: (min, max, fieldName) => createNumberRangeSchema(min, max, fieldName),
1972
- password: (minLength) => createPasswordSchema(minLength),
1973
- pastDate: (fieldName) => createPastDateSchema(fieldName),
1974
- phone: createPhoneSchema(),
1975
- required: (fieldName) => createRequiredSchema(fieldName),
1976
- requiredCheckbox: (fieldName) => createRequiredCheckboxSchema(fieldName),
1977
- url: createUrlSchema()
1978
- };
1979
-
1980
- // src/index.ts
1981
- import { useFormContext as useFormContext5 } from "react-hook-form";
1982
-
1983
- // src/components/ZodForm.tsx
1984
- import React23 from "react";
1985
- import { Button as Button5 } from "@heroui/react";
1986
- import {
1987
- FormProvider as FormProvider2
1988
- } from "react-hook-form";
1989
-
1990
- // src/zod-integration.ts
1991
- import { useForm as useForm2 } from "react-hook-form";
1992
- import { z as z2 } from "zod";
1993
- function createZodResolver(schema) {
1994
- return async (values) => {
1995
- try {
1996
- const result = await schema.parseAsync(values);
1997
- return {
1998
- errors: {},
1999
- values: result
2000
- };
2001
- } catch (error) {
2002
- if (error instanceof z2.ZodError) {
2003
- const errors = {};
2004
- error.issues.forEach((err) => {
2005
- const path = err.path.join(".");
2006
- errors[path] = { message: err.message };
2007
- });
2008
- return {
2009
- errors,
2010
- values: {}
2011
- };
2012
- }
2013
- throw error;
2014
- }
2015
- };
2016
- }
2017
- function useZodForm(config) {
2018
- if (!config.resolver && config.schema) {
2019
- config.resolver = createZodResolver(config.schema);
2020
- }
2021
- return useForm2(config);
2022
- }
2023
- function createZodFormConfig(schema, fields, defaultValues) {
2024
- return {
2025
- fields,
2026
- schema,
2027
- ...defaultValues && {
2028
- defaultValues
2029
- }
2030
- };
2031
- }
2032
-
2033
- // src/hooks/useEnhancedFormState.ts
2034
- import { useCallback, useEffect, useState as useState2 } from "react";
2035
- function useEnhancedFormState(form, options = {}) {
2036
- const {
2037
- autoReset = true,
2038
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2039
- errorMessage: _errorMessage = "An error occurred. Please try again.",
2040
- onError,
2041
- onSuccess,
2042
- resetDelay = 3e3,
2043
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2044
- successMessage: _successMessage = "Form submitted successfully!"
2045
- } = options;
2046
- const [status, setStatus] = useState2("idle");
2047
- const [error, setError] = useState2(void 0);
2048
- const [submittedData, setSubmittedData] = useState2(void 0);
2049
- const { formState, getValues: _getValues } = form;
2050
- const { dirtyFields, errors, isSubmitting, touchedFields } = formState;
2051
- useEffect(() => {
2052
- if (isSubmitting) {
2053
- setStatus("submitting");
2054
- }
2055
- }, [isSubmitting]);
2056
- useEffect(() => {
2057
- if (status === "success" && autoReset) {
2058
- const timer = setTimeout(() => {
2059
- setStatus("idle");
2060
- setSubmittedData(void 0);
2061
- setError(void 0);
2062
- }, resetDelay);
2063
- return () => clearTimeout(timer);
2064
- }
2065
- }, [status, autoReset, resetDelay]);
2066
- const handleSuccess = useCallback(
2067
- (data) => {
2068
- setStatus("success");
2069
- setSubmittedData(data);
2070
- setError(void 0);
2071
- onSuccess?.(data);
2072
- },
2073
- [onSuccess]
2074
- );
2075
- const handleError = useCallback(
2076
- (errorMessage) => {
2077
- setStatus("error");
2078
- setError(errorMessage);
2079
- setSubmittedData(void 0);
2080
- onError?.(errorMessage);
2081
- },
2082
- [onError]
2083
- );
2084
- const reset = useCallback(() => {
2085
- setStatus("idle");
2086
- setError(void 0);
2087
- setSubmittedData(void 0);
2088
- }, []);
2089
- return {
2090
- dirtyFields: new Set(Object.keys(dirtyFields)),
2091
- error,
2092
- errorCount: Object.keys(errors).length,
2093
- handleError,
2094
- handleSuccess,
2095
- hasErrors: Object.keys(errors).length > 0,
2096
- isError: status === "error",
2097
- isSubmitting,
2098
- isSuccess: status === "success",
2099
- reset,
2100
- status,
2101
- submittedData,
2102
- touchedFields: new Set(Object.keys(touchedFields))
2103
- };
2104
- }
2105
-
2106
- // src/components/FormStatus.tsx
2107
- import React22 from "react";
2108
- import { Button as Button4 } from "@heroui/react";
2109
- function FormStatus({
2110
- className = "",
2111
- onDismiss,
2112
- showDetails = false,
2113
- state
2114
- }) {
2115
- const { error, isError, isSubmitting, isSuccess, status, submittedData } = state;
2116
- if (status === "idle") {
2117
- return null;
2118
- }
2119
- if (isSubmitting) {
2120
- return /* @__PURE__ */ React22.createElement(
2121
- "div",
2122
- {
2123
- className: `flex items-center gap-3 p-4 bg-blue-50 border border-blue-200 rounded-lg ${className}`
2124
- },
2125
- /* @__PURE__ */ React22.createElement("span", { className: "text-blue-600" }, "\u23F3"),
2126
- /* @__PURE__ */ React22.createElement("div", null, /* @__PURE__ */ React22.createElement("p", { className: "text-sm font-medium text-blue-900" }, "Submitting form..."), showDetails && /* @__PURE__ */ React22.createElement("p", { className: "text-xs text-blue-700" }, "Please wait while we process your request."))
2127
- );
2128
- }
2129
- if (isSuccess) {
2130
- return /* @__PURE__ */ React22.createElement(
2131
- "div",
2132
- {
2133
- className: `flex items-center gap-3 p-4 bg-green-50 border border-green-200 rounded-lg ${className}`,
2134
- "data-testid": "success-message"
2135
- },
2136
- /* @__PURE__ */ React22.createElement("span", { className: "text-green-600" }, "\u2705"),
2137
- /* @__PURE__ */ React22.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React22.createElement("p", { className: "text-sm font-medium text-green-900" }, "Form submitted successfully!"), showDetails && submittedData && /* @__PURE__ */ React22.createElement("p", { className: "text-xs text-green-700" }, "Your data has been saved. Thank you for your submission.")),
2138
- onDismiss && /* @__PURE__ */ React22.createElement(
2139
- Button4,
2140
- {
2141
- size: "sm",
2142
- variant: "light",
2143
- isIconOnly: true,
2144
- onPress: onDismiss,
2145
- "aria-label": "Dismiss success message"
2146
- },
2147
- "\u2715"
2148
- )
2149
- );
2150
- }
2151
- if (isError && error) {
2152
- return /* @__PURE__ */ React22.createElement(
2153
- "div",
2154
- {
2155
- className: `flex items-center gap-3 p-4 bg-red-50 border border-red-200 rounded-lg ${className}`,
2156
- "data-testid": "error-message"
2157
- },
2158
- /* @__PURE__ */ React22.createElement("span", { className: "text-red-600" }, "\u26A0\uFE0F"),
2159
- /* @__PURE__ */ React22.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React22.createElement("p", { className: "text-sm font-medium text-red-900" }, "Error submitting form"), /* @__PURE__ */ React22.createElement("p", { className: "text-xs text-red-700" }, error)),
2160
- onDismiss && /* @__PURE__ */ React22.createElement(
2161
- Button4,
2162
- {
2163
- size: "sm",
2164
- variant: "light",
2165
- isIconOnly: true,
2166
- onPress: onDismiss,
2167
- "aria-label": "Dismiss error message"
2168
- },
2169
- "\u2715"
2170
- )
2171
- );
2172
- }
2173
- return null;
2174
- }
2175
- function FormToast({
2176
- duration = 5e3,
2177
- onDismiss,
2178
- position = "top-right",
2179
- state
2180
- }) {
2181
- const [isVisible, setIsVisible] = React22.useState(false);
2182
- React22.useEffect(() => {
2183
- if (state.isSuccess || state.isError) {
2184
- setIsVisible(true);
2185
- if (duration > 0) {
2186
- const timer = setTimeout(() => {
2187
- setIsVisible(false);
2188
- onDismiss?.();
2189
- }, duration);
2190
- return () => clearTimeout(timer);
2191
- }
2192
- }
2193
- }, [state.isSuccess, state.isError, duration, onDismiss]);
2194
- if (!isVisible) {
2195
- return null;
2196
- }
2197
- const positionClasses = {
2198
- "bottom-left": "bottom-4 left-4",
2199
- "bottom-right": "bottom-4 right-4",
2200
- "top-left": "top-4 left-4",
2201
- "top-right": "top-4 right-4"
2202
- };
2203
- return /* @__PURE__ */ React22.createElement("div", { className: `fixed z-50 ${positionClasses[position]}` }, /* @__PURE__ */ React22.createElement(FormStatus, { state, onDismiss }));
2204
- }
2205
-
2206
- // src/components/ZodForm.tsx
2207
- function ZodForm({
2208
- className,
2209
- columns = 1,
2210
- config,
2211
- layout = "vertical",
2212
- onError,
2213
- onSubmit,
2214
- onSuccess,
2215
- resetButtonText = "Reset",
2216
- showResetButton = false,
2217
- spacing = "4",
2218
- submitButtonProps = {},
2219
- submitButtonText = "Submit",
2220
- subtitle,
2221
- title
2222
- }) {
2223
- const form = useZodForm(config);
2224
- const enhancedState = useEnhancedFormState(form, {
2225
- autoReset: true,
2226
- onError: (error) => onError?.({ field: "form", message: error }),
2227
- onSuccess,
2228
- resetDelay: 3e3
2229
- });
2230
- const handleSubmit = async () => {
2231
- try {
2232
- await form.handleSubmit(
2233
- async (formData) => {
2234
- await onSubmit(formData);
2235
- enhancedState.handleSuccess(formData);
2236
- },
2237
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2238
- (_errors) => {
2239
- enhancedState.handleError("Please fix the validation errors above");
2240
- }
2241
- )();
2242
- } catch (error) {
2243
- const errorMessage = error instanceof Error ? error.message : "An error occurred";
2244
- enhancedState.handleError(errorMessage);
2245
- }
2246
- };
2247
- const resetForm = () => {
2248
- form.reset();
2249
- enhancedState.reset();
2250
- };
2251
- const renderFields = () => {
2252
- if (layout === "grid") {
2253
- return /* @__PURE__ */ React23.createElement(
2254
- "div",
2255
- {
2256
- className: `grid gap-${spacing} ${columns === 1 ? "grid-cols-1" : columns === 2 ? "grid-cols-1 md:grid-cols-2" : "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"}`
2257
- },
2258
- config.fields.map((field2) => /* @__PURE__ */ React23.createElement(
2259
- FormField,
2260
- {
2261
- key: field2.name,
2262
- config: field2,
2263
- form,
2264
- submissionState: {
2265
- error: enhancedState.error,
2266
- isSubmitted: enhancedState.status !== "idle",
2267
- isSubmitting: enhancedState.isSubmitting,
2268
- isSuccess: enhancedState.isSuccess
2269
- }
2270
- }
2271
- ))
2272
- );
2273
- }
2274
- if (layout === "horizontal") {
2275
- return /* @__PURE__ */ React23.createElement("div", { className: `grid gap-${spacing} grid-cols-1 md:grid-cols-2` }, config.fields.map((field2) => /* @__PURE__ */ React23.createElement(
2276
- FormField,
2277
- {
2278
- key: field2.name,
2279
- config: field2,
2280
- form,
2281
- submissionState: {
2282
- error: enhancedState.error,
2283
- isSubmitted: enhancedState.status !== "idle",
2284
- isSubmitting: enhancedState.isSubmitting,
2285
- isSuccess: enhancedState.isSuccess
2286
- }
2287
- }
2288
- )));
2289
- }
2290
- return /* @__PURE__ */ React23.createElement("div", { className: `space-y-${spacing}` }, config.fields.map((field2) => /* @__PURE__ */ React23.createElement(
2291
- FormField,
2292
- {
2293
- key: field2.name,
2294
- config: field2,
2295
- form,
2296
- submissionState: {
2297
- error: enhancedState.error,
2298
- isSubmitted: enhancedState.status !== "idle",
2299
- isSubmitting: enhancedState.isSubmitting,
2300
- isSuccess: enhancedState.isSuccess
2301
- }
2302
- }
2303
- )));
2304
- };
2305
- const handleFormSubmit = (e) => {
2306
- e.preventDefault();
2307
- void handleSubmit();
2308
- };
2309
- React23.useEffect(() => {
2310
- if (config.onError && Object.keys(form.formState.errors).length > 0) {
2311
- config.onError(form.formState.errors);
2312
- }
2313
- }, [form.formState.errors, config.onError]);
2314
- return /* @__PURE__ */ React23.createElement(FormProvider2, { ...form }, /* @__PURE__ */ React23.createElement("form", { className, role: "form", onSubmit: handleFormSubmit }, title && /* @__PURE__ */ React23.createElement("div", { className: "mb-6" }, /* @__PURE__ */ React23.createElement("h2", { className: "text-xl font-semibold text-foreground mb-2" }, title), subtitle && /* @__PURE__ */ React23.createElement("p", { className: "text-sm text-muted-foreground" }, subtitle)), /* @__PURE__ */ React23.createElement(
2315
- FormStatus,
2316
- {
2317
- state: enhancedState,
2318
- onDismiss: () => enhancedState.reset(),
2319
- showDetails: true
2320
- }
2321
- ), renderFields(), /* @__PURE__ */ React23.createElement("div", { className: "mt-6 flex gap-3 justify-end" }, /* @__PURE__ */ React23.createElement(
2322
- Button5,
2323
- {
2324
- color: "primary",
2325
- isDisabled: enhancedState.isSubmitting,
2326
- isLoading: enhancedState.isSubmitting,
2327
- type: "submit",
2328
- ...submitButtonProps
2329
- },
2330
- enhancedState.isSuccess ? "Success!" : submitButtonText
2331
- ), showResetButton && /* @__PURE__ */ React23.createElement(
2332
- Button5,
2333
- {
2334
- isDisabled: enhancedState.isSubmitting,
2335
- type: "button",
2336
- variant: "bordered",
2337
- onPress: resetForm
2338
- },
2339
- resetButtonText
2340
- ))));
2341
- }
2342
-
2343
- // src/builders/BasicFormBuilder.ts
2344
- var BasicFormBuilder = class {
2345
- constructor() {
2346
- this.fields = [];
2347
- }
2348
- /**
2349
- * Add an input field
2350
- */
2351
- input(name, label, type = "text") {
2352
- this.fields.push({
2353
- inputProps: { type },
2354
- label,
2355
- name,
2356
- type: "input"
2357
- });
2358
- return this;
2359
- }
2360
- /**
2361
- * Add a textarea field
2362
- */
2363
- textarea(name, label, placeholder) {
2364
- this.fields.push({
2365
- label,
2366
- name,
2367
- textareaProps: { placeholder },
2368
- type: "textarea"
2369
- });
2370
- return this;
2371
- }
2372
- /**
2373
- * Add a select field
2374
- */
2375
- select(name, label, options) {
2376
- this.fields.push({
2377
- label,
2378
- name,
2379
- options,
2380
- type: "select"
2381
- });
2382
- return this;
2383
- }
2384
- /**
2385
- * Add an autocomplete field
2386
- */
2387
- autocomplete(name, label, items, placeholder) {
2388
- this.fields.push({
2389
- autocompleteProps: placeholder ? { placeholder } : void 0,
2390
- label,
2391
- name,
2392
- options: items,
2393
- type: "autocomplete"
2394
- });
2395
- return this;
2396
- }
2397
- /**
2398
- * Add a checkbox field
2399
- */
2400
- checkbox(name, label) {
2401
- this.fields.push({
2402
- label,
2403
- name,
2404
- type: "checkbox"
2405
- });
2406
- return this;
2407
- }
2408
- /**
2409
- * Add a content field for headers, questions, or custom content between fields
2410
- */
2411
- content(title, description, options) {
2412
- this.fields.push({
2413
- className: options?.className,
2414
- description: description || void 0,
2415
- name: options?.name,
2416
- render: options?.render,
2417
- title: title || void 0,
2418
- type: "content"
2419
- });
2420
- return this;
2421
- }
2422
- /**
2423
- * Add a switch field
2424
- */
2425
- switch(name, label, description) {
2426
- this.fields.push({
2427
- description,
2428
- label,
2429
- name,
2430
- type: "switch"
2431
- });
2432
- return this;
2433
- }
2434
- /**
2435
- * Build the final field configuration array
2436
- */
2437
- build() {
2438
- return this.fields;
2439
- }
2440
- };
2441
- function createBasicFormBuilder() {
2442
- return new BasicFormBuilder();
2443
- }
2444
- var FormFieldHelpers = {
2445
- /**
2446
- * Create an autocomplete field
2447
- *
2448
- * @example
2449
- * ```tsx
2450
- * // Simple autocomplete
2451
- * FormFieldHelpers.autocomplete("country", "Country", options)
2452
- *
2453
- * // With placeholder
2454
- * FormFieldHelpers.autocomplete("country", "Country", options, "Search countries")
2455
- *
2456
- * // With full customization
2457
- * FormFieldHelpers.autocomplete("country", "Country", options, "Search countries", {
2458
- * classNames: { base: "custom-autocomplete" },
2459
- * allowsCustomValue: true
2460
- * })
2461
- * ```
2462
- */
2463
- autocomplete: (name, label, items, placeholder, autocompleteProps) => ({
2464
- autocompleteProps: {
2465
- ...placeholder && { placeholder },
2466
- ...autocompleteProps
2467
- },
2468
- label,
2469
- name,
2470
- options: items,
2471
- type: "autocomplete"
2472
- }),
2473
- /**
2474
- * Create a checkbox field
2475
- *
2476
- * @example
2477
- * ```tsx
2478
- * // Simple checkbox
2479
- * FormFieldHelpers.checkbox("newsletter", "Subscribe to newsletter")
2480
- *
2481
- * // With full customization
2482
- * FormFieldHelpers.checkbox("newsletter", "Subscribe to newsletter", {
2483
- * classNames: { base: "custom-checkbox" },
2484
- * size: "lg"
2485
- * })
2486
- * ```
2487
- */
2488
- checkbox: (name, label, checkboxProps) => ({
2489
- checkboxProps,
2490
- label,
2491
- name,
2492
- type: "checkbox"
2493
- }),
2494
- /**
2495
- * Create a conditional field that shows/hides based on form data
2496
- *
2497
- * @example
2498
- * ```tsx
2499
- * FormFieldHelpers.conditional(
2500
- * "phone",
2501
- * (values) => values.hasPhone === true,
2502
- * FormFieldHelpers.input("phone", "Phone Number", "tel")
2503
- * )
2504
- * ```
2505
- *
2506
- * @example
2507
- * With explicit type in condition function (similar to content helper pattern):
2508
- * ```tsx
2509
- * FormFieldHelpers.conditional(
2510
- * "options",
2511
- * (formData: Partial<z.infer<typeof fieldSchema>>) =>
2512
- * formData.fieldType === 'DROPDOWN',
2513
- * FormFieldHelpers.textarea("options", "Dropdown Options", "One per line")
2514
- * )
2515
- * ```
2516
- */
2517
- conditional: (name, condition, field2) => {
2518
- return {
2519
- condition,
2520
- field: field2,
2521
- name,
2522
- type: "conditional"
2523
- };
2524
- },
2525
- /**
2526
- * Create a content field for headers, questions, or custom content between fields
2527
- *
2528
- * @example
2529
- * ```tsx
2530
- * // Simple header
2531
- * FormFieldHelpers.content("Personal Information", "Please provide your details")
2532
- *
2533
- * // Custom render
2534
- * FormFieldHelpers.content(null, null, {
2535
- * render: () => <div>Custom content</div>
2536
- * })
2537
- * ```
2538
- */
2539
- content: (title, description, options) => {
2540
- return {
2541
- className: options?.className,
2542
- description: description || void 0,
2543
- name: options?.name,
2544
- render: options?.render,
2545
- title: title || void 0,
2546
- type: "content"
2547
- };
2548
- },
2549
- /**
2550
- * Create a date field
2551
- *
2552
- * @example
2553
- * ```tsx
2554
- * // Simple date field
2555
- * FormFieldHelpers.date("birthDate", "Birth Date")
2556
- *
2557
- * // With full customization
2558
- * FormFieldHelpers.date("birthDate", "Birth Date", {
2559
- * label: "Select your birth date",
2560
- * granularity: "day",
2561
- * minValue: new CalendarDate(1900, 1, 1)
2562
- * })
2563
- * ```
2564
- */
2565
- date: (name, label, dateProps) => ({
2566
- dateProps,
2567
- label,
2568
- name,
2569
- type: "date"
2570
- }),
2571
- /**
2572
- * Create a file upload field
2573
- *
2574
- * @example
2575
- * ```tsx
2576
- * // Simple file field
2577
- * FormFieldHelpers.file("avatar", "Profile Picture")
2578
- *
2579
- * // With accept and multiple
2580
- * FormFieldHelpers.file("avatar", "Profile Picture", {
2581
- * accept: "image/*",
2582
- * multiple: true
2583
- * })
2584
- *
2585
- * // With full customization
2586
- * FormFieldHelpers.file("avatar", "Profile Picture", {
2587
- * accept: "image/*",
2588
- * multiple: false,
2589
- * fileProps: { className: "custom-file-input" }
2590
- * })
2591
- * ```
2592
- */
2593
- file: (name, label, options) => ({
2594
- accept: options?.accept,
2595
- fileProps: options?.fileProps,
2596
- label,
2597
- multiple: options?.multiple,
2598
- name,
2599
- type: "file"
2600
- }),
2601
- /**
2602
- * Create a font picker field
2603
- *
2604
- * @example
2605
- * ```tsx
2606
- * // Simple font picker
2607
- * FormFieldHelpers.fontPicker("font", "Choose Font")
2608
- *
2609
- * // With full customization
2610
- * FormFieldHelpers.fontPicker("font", "Choose Font", {
2611
- * showFontPreview: true,
2612
- * loadAllVariants: false,
2613
- * fontsLoadedTimeout: 5000
2614
- * })
2615
- * ```
2616
- */
2617
- fontPicker: (name, label, fontPickerProps) => ({
2618
- fontPickerProps,
2619
- label,
2620
- name,
2621
- type: "fontPicker"
2622
- }),
2623
- /**
2624
- * Create an input field
2625
- *
2626
- * @example
2627
- * ```tsx
2628
- * // Simple input
2629
- * FormFieldHelpers.input("name", "Name")
2630
- *
2631
- * // With type
2632
- * FormFieldHelpers.input("email", "Email", "email")
2633
- *
2634
- * // With full customization
2635
- * FormFieldHelpers.input("email", "Email", "email", {
2636
- * placeholder: "Enter your email",
2637
- * classNames: { input: "custom-input" },
2638
- * startContent: <MailIcon />,
2639
- * description: "We'll never share your email"
2640
- * })
2641
- * ```
2642
- */
2643
- input: (name, label, type, inputProps) => ({
2644
- inputProps: {
2645
- type: type || "text",
2646
- ...inputProps
2647
- },
2648
- label,
2649
- name,
2650
- type: "input"
2651
- }),
2652
- /**
2653
- * Create a radio group field
2654
- *
2655
- * @example
2656
- * ```tsx
2657
- * // Simple radio group
2658
- * FormFieldHelpers.radio("gender", "Gender", [
2659
- * { label: "Male", value: "male" },
2660
- * { label: "Female", value: "female" }
2661
- * ])
2662
- *
2663
- * // With full customization
2664
- * FormFieldHelpers.radio("gender", "Gender", options, {
2665
- * orientation: "horizontal",
2666
- * classNames: { base: "custom-radio" }
2667
- * })
2668
- * ```
2669
- */
2670
- radio: (name, label, options, radioProps) => ({
2671
- label,
2672
- name,
2673
- radioOptions: options,
2674
- radioProps,
2675
- type: "radio"
2676
- }),
2677
- /**
2678
- * Create a select field
2679
- *
2680
- * @example
2681
- * ```tsx
2682
- * // Simple select
2683
- * FormFieldHelpers.select("country", "Country", options)
2684
- *
2685
- * // With full customization
2686
- * FormFieldHelpers.select("country", "Country", options, {
2687
- * placeholder: "Select a country",
2688
- * classNames: { trigger: "custom-select" },
2689
- * selectionMode: "multiple"
2690
- * })
2691
- * ```
2692
- */
2693
- select: (name, label, options, selectProps) => ({
2694
- label,
2695
- name,
2696
- options,
2697
- selectProps,
2698
- type: "select"
2699
- }),
2700
- /**
2701
- * Create a slider field
2702
- *
2703
- * @example
2704
- * ```tsx
2705
- * // Simple slider
2706
- * FormFieldHelpers.slider("rating", "Rating")
2707
- *
2708
- * // With full customization
2709
- * FormFieldHelpers.slider("rating", "Rating", {
2710
- * minValue: 1,
2711
- * maxValue: 5,
2712
- * step: 1,
2713
- * showSteps: true,
2714
- * classNames: { base: "custom-slider" }
2715
- * })
2716
- * ```
2717
- */
2718
- slider: (name, label, sliderProps) => ({
2719
- label,
2720
- name,
2721
- sliderProps,
2722
- type: "slider"
2723
- }),
2724
- /**
2725
- * Create a switch field
2726
- *
2727
- * @example
2728
- * ```tsx
2729
- * // Simple switch
2730
- * FormFieldHelpers.switch("notifications", "Enable notifications")
2731
- *
2732
- * // With description
2733
- * FormFieldHelpers.switch("notifications", "Enable notifications", "Receive email notifications")
2734
- *
2735
- * // With full customization
2736
- * FormFieldHelpers.switch("notifications", "Enable notifications", "Receive email notifications", {
2737
- * classNames: { base: "custom-switch" },
2738
- * size: "lg",
2739
- * color: "primary"
2740
- * })
2741
- * ```
2742
- */
2743
- switch: (name, label, description, switchProps) => ({
2744
- description,
2745
- label,
2746
- name,
2747
- switchProps,
2748
- type: "switch"
2749
- }),
2750
- /**
2751
- * Create a textarea field
2752
- *
2753
- * @example
2754
- * ```tsx
2755
- * // Simple textarea
2756
- * FormFieldHelpers.textarea("message", "Message")
2757
- *
2758
- * // With placeholder
2759
- * FormFieldHelpers.textarea("message", "Message", "Enter your message")
2760
- *
2761
- * // With full customization
2762
- * FormFieldHelpers.textarea("message", "Message", "Enter your message", {
2763
- * classNames: { input: "custom-textarea" },
2764
- * minRows: 3,
2765
- * maxRows: 10
2766
- * })
2767
- * ```
2768
- */
2769
- textarea: (name, label, placeholder, textareaProps) => ({
2770
- label,
2771
- name,
2772
- textareaProps: {
2773
- ...placeholder && { placeholder },
2774
- ...textareaProps
2775
- },
2776
- type: "textarea"
2777
- })
2778
- };
2779
- var CommonFields = {
2780
- /**
2781
- * Address fields
2782
- */
2783
- address: () => [
2784
- FormFieldHelpers.input("street", "Street Address"),
2785
- FormFieldHelpers.input("city", "City"),
2786
- FormFieldHelpers.input("state", "State/Province"),
2787
- FormFieldHelpers.input("zipCode", "ZIP/Postal Code"),
2788
- FormFieldHelpers.select("country", "Country", [
2789
- { label: "Select a country", value: "" },
2790
- { label: "United States", value: "us" },
2791
- { label: "Canada", value: "ca" },
2792
- { label: "United Kingdom", value: "uk" },
2793
- { label: "Australia", value: "au" },
2794
- { label: "Germany", value: "de" },
2795
- { label: "France", value: "fr" }
2796
- ])
2797
- ],
2798
- /**
2799
- * Personal information fields
2800
- */
2801
- personal: () => [
2802
- FormFieldHelpers.input("firstName", "First Name"),
2803
- FormFieldHelpers.input("lastName", "Last Name"),
2804
- FormFieldHelpers.input("email", "Email", "email"),
2805
- FormFieldHelpers.input("phone", "Phone", "tel")
2806
- ],
2807
- /**
2808
- * Terms and conditions fields
2809
- */
2810
- terms: () => [
2811
- FormFieldHelpers.checkbox(
2812
- "terms",
2813
- "I agree to the terms and conditions"
2814
- ),
2815
- FormFieldHelpers.checkbox(
2816
- "privacy",
2817
- "I agree to the privacy policy"
2818
- ),
2819
- FormFieldHelpers.checkbox(
2820
- "newsletter",
2821
- "Subscribe to newsletter"
2822
- )
2823
- ]
2824
- };
2825
-
2826
- // src/builders/AdvancedFormBuilder.ts
2827
- function inputField(name, label, props) {
2828
- return {
2829
- label,
2830
- name,
2831
- type: "input",
2832
- ...props && {
2833
- inputProps: {
2834
- className: props.className,
2835
- description: props.description,
2836
- disabled: props.isDisabled,
2837
- placeholder: props.placeholder,
2838
- type: props.type || "text"
2839
- }
2840
- }
2841
- };
2842
- }
2843
- function textareaField(name, label, props) {
2844
- return {
2845
- label,
2846
- name,
2847
- type: "textarea",
2848
- ...props && {
2849
- textareaProps: {
2850
- className: props.className,
2851
- description: props.description,
2852
- disabled: props.isDisabled,
2853
- placeholder: props.placeholder,
2854
- rows: props.rows
2855
- }
2856
- }
2857
- };
2858
- }
2859
- function selectField(name, label, options) {
2860
- return {
2861
- label,
2862
- name,
2863
- options,
2864
- type: "select"
2865
- };
2866
- }
2867
- function checkboxField(name, label, props) {
2868
- return {
2869
- label,
2870
- name,
2871
- type: "checkbox",
2872
- ...props && {
2873
- checkboxProps: {
2874
- className: props.className,
2875
- disabled: props.isDisabled
2876
- }
2877
- }
2878
- };
2879
- }
2880
- function switchField(name, label, props) {
2881
- return {
2882
- description: props?.description,
2883
- isDisabled: props?.isDisabled,
2884
- label,
2885
- name,
2886
- type: "switch",
2887
- ...props?.className && {
2888
- switchProps: {
2889
- className: props.className
2890
- }
2891
- }
2892
- };
2893
- }
2894
- function radioField(name, label, options, props) {
2895
- return {
2896
- label,
2897
- name,
2898
- radioOptions: options,
2899
- type: "radio",
2900
- ...props && {
2901
- radioProps: {
2902
- className: props.className,
2903
- isDisabled: props.isDisabled,
2904
- orientation: props.orientation
2905
- }
2906
- }
2907
- };
2908
- }
2909
- function sliderField(name, label, props) {
2910
- return {
2911
- label,
2912
- name,
2913
- type: "slider",
2914
- ...props && {
2915
- sliderProps: {
2916
- className: props.className,
2917
- maxValue: props.max ?? 100,
2918
- minValue: props.min ?? 0,
2919
- step: props.step ?? 1
2920
- }
2921
- }
2922
- };
2923
- }
2924
- function dateField(name, label, props) {
2925
- return {
2926
- label,
2927
- name,
2928
- type: "date",
2929
- ...props && {
2930
- dateProps: {
2931
- className: props.className,
2932
- placeholder: props.placeholder
2933
- }
2934
- }
2935
- };
2936
- }
2937
- function fileField(name, label, props) {
2938
- return {
2939
- label,
2940
- name,
2941
- type: "file",
2942
- ...props && {
2943
- fileProps: {
2944
- accept: props.accept || "",
2945
- className: props.className || "",
2946
- disabled: props.isDisabled || false,
2947
- multiple: props.multiple || false
2948
- }
2949
- }
2950
- };
2951
- }
2952
- function fontPickerField(name, label, props) {
2953
- return {
2954
- className: props?.className,
2955
- description: props?.description,
2956
- fontPickerProps: props?.fontPickerProps,
2957
- label,
2958
- name,
2959
- type: "fontPicker"
2960
- };
2961
- }
2962
- function contentField(title, description, options) {
2963
- return {
2964
- className: options?.className,
2965
- description: description || void 0,
2966
- name: options?.name,
2967
- render: options?.render,
2968
- title: title || void 0,
2969
- type: "content"
2970
- };
2971
- }
2972
- function createField(type, name, label, optionsOrProps, props) {
2973
- switch (type) {
2974
- case "input":
2975
- return inputField(name, label, optionsOrProps);
2976
- case "textarea":
2977
- return textareaField(name, label, optionsOrProps);
2978
- case "select":
2979
- return selectField(name, label, optionsOrProps);
2980
- case "checkbox":
2981
- return checkboxField(name, label, optionsOrProps);
2982
- case "switch":
2983
- return switchField(name, label, optionsOrProps);
2984
- case "radio":
2985
- return radioField(name, label, optionsOrProps, props);
2986
- case "slider":
2987
- return sliderField(name, label, optionsOrProps);
2988
- case "date":
2989
- return dateField(name, label, optionsOrProps);
2990
- case "file":
2991
- return fileField(name, label, optionsOrProps);
2992
- case "fontPicker":
2993
- return fontPickerField(name, label, optionsOrProps);
2994
- case "content":
2995
- if (typeof optionsOrProps === "string" || optionsOrProps === null) {
2996
- return contentField(optionsOrProps, props);
2997
- }
2998
- if (typeof optionsOrProps === "object" && optionsOrProps !== null) {
2999
- return contentField(
3000
- optionsOrProps.title,
3001
- optionsOrProps.description,
3002
- optionsOrProps
3003
- );
3004
- }
3005
- return contentField(
3006
- name,
3007
- label,
3008
- optionsOrProps
3009
- );
3010
- default:
3011
- throw new Error(`Unknown field type: ${type}`);
3012
- }
3013
- }
3014
- var AdvancedFieldBuilder = class {
3015
- constructor() {
3016
- this.fields = [];
3017
- }
3018
- field(type, name, label, optionsOrProps, props) {
3019
- this.fields.push(createField(type, name, label, optionsOrProps, props));
3020
- return this;
3021
- }
3022
- /**
3023
- * Add a conditional field that shows/hides based on form data
3024
- */
3025
- conditionalField(name, condition, field2) {
3026
- this.fields.push({
3027
- condition,
3028
- field: field2,
3029
- name,
3030
- type: "conditional"
3031
- });
3032
- return this;
3033
- }
3034
- /**
3035
- * Add a field array for dynamic repeating field groups
3036
- */
3037
- fieldArray(name, label, fields, options) {
3038
- this.fields.push({
3039
- addButtonText: options?.addButtonText,
3040
- fields,
3041
- label,
3042
- max: options?.max,
3043
- min: options?.min,
3044
- name,
3045
- removeButtonText: options?.removeButtonText,
3046
- type: "fieldArray"
3047
- });
3048
- return this;
3049
- }
3050
- /**
3051
- * Add a dynamic section that shows/hides based on form data
3052
- */
3053
- dynamicSection(name, condition, fields, options) {
3054
- this.fields.push({
3055
- condition,
3056
- description: options?.description,
3057
- fields,
3058
- name,
3059
- title: options?.title,
3060
- type: "dynamicSection"
3061
- });
3062
- return this;
3063
- }
3064
- /**
3065
- * Build the final field configuration array
3066
- */
3067
- build() {
3068
- return this.fields;
3069
- }
3070
- };
3071
- var FieldArrayItemBuilder = class {
3072
- constructor() {
3073
- this.fields = [];
3074
- }
3075
- field(type, name, label, optionsOrProps, props) {
3076
- this.fields.push(createField(type, name, label, optionsOrProps, props));
3077
- return this;
3078
- }
3079
- /**
3080
- * Build the field array item configuration
3081
- */
3082
- build() {
3083
- return this.fields;
3084
- }
3085
- };
3086
- function createFieldArrayItemBuilder() {
3087
- return new FieldArrayItemBuilder();
3088
- }
3089
- var FieldArrayBuilder = class {
3090
- constructor(arrayName) {
3091
- this.arrayName = arrayName;
3092
- this.fields = [];
3093
- }
3094
- field(type, name, label, optionsOrProps, props) {
3095
- const fullPath = `${this.arrayName}.${name}`;
3096
- const fieldConfig = createField(
3097
- type,
3098
- fullPath,
3099
- label,
3100
- optionsOrProps,
3101
- props
3102
- );
3103
- this.fields.push(fieldConfig);
3104
- return this;
3105
- }
3106
- build() {
3107
- return this.fields;
3108
- }
3109
- };
3110
- function createFieldArrayBuilder(arrayName) {
3111
- return new FieldArrayBuilder(arrayName);
3112
- }
3113
- function createAdvancedBuilder() {
3114
- return new AdvancedFieldBuilder();
3115
- }
3116
-
3117
- // src/builders/TypeInferredBuilder.ts
3118
- import { z as z3 } from "zod";
3119
- var TypeInferredBuilder = class {
3120
- constructor() {
3121
- this.schemaFields = {};
3122
- this.formFields = [];
3123
- }
3124
- /**
3125
- * Add a text field
3126
- */
3127
- text(name, label, options) {
3128
- const { maxLength, minLength, pattern, ...fieldOptions } = options || {};
3129
- let zodType = z3.string();
3130
- if (minLength)
3131
- zodType = zodType.min(
3132
- minLength,
3133
- `${label} must be at least ${minLength} characters`
3134
- );
3135
- if (maxLength)
3136
- zodType = zodType.max(
3137
- maxLength,
3138
- `${label} must be no more than ${maxLength} characters`
3139
- );
3140
- if (pattern)
3141
- zodType = zodType.regex(
3142
- new RegExp(pattern),
3143
- `${label} format is invalid`
3144
- );
3145
- this.schemaFields[name] = zodType;
3146
- this.formFields.push({
3147
- inputProps: { type: "text", ...fieldOptions },
3148
- label,
3149
- name,
3150
- type: "input"
3151
- });
3152
- return this;
3153
- }
3154
- /**
3155
- * Add an email field
3156
- */
3157
- email(name, label, options) {
3158
- this.schemaFields[name] = z3.string().email(`Please enter a valid email address`);
3159
- this.formFields.push({
3160
- inputProps: { type: "email", ...options },
3161
- label,
3162
- name,
3163
- type: "input"
3164
- });
3165
- return this;
3166
- }
3167
- /**
3168
- * Add a number field
3169
- */
3170
- number(name, label, options) {
3171
- const { max, min, step, ...fieldOptions } = options || {};
3172
- let zodType = z3.number();
3173
- if (min !== void 0)
3174
- zodType = zodType.min(min, `${label} must be at least ${min}`);
3175
- if (max !== void 0)
3176
- zodType = zodType.max(max, `${label} must be no more than ${max}`);
3177
- this.schemaFields[name] = zodType;
3178
- this.formFields.push({
3179
- inputProps: { max, min, step, type: "number", ...fieldOptions },
3180
- label,
3181
- name,
3182
- type: "input"
3183
- });
3184
- return this;
3185
- }
3186
- /**
3187
- * Add a textarea field
3188
- */
3189
- textarea(name, label, options) {
3190
- const { minLength, ...fieldOptions } = options || {};
3191
- let zodType = z3.string();
3192
- if (minLength)
3193
- zodType = zodType.min(
3194
- minLength,
3195
- `${label} must be at least ${minLength} characters`
3196
- );
3197
- this.schemaFields[name] = zodType;
3198
- this.formFields.push({
3199
- label,
3200
- name,
3201
- textareaProps: fieldOptions,
3202
- type: "textarea"
3203
- });
3204
- return this;
3205
- }
3206
- /**
3207
- * Add a select field
3208
- */
3209
- select(name, label, options) {
3210
- this.schemaFields[name] = z3.string().min(1, `Please select a ${label.toLowerCase()}`);
3211
- this.formFields.push({
3212
- label,
3213
- name,
3214
- options,
3215
- type: "select"
3216
- });
3217
- return this;
3218
- }
3219
- /**
3220
- * Add a checkbox field
3221
- */
3222
- checkbox(name, label, options) {
3223
- const { required = false, ...fieldOptions } = options || {};
3224
- let zodType = z3.boolean();
3225
- if (required) {
3226
- zodType = zodType.refine(
3227
- (val) => val === true,
3228
- `You must agree to ${label.toLowerCase()}`
3229
- );
3230
- }
3231
- this.schemaFields[name] = zodType;
3232
- this.formFields.push({
3233
- checkboxProps: fieldOptions,
3234
- label,
3235
- name,
3236
- type: "checkbox"
3237
- });
3238
- return this;
3239
- }
3240
- /**
3241
- * Add a switch field
3242
- */
3243
- switch(name, label, options) {
3244
- this.schemaFields[name] = z3.boolean().optional();
3245
- this.formFields.push({
3246
- label,
3247
- name,
3248
- switchProps: options,
3249
- type: "switch"
3250
- });
3251
- return this;
3252
- }
3253
- /**
3254
- * Add a radio field
3255
- */
3256
- radio(name, label, options, fieldOptions) {
3257
- this.schemaFields[name] = z3.string().min(1, `Please select a ${label.toLowerCase()}`);
3258
- this.formFields.push({
3259
- label,
3260
- name,
3261
- radioOptions: options,
3262
- radioProps: fieldOptions,
3263
- type: "radio"
3264
- });
3265
- return this;
3266
- }
3267
- /**
3268
- * Add a slider field
3269
- */
3270
- slider(name, label, options) {
3271
- const { max = 100, min = 0, step = 1, ...fieldOptions } = options || {};
3272
- let zodType = z3.number();
3273
- if (min !== void 0)
3274
- zodType = zodType.min(min, `${label} must be at least ${min}`);
3275
- if (max !== void 0)
3276
- zodType = zodType.max(max, `${label} must be no more than ${max}`);
3277
- this.schemaFields[name] = zodType;
3278
- this.formFields.push({
3279
- label,
3280
- name,
3281
- sliderProps: {
3282
- maxValue: max,
3283
- minValue: min,
3284
- step,
3285
- ...fieldOptions
3286
- },
3287
- type: "slider"
3288
- });
3289
- return this;
3290
- }
3291
- /**
3292
- * Add a date field
3293
- */
3294
- date(name, label, options) {
3295
- this.schemaFields[name] = z3.string().min(1, `${label} is required`);
3296
- this.formFields.push({
3297
- dateProps: options,
3298
- label,
3299
- name,
3300
- type: "date"
3301
- });
3302
- return this;
3303
- }
3304
- /**
3305
- * Add a file field
3306
- */
3307
- file(name, label, options) {
3308
- this.schemaFields[name] = z3.any().optional();
3309
- this.formFields.push({
3310
- fileProps: options,
3311
- label,
3312
- name,
3313
- type: "file"
3314
- });
3315
- return this;
3316
- }
3317
- /**
3318
- * Build the final schema and fields
3319
- */
3320
- build() {
3321
- return {
3322
- fields: this.formFields,
3323
- schema: z3.object(this.schemaFields)
3324
- };
3325
- }
3326
- };
3327
- function createTypeInferredBuilder() {
3328
- return new TypeInferredBuilder();
3329
- }
3330
- function defineInferredForm(fieldDefinitions) {
3331
- const builder = createTypeInferredBuilder();
3332
- fieldDefinitions(builder);
3333
- return builder.build();
3334
- }
3335
- var field = {
3336
- checkbox: (name, label, options) => {
3337
- const builder = new TypeInferredBuilder();
3338
- return builder.checkbox(name, label, options);
3339
- },
3340
- date: (name, label, options) => {
3341
- const builder = new TypeInferredBuilder();
3342
- return builder.date(name, label, options);
3343
- },
3344
- email: (name, label, options) => {
3345
- const builder = new TypeInferredBuilder();
3346
- return builder.email(name, label, options);
3347
- },
3348
- file: (name, label, options) => {
3349
- const builder = new TypeInferredBuilder();
3350
- return builder.file(name, label, options);
3351
- },
3352
- number: (name, label, options) => {
3353
- const builder = new TypeInferredBuilder();
3354
- return builder.number(name, label, options);
3355
- },
3356
- radio: (name, label, options, fieldOptions) => {
3357
- const builder = new TypeInferredBuilder();
3358
- return builder.radio(name, label, options, fieldOptions);
3359
- },
3360
- select: (name, label, options) => {
3361
- const builder = new TypeInferredBuilder();
3362
- return builder.select(name, label, options);
3363
- },
3364
- slider: (name, label, options) => {
3365
- const builder = new TypeInferredBuilder();
3366
- return builder.slider(name, label, options);
3367
- },
3368
- switch: (name, label, options) => {
3369
- const builder = new TypeInferredBuilder();
3370
- return builder.switch(name, label, options);
3371
- },
3372
- text: (name, label, options) => {
3373
- const builder = new TypeInferredBuilder();
3374
- return builder.text(name, label, options);
3375
- },
3376
- textarea: (name, label, options) => {
3377
- const builder = new TypeInferredBuilder();
3378
- return builder.textarea(name, label, options);
3379
- }
3380
- };
3381
-
3382
- // src/builders/NestedPathBuilder.ts
3383
- var NestedPathBuilder = class {
3384
- constructor() {
3385
- this.fields = [];
3386
- }
3387
- /**
3388
- * Create a nested object path builder
3389
- * Usage: builder.nest("address").field("street", "Street Address")
3390
- */
3391
- nest(path) {
3392
- return new NestedObjectBuilder(this, path);
3393
- }
3394
- /**
3395
- * Create a section-based path builder
3396
- * Usage: builder.section("shipping").field("street", "Street Address")
3397
- */
3398
- section(path) {
3399
- return new SectionBuilder(this, path);
3400
- }
3401
- /**
3402
- * Add a field with single path
3403
- * Usage: builder.field("firstName", "First Name")
3404
- */
3405
- field(name, label, type = "input", props) {
3406
- this.fields.push({
3407
- label,
3408
- name,
3409
- type,
3410
- ...props
3411
- });
3412
- return this;
3413
- }
3414
- /**
3415
- * Add a field with path segments
3416
- * Usage: builder.fieldPath(["user", "profile", "name"], "Full Name")
3417
- */
3418
- fieldPath(path, label, type = "input", props) {
3419
- const name = path.join(".");
3420
- this.fields.push({
3421
- label,
3422
- name,
3423
- type,
3424
- ...props
3425
- });
3426
- return this;
3427
- }
3428
- /**
3429
- * Add a field with template literal path
3430
- * Usage: builder.field`user.profile.name`("Full Name")
3431
- */
3432
- fieldTemplate(path) {
3433
- const pathString = path[0];
3434
- return new FieldTemplateBuilder(this, pathString);
3435
- }
3436
- /**
3437
- * Return to the parent builder (no-op for root builder)
3438
- */
3439
- end() {
3440
- return this;
3441
- }
3442
- build() {
3443
- return this.fields;
3444
- }
3445
- };
3446
- var NestedObjectBuilder = class _NestedObjectBuilder {
3447
- constructor(parent, path) {
3448
- this.parent = parent;
3449
- this.path = path;
3450
- }
3451
- /**
3452
- * Add a field to the current nested path
3453
- */
3454
- field(fieldName, label, type = "input", props) {
3455
- const fullPath = `${this.path}.${fieldName}`;
3456
- this.parent.fields.push({
3457
- label,
3458
- name: fullPath,
3459
- type,
3460
- ...props
3461
- });
3462
- return this;
3463
- }
3464
- /**
3465
- * Nest deeper into the object
3466
- */
3467
- nest(subPath) {
3468
- return new _NestedObjectBuilder(
3469
- this.parent,
3470
- `${this.path}.${subPath}`
3471
- );
3472
- }
3473
- /**
3474
- * Return to the parent builder
3475
- */
3476
- end() {
3477
- return this.parent;
3478
- }
3479
- };
3480
- var SectionBuilder = class {
3481
- constructor(parent, path) {
3482
- this.parent = parent;
3483
- this.path = path;
3484
- }
3485
- /**
3486
- * Add a field to the current section
3487
- */
3488
- field(fieldName, label, type = "input", props) {
3489
- const fullPath = `${this.path}.${fieldName}`;
3490
- this.parent.fields.push({
3491
- label,
3492
- name: fullPath,
3493
- type,
3494
- ...props
3495
- });
3496
- return this;
3497
- }
3498
- /**
3499
- * Add multiple fields to the section
3500
- */
3501
- fields(fieldDefinitions) {
3502
- fieldDefinitions.forEach((field2) => {
3503
- this.field(field2.name, field2.label, field2.type, field2.props);
3504
- });
3505
- return this;
3506
- }
3507
- /**
3508
- * Nest deeper into the section
3509
- */
3510
- nest(subPath) {
3511
- return new NestedObjectBuilder(
3512
- this.parent,
3513
- `${this.path}.${subPath}`
3514
- );
3515
- }
3516
- /**
3517
- * Return to the parent builder
3518
- */
3519
- end() {
3520
- return this.parent;
3521
- }
3522
- };
3523
- var FieldTemplateBuilder = class {
3524
- constructor(parent, path) {
3525
- this.parent = parent;
3526
- this.path = path;
3527
- }
3528
- /**
3529
- * Complete the field definition
3530
- */
3531
- complete(label, type = "input", props) {
3532
- this.parent.fields.push({
3533
- label,
3534
- name: this.path,
3535
- type,
3536
- ...props
3537
- });
3538
- return this.parent;
3539
- }
3540
- };
3541
- function createNestedPathBuilder() {
3542
- return new NestedPathBuilder();
3543
- }
3544
-
3545
- // src/hooks/useDebouncedValidation.ts
3546
- import { useCallback as useCallback2, useEffect as useEffect2, useRef } from "react";
3547
- function useDebouncedValidation(form, options = {}) {
3548
- const { delay = 300, enabled = true, fields } = options;
3549
- const timeoutRef = useRef(
3550
- void 0
3551
- );
3552
- const lastValuesRef = useRef({});
3553
- const debouncedTrigger = useCallback2(() => {
3554
- if (!enabled) return;
3555
- if (timeoutRef.current) {
3556
- clearTimeout(timeoutRef.current);
3557
- timeoutRef.current = void 0;
3558
- }
3559
- timeoutRef.current = setTimeout(async () => {
3560
- const currentValues = form.getValues();
3561
- const lastValues = lastValuesRef.current;
3562
- const hasChanges = fields ? fields.some((field2) => currentValues[field2] !== lastValues[field2]) : Object.keys(currentValues).some(
3563
- (key) => currentValues[key] !== lastValues[key]
3564
- );
3565
- if (hasChanges) {
3566
- lastValuesRef.current = { ...currentValues };
3567
- if (fields && fields.length > 0) {
3568
- await form.trigger(fields);
3569
- } else {
3570
- await form.trigger();
3571
- }
3572
- }
3573
- }, delay);
3574
- }, [form, delay, fields, enabled]);
3575
- useEffect2(() => {
3576
- return () => {
3577
- if (timeoutRef.current) {
3578
- clearTimeout(timeoutRef.current);
3579
- timeoutRef.current = void 0;
3580
- }
3581
- };
3582
- }, []);
3583
- useEffect2(() => {
3584
- if (form.formState.isSubmitSuccessful) {
3585
- lastValuesRef.current = {};
3586
- }
3587
- }, [form.formState.isSubmitSuccessful]);
3588
- return {
3589
- debouncedTrigger,
3590
- isDebouncing: !!timeoutRef.current
3591
- };
3592
- }
3593
- function useDebouncedFieldValidation(form, fieldName, options = {}) {
3594
- const { delay = 300, enabled = true } = options;
3595
- const timeoutRef = useRef(
3596
- void 0
3597
- );
3598
- const debouncedFieldTrigger = useCallback2(() => {
3599
- if (!enabled) return;
3600
- if (timeoutRef.current) {
3601
- clearTimeout(timeoutRef.current);
3602
- }
3603
- timeoutRef.current = setTimeout(async () => {
3604
- await form.trigger(fieldName);
3605
- }, delay);
3606
- }, [form, fieldName, delay, enabled]);
3607
- useEffect2(() => {
3608
- return () => {
3609
- if (timeoutRef.current) {
3610
- clearTimeout(timeoutRef.current);
3611
- timeoutRef.current = void 0;
3612
- }
3613
- };
3614
- }, []);
3615
- return {
3616
- debouncedFieldTrigger,
3617
- isDebouncing: !!timeoutRef.current
3618
- };
3619
- }
3620
-
3621
- // src/hooks/useInferredForm.ts
3622
- import { useForm as useForm3 } from "react-hook-form";
3623
- var zodResolver;
3624
- try {
3625
- zodResolver = __require("@hookform/resolvers/zod").zodResolver;
3626
- } catch {
3627
- }
3628
- function useInferredForm(schema, fields, options = {}) {
3629
- const {
3630
- defaultValues,
3631
- delayError = 0,
3632
- mode = "onChange",
3633
- reValidateMode = "onChange",
3634
- shouldFocusError = true,
3635
- shouldUnregister = false
3636
- } = options;
3637
- return useForm3({
3638
- defaultValues,
3639
- delayError,
3640
- mode,
3641
- resolver: zodResolver ? zodResolver(schema) : void 0,
3642
- reValidateMode,
3643
- shouldFocusError,
3644
- shouldUnregister
3645
- });
3646
- }
3647
- function useTypeInferredForm(formConfig, options = {}) {
3648
- return useInferredForm(formConfig.schema, formConfig.fields, options);
3649
- }
3650
-
3651
- // src/utils/performance.ts
3652
- import { useCallback as useCallback3, useMemo as useMemo2, useRef as useRef2 } from "react";
3653
- function debounce(func, delay) {
3654
- let timeoutId;
3655
- return (...args) => {
3656
- clearTimeout(timeoutId);
3657
- timeoutId = setTimeout(() => func(...args), delay);
3658
- };
3659
- }
3660
- function throttle(func, limit) {
3661
- let inThrottle;
3662
- return (...args) => {
3663
- if (!inThrottle) {
3664
- func(...args);
3665
- inThrottle = true;
3666
- setTimeout(() => inThrottle = false, limit);
3667
- }
3668
- };
3669
- }
3670
- function useMemoizedCallback(callback, deps) {
3671
- const callbackRef = useRef2(callback);
3672
- callbackRef.current = callback;
3673
- return useCallback3(
3674
- ((...args) => callbackRef.current(...args)),
3675
- deps
3676
- );
3677
- }
3678
- function shallowEqual(prevProps, nextProps) {
3679
- const prevKeys = Object.keys(prevProps);
3680
- const nextKeys = Object.keys(nextProps);
3681
- if (prevKeys.length !== nextKeys.length) {
3682
- return false;
3683
- }
3684
- for (const key of prevKeys) {
3685
- if (prevProps[key] !== nextProps[key]) {
3686
- return false;
3687
- }
3688
- }
3689
- return true;
3690
- }
3691
- function deepEqual(prevProps, nextProps) {
3692
- if (prevProps === nextProps) {
3693
- return true;
3694
- }
3695
- if (typeof prevProps !== typeof nextProps) {
3696
- return false;
3697
- }
3698
- if (typeof prevProps !== "object" || prevProps === null || nextProps === null) {
3699
- return prevProps === nextProps;
3700
- }
3701
- const prevKeys = Object.keys(prevProps);
3702
- const nextKeys = Object.keys(nextProps);
3703
- if (prevKeys.length !== nextKeys.length) {
3704
- return false;
3705
- }
3706
- for (const key of prevKeys) {
3707
- if (!nextKeys.includes(key)) {
3708
- return false;
3709
- }
3710
- if (!deepEqual(prevProps[key], nextProps[key])) {
3711
- return false;
3712
- }
3713
- }
3714
- return true;
3715
- }
3716
- function usePerformanceMonitor(componentName, enabled = false) {
3717
- const renderCountRef = useRef2(0);
3718
- const lastRenderTimeRef = useRef2(Date.now());
3719
- if (enabled) {
3720
- renderCountRef.current += 1;
3721
- const now = Date.now();
3722
- const timeSinceLastRender = now - lastRenderTimeRef.current;
3723
- console.log(`[Performance] ${componentName}:`, {
3724
- renderCount: renderCountRef.current,
3725
- timeSinceLastRender: `${timeSinceLastRender}ms`
3726
- });
3727
- lastRenderTimeRef.current = now;
3728
- }
3729
- return {
3730
- renderCount: renderCountRef.current,
3731
- resetRenderCount: () => {
3732
- renderCountRef.current = 0;
3733
- }
3734
- };
3735
- }
3736
- function createOptimizedFieldHandler(onChange, options = {}) {
3737
- const { debounce: debounceMs, throttle: throttleMs } = options;
3738
- let handler = onChange;
3739
- if (throttleMs) {
3740
- handler = throttle(handler, throttleMs);
3741
- }
3742
- if (debounceMs) {
3743
- handler = debounce(handler, debounceMs);
3744
- }
3745
- return handler;
3746
- }
3747
- function useMemoizedFieldProps(props, deps) {
3748
- return useMemo2(() => props, deps);
3749
- }
3750
-
3751
- // src/builders/validation-helpers.ts
3752
- import { z as z4 } from "zod";
3753
- var validationPatterns = {
3754
- // Credit card validation
3755
- creditCard: z4.string().regex(
3756
- // eslint-disable-next-line no-useless-escape
3757
- /^[0-9]{4}[\s\-]?[0-9]{4}[\s\-]?[0-9]{4}[\s\-]?[0-9]{4}$/,
3758
- "Please enter a valid credit card number"
3759
- ),
3760
- // Date validation (MM/DD/YYYY)
3761
- date: z4.string().regex(
3762
- /^(0[1-9]|1[0-2])\/(0[1-9]|[12][0-9]|3[01])\/\d{4}$/,
3763
- "Please enter a valid date (MM/DD/YYYY)"
3764
- ),
3765
- // Email validation
3766
- email: z4.string().email("Please enter a valid email address"),
3767
- // Password validation
3768
- password: z4.string().min(8, "Password must be at least 8 characters").regex(/[A-Z]/, "Password must contain at least one uppercase letter").regex(/[a-z]/, "Password must contain at least one lowercase letter").regex(/[0-9]/, "Password must contain at least one number").regex(
3769
- /[^A-Za-z0-9]/,
3770
- "Password must contain at least one special character"
3771
- ),
3772
- // Phone number validation (international)
3773
- phoneInternational: z4.string().regex(/^\+?[\d\s\-\(\)]+$/, "Please enter a valid phone number"),
3774
- // Phone number validation (US format)
3775
- phoneUS: z4.string().regex(
3776
- /^\(\d{3}\) \d{3}-\d{4}$/,
3777
- "Please enter a valid phone number (XXX) XXX-XXXX"
3778
- ),
3779
- // SSN validation
3780
- ssn: z4.string().regex(/^\d{3}-\d{2}-\d{4}$/, "Please enter a valid SSN (XXX-XX-XXXX)"),
3781
- // Strong password validation
3782
- strongPassword: z4.string().min(12, "Password must be at least 12 characters").regex(/[A-Z]/, "Password must contain at least one uppercase letter").regex(/[a-z]/, "Password must contain at least one lowercase letter").regex(/[0-9]/, "Password must contain at least one number").regex(
3783
- /[^A-Za-z0-9]/,
3784
- "Password must contain at least one special character"
3785
- ),
3786
- // Time validation (HH:MM AM/PM)
3787
- time: z4.string().regex(
3788
- /^(0[1-9]|1[0-2]):[0-5][0-9] (AM|PM)$/i,
3789
- "Please enter a valid time (HH:MM AM/PM)"
3790
- ),
3791
- // URL validation
3792
- url: z4.string().url("Please enter a valid URL"),
3793
- // ZIP code validation
3794
- zipCode: z4.string().regex(/^\d{5}(-\d{4})?$/, "Please enter a valid ZIP code")
3795
- };
3796
- var asyncValidation = {
3797
- /**
3798
- * Email availability check
3799
- */
3800
- emailAvailability: async (email) => {
3801
- return new Promise((resolve) => {
3802
- setTimeout(() => {
3803
- const takenEmails = ["test@example.com", "admin@example.com"];
3804
- resolve(!takenEmails.includes(email));
3805
- }, 1e3);
3806
- });
3807
- },
3808
- /**
3809
- * Username availability check
3810
- */
3811
- usernameAvailability: async (username) => {
3812
- return new Promise((resolve) => {
3813
- setTimeout(() => {
3814
- const takenUsernames = ["admin", "test", "user"];
3815
- resolve(!takenUsernames.includes(username.toLowerCase()));
3816
- }, 1e3);
3817
- });
3818
- }
3819
- };
3820
- var errorMessages = {
3821
- date: () => "Please enter a valid date",
3822
- email: () => "Please enter a valid email address",
3823
- max: (fieldName, max) => `${fieldName} must be no more than ${max}`,
3824
- maxLength: (fieldName, max) => `${fieldName} must be no more than ${max} characters`,
3825
- min: (fieldName, min) => `${fieldName} must be at least ${min}`,
3826
- minLength: (fieldName, min) => `${fieldName} must be at least ${min} characters`,
3827
- pattern: (fieldName) => `${fieldName} format is invalid`,
3828
- phone: () => "Please enter a valid phone number",
3829
- required: (fieldName) => `${fieldName} is required`,
3830
- time: () => "Please enter a valid time",
3831
- url: () => "Please enter a valid URL"
3832
- };
3833
- var serverValidation = {
3834
- /**
3835
- * Apply server errors to form
3836
- */
3837
- applyServerErrors: (errors, setError) => {
3838
- Object.entries(errors).forEach(([field2, messages]) => {
3839
- setError(field2, {
3840
- message: messages[0],
3841
- type: "server"
3842
- // Use first error message
3843
- });
3844
- });
3845
- },
3846
- /**
3847
- * Clear server errors
3848
- */
3849
- clearServerErrors: (fields, clearErrors) => {
3850
- fields.forEach((field2) => {
3851
- clearErrors(field2, "server");
3852
- });
3853
- }
3854
- };
3855
- var validationUtils = {
3856
- /**
3857
- * Debounced validation
3858
- */
3859
- debounceValidation: (fn, delay = 300) => {
3860
- let timeoutId;
3861
- return (...args) => {
3862
- clearTimeout(timeoutId);
3863
- timeoutId = setTimeout(() => fn(...args), delay);
3864
- };
3865
- },
3866
- /**
3867
- * Get field error message
3868
- */
3869
- getFieldError: (errors, field2) => {
3870
- return errors[field2];
3871
- },
3872
- /**
3873
- * Check if field has error
3874
- */
3875
- hasFieldError: (errors, field2) => {
3876
- return !!errors[field2];
3877
- },
3878
- /**
3879
- * Validate form data against schema
3880
- */
3881
- validateForm: async (data, schema) => {
3882
- try {
3883
- await schema.parseAsync(data);
3884
- return { errors: {}, success: true };
3885
- } catch (error) {
3886
- if (error instanceof z4.ZodError) {
3887
- const errors = {};
3888
- error.issues.forEach((err) => {
3889
- const path = err.path.join(".");
3890
- errors[path] = err.message;
3891
- });
3892
- return { errors, success: false };
3893
- }
3894
- throw error;
3895
- }
3896
- }
3897
- };
3898
- export {
3899
- AdvancedFieldBuilder,
3900
- AutocompleteField,
3901
- BasicFormBuilder,
3902
- CheckboxField,
3903
- CommonFields,
3904
- ConditionalField,
3905
- ConfigurableForm,
3906
- ContentField,
3907
- DateField,
3908
- DynamicSectionField,
3909
- FieldArrayBuilder,
3910
- FieldArrayField,
3911
- FieldArrayItemBuilder,
3912
- FileField,
3913
- FontPickerField,
3914
- FormField,
3915
- FormFieldHelpers,
3916
- FormProvider,
3917
- FormStatus,
3918
- FormToast,
3919
- HeroHookFormProvider,
3920
- InputField,
3921
- RadioGroupField,
3922
- SelectField,
3923
- ServerActionForm,
3924
- SliderField,
3925
- SubmitButton,
3926
- SwitchField,
3927
- TextareaField,
3928
- TypeInferredBuilder,
3929
- ZodForm,
3930
- applyServerErrors,
3931
- asyncValidation,
3932
- commonValidations,
3933
- createAdvancedBuilder,
3934
- createBasicFormBuilder,
3935
- createDateSchema,
3936
- createEmailSchema,
3937
- createField,
3938
- createFieldArrayBuilder,
3939
- createFieldArrayItemBuilder,
3940
- createFileSchema,
3941
- createFormTestUtils,
3942
- createFutureDateSchema,
3943
- createMaxLengthSchema,
3944
- createMinLengthSchema,
3945
- createMockFormData,
3946
- createMockFormErrors,
3947
- createNestedPathBuilder,
3948
- createNumberRangeSchema,
3949
- createOptimizedFieldHandler,
3950
- createPasswordSchema,
3951
- createPastDateSchema,
3952
- createPhoneSchema,
3953
- createRequiredCheckboxSchema,
3954
- createRequiredSchema,
3955
- createTypeInferredBuilder,
3956
- createUrlSchema,
3957
- createZodFormConfig,
3958
- crossFieldValidation,
3959
- debounce,
3960
- deepEqual,
3961
- defineInferredForm,
3962
- errorMessages,
3963
- field,
3964
- getFieldError,
3965
- getFormErrors,
3966
- hasFieldError,
3967
- hasFormErrors,
3968
- serverValidation,
3969
- shallowEqual,
3970
- simulateFieldInput,
3971
- simulateFormSubmission,
3972
- throttle,
3973
- useDebouncedFieldValidation,
3974
- useDebouncedValidation,
3975
- useEnhancedFormState,
3976
- useFormContext5 as useFormContext,
3977
- useFormHelper,
3978
- useHeroForm,
3979
- useHeroHookFormDefaults,
3980
- useInferredForm,
3981
- useMemoizedCallback,
3982
- useMemoizedFieldProps,
3983
- usePerformanceMonitor,
3984
- useTypeInferredForm,
3985
- useZodForm,
3986
- validationPatterns,
3987
- validationUtils,
3988
- waitForFormState
3989
- };