@rachelallyson/hero-hook-form 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +80 -0
- package/dist/cypress/index.d.ts +141 -0
- package/dist/cypress/index.js +897 -0
- package/dist/index.d.ts +730 -5
- package/dist/index.js +1832 -220
- package/dist/react/index.d.ts +730 -5
- package/dist/react/index.js +1832 -220
- package/package.json +10 -3
- package/README.md +0 -412
package/dist/react/index.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
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
|
+
|
|
1
8
|
// src/components/Form.tsx
|
|
2
|
-
import
|
|
3
|
-
import { Button as
|
|
9
|
+
import React16 from "react";
|
|
10
|
+
import { Button as Button3 } from "@heroui/react";
|
|
4
11
|
|
|
5
12
|
// src/hooks/useFormHelper.ts
|
|
6
13
|
import { useState } from "react";
|
|
@@ -68,8 +75,8 @@ function useFormHelper({
|
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
// src/components/FormField.tsx
|
|
71
|
-
import
|
|
72
|
-
import { useWatch } from "react-hook-form";
|
|
78
|
+
import React15 from "react";
|
|
79
|
+
import { useWatch as useWatch3 } from "react-hook-form";
|
|
73
80
|
|
|
74
81
|
// src/fields/CheckboxField.tsx
|
|
75
82
|
import React2 from "react";
|
|
@@ -196,16 +203,16 @@ function CheckboxField(props) {
|
|
|
196
203
|
{
|
|
197
204
|
control,
|
|
198
205
|
name,
|
|
199
|
-
render: ({ field, fieldState }) => /* @__PURE__ */ React2.createElement("div", { className }, /* @__PURE__ */ React2.createElement(
|
|
206
|
+
render: ({ field: field2, fieldState }) => /* @__PURE__ */ React2.createElement("div", { className }, /* @__PURE__ */ React2.createElement(
|
|
200
207
|
Checkbox,
|
|
201
208
|
{
|
|
202
209
|
...defaults.checkbox,
|
|
203
210
|
...checkboxProps,
|
|
204
211
|
isDisabled,
|
|
205
212
|
isInvalid: Boolean(fieldState.error),
|
|
206
|
-
isSelected: Boolean(
|
|
207
|
-
onBlur:
|
|
208
|
-
onValueChange: (val) =>
|
|
213
|
+
isSelected: Boolean(field2.value),
|
|
214
|
+
onBlur: field2.onBlur,
|
|
215
|
+
onValueChange: (val) => field2.onChange(val)
|
|
209
216
|
},
|
|
210
217
|
label
|
|
211
218
|
), description ? /* @__PURE__ */ React2.createElement("p", { className: "text-small text-default-400" }, description) : null, fieldState.error?.message ? /* @__PURE__ */ React2.createElement("p", { className: "text-tiny text-danger mt-1" }, fieldState.error.message) : null),
|
|
@@ -214,13 +221,43 @@ function CheckboxField(props) {
|
|
|
214
221
|
);
|
|
215
222
|
}
|
|
216
223
|
|
|
217
|
-
// src/fields/
|
|
224
|
+
// src/fields/ConditionalField.tsx
|
|
218
225
|
import React3 from "react";
|
|
226
|
+
import { useWatch, useFormContext } from "react-hook-form";
|
|
227
|
+
function ConditionalField({
|
|
228
|
+
config,
|
|
229
|
+
control,
|
|
230
|
+
className
|
|
231
|
+
}) {
|
|
232
|
+
const { condition, field: field2, name } = config;
|
|
233
|
+
const form = useFormContext();
|
|
234
|
+
const formValues = useWatch({ control });
|
|
235
|
+
const shouldShow = condition(formValues);
|
|
236
|
+
if (!shouldShow) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
return /* @__PURE__ */ React3.createElement("div", { className }, /* @__PURE__ */ React3.createElement(
|
|
240
|
+
FormField,
|
|
241
|
+
{
|
|
242
|
+
config: field2,
|
|
243
|
+
form,
|
|
244
|
+
submissionState: {
|
|
245
|
+
isSubmitting: false,
|
|
246
|
+
isSubmitted: false,
|
|
247
|
+
isSuccess: false,
|
|
248
|
+
error: void 0
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// src/fields/DateField.tsx
|
|
255
|
+
import React4 from "react";
|
|
219
256
|
import { Controller as Controller2 } from "react-hook-form";
|
|
220
257
|
function CoercedDateInput(props) {
|
|
221
|
-
const { dateProps, description, disabled, errorMessage, field, label } = props;
|
|
258
|
+
const { dateProps, description, disabled, errorMessage, field: field2, label } = props;
|
|
222
259
|
const defaults = useHeroHookFormDefaults();
|
|
223
|
-
return /* @__PURE__ */
|
|
260
|
+
return /* @__PURE__ */ React4.createElement(
|
|
224
261
|
DateInput,
|
|
225
262
|
{
|
|
226
263
|
...defaults.dateInput,
|
|
@@ -230,9 +267,9 @@ function CoercedDateInput(props) {
|
|
|
230
267
|
isDisabled: disabled,
|
|
231
268
|
isInvalid: Boolean(errorMessage),
|
|
232
269
|
label,
|
|
233
|
-
value:
|
|
234
|
-
onBlur:
|
|
235
|
-
onChange:
|
|
270
|
+
value: field2.value ?? null,
|
|
271
|
+
onBlur: field2.onBlur,
|
|
272
|
+
onChange: field2.onChange
|
|
236
273
|
}
|
|
237
274
|
);
|
|
238
275
|
}
|
|
@@ -248,12 +285,12 @@ function DateField(props) {
|
|
|
248
285
|
rules,
|
|
249
286
|
transform
|
|
250
287
|
} = props;
|
|
251
|
-
return /* @__PURE__ */
|
|
288
|
+
return /* @__PURE__ */ React4.createElement(
|
|
252
289
|
Controller2,
|
|
253
290
|
{
|
|
254
291
|
control,
|
|
255
292
|
name,
|
|
256
|
-
render: ({ field, fieldState }) => /* @__PURE__ */
|
|
293
|
+
render: ({ field: field2, fieldState }) => /* @__PURE__ */ React4.createElement("div", { className }, /* @__PURE__ */ React4.createElement(
|
|
257
294
|
CoercedDateInput,
|
|
258
295
|
{
|
|
259
296
|
dateProps,
|
|
@@ -261,8 +298,8 @@ function DateField(props) {
|
|
|
261
298
|
disabled: isDisabled,
|
|
262
299
|
errorMessage: fieldState.error?.message,
|
|
263
300
|
field: {
|
|
264
|
-
...
|
|
265
|
-
onChange: (value) =>
|
|
301
|
+
...field2,
|
|
302
|
+
onChange: (value) => field2.onChange(transform ? transform(value) : value)
|
|
266
303
|
},
|
|
267
304
|
label
|
|
268
305
|
}
|
|
@@ -272,8 +309,136 @@ function DateField(props) {
|
|
|
272
309
|
);
|
|
273
310
|
}
|
|
274
311
|
|
|
312
|
+
// src/fields/DynamicSectionField.tsx
|
|
313
|
+
import React5 from "react";
|
|
314
|
+
import { useWatch as useWatch2, useFormContext as useFormContext2 } from "react-hook-form";
|
|
315
|
+
function DynamicSectionField({
|
|
316
|
+
config,
|
|
317
|
+
control,
|
|
318
|
+
className
|
|
319
|
+
}) {
|
|
320
|
+
const { condition, fields, title, description } = config;
|
|
321
|
+
const form = useFormContext2();
|
|
322
|
+
const formValues = useWatch2({ control });
|
|
323
|
+
const shouldShow = condition(formValues);
|
|
324
|
+
if (!shouldShow) {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
return /* @__PURE__ */ React5.createElement("div", { className }, (title || description) && /* @__PURE__ */ React5.createElement("div", { className: "mb-6" }, title && /* @__PURE__ */ React5.createElement("h3", { className: "text-lg font-semibold text-gray-900 mb-2" }, title), description && /* @__PURE__ */ React5.createElement("p", { className: "text-sm text-gray-600" }, description)), /* @__PURE__ */ React5.createElement("div", { className: "space-y-4" }, fields.map((fieldConfig, index) => /* @__PURE__ */ React5.createElement(
|
|
328
|
+
FormField,
|
|
329
|
+
{
|
|
330
|
+
key: `${fieldConfig.name}-${index}`,
|
|
331
|
+
config: fieldConfig,
|
|
332
|
+
form,
|
|
333
|
+
submissionState: {
|
|
334
|
+
isSubmitting: false,
|
|
335
|
+
isSubmitted: false,
|
|
336
|
+
isSuccess: false,
|
|
337
|
+
error: void 0
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
))));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// src/fields/FieldArrayField.tsx
|
|
344
|
+
import React6 from "react";
|
|
345
|
+
import { useFieldArray, useFormContext as useFormContext3 } from "react-hook-form";
|
|
346
|
+
import { Button as Button2 } from "@heroui/react";
|
|
347
|
+
function FieldArrayField({
|
|
348
|
+
config,
|
|
349
|
+
className
|
|
350
|
+
}) {
|
|
351
|
+
const {
|
|
352
|
+
name,
|
|
353
|
+
fields: fieldConfigs,
|
|
354
|
+
min = 0,
|
|
355
|
+
max = 10,
|
|
356
|
+
addButtonText = "Add Item",
|
|
357
|
+
removeButtonText = "Remove"
|
|
358
|
+
} = config;
|
|
359
|
+
const form = useFormContext3();
|
|
360
|
+
if (!form || !form.control) {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
const { control } = form;
|
|
364
|
+
const { fields, append, remove } = useFieldArray({
|
|
365
|
+
control,
|
|
366
|
+
name
|
|
367
|
+
// FieldArray name
|
|
368
|
+
});
|
|
369
|
+
const canAdd = fields.length < max;
|
|
370
|
+
const canRemove = fields.length > min;
|
|
371
|
+
const handleAdd = () => {
|
|
372
|
+
if (canAdd) {
|
|
373
|
+
const defaultValues = fieldConfigs.reduce((acc, fieldConfig) => {
|
|
374
|
+
const fieldName = fieldConfig.name;
|
|
375
|
+
if (fieldConfig.type === "checkbox" || fieldConfig.type === "switch") {
|
|
376
|
+
acc[fieldName] = false;
|
|
377
|
+
} else if (fieldConfig.type === "slider") {
|
|
378
|
+
acc[fieldName] = 0;
|
|
379
|
+
} else {
|
|
380
|
+
acc[fieldName] = "";
|
|
381
|
+
}
|
|
382
|
+
return acc;
|
|
383
|
+
}, {});
|
|
384
|
+
append(defaultValues);
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
const handleRemove = (index) => {
|
|
388
|
+
if (canRemove) {
|
|
389
|
+
remove(index);
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
return /* @__PURE__ */ React6.createElement("div", { className }, /* @__PURE__ */ React6.createElement("div", { className: "space-y-4" }, fields.map((field2, index) => /* @__PURE__ */ React6.createElement("div", { key: field2.id, className: "border border-gray-200 rounded-lg p-4 space-y-4" }, /* @__PURE__ */ React6.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React6.createElement("h4", { className: "text-sm font-medium text-gray-700" }, config.label, " #", index + 1), canRemove && /* @__PURE__ */ React6.createElement(
|
|
393
|
+
Button2,
|
|
394
|
+
{
|
|
395
|
+
size: "sm",
|
|
396
|
+
variant: "light",
|
|
397
|
+
color: "danger",
|
|
398
|
+
startContent: "\u{1F5D1}\uFE0F",
|
|
399
|
+
onPress: () => handleRemove(index),
|
|
400
|
+
"aria-label": `${removeButtonText} ${config.label} ${index + 1}`
|
|
401
|
+
},
|
|
402
|
+
removeButtonText
|
|
403
|
+
)), /* @__PURE__ */ React6.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, fieldConfigs.map((fieldConfig) => /* @__PURE__ */ React6.createElement(
|
|
404
|
+
FormField,
|
|
405
|
+
{
|
|
406
|
+
key: `${fieldConfig.name}-${index}`,
|
|
407
|
+
config: {
|
|
408
|
+
...fieldConfig,
|
|
409
|
+
name: `${name}.${index}.${fieldConfig.name}`
|
|
410
|
+
},
|
|
411
|
+
form,
|
|
412
|
+
submissionState: {
|
|
413
|
+
isSubmitting: false,
|
|
414
|
+
isSubmitted: false,
|
|
415
|
+
isSuccess: false,
|
|
416
|
+
error: void 0
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
))))), canAdd && /* @__PURE__ */ React6.createElement(
|
|
420
|
+
Button2,
|
|
421
|
+
{
|
|
422
|
+
variant: "bordered",
|
|
423
|
+
startContent: "\u2795",
|
|
424
|
+
onPress: handleAdd,
|
|
425
|
+
className: "w-full"
|
|
426
|
+
},
|
|
427
|
+
addButtonText
|
|
428
|
+
), fields.length === 0 && /* @__PURE__ */ React6.createElement("div", { className: "text-center py-8 text-gray-500" }, /* @__PURE__ */ React6.createElement("p", null, "No ", config.label?.toLowerCase(), " added yet."), /* @__PURE__ */ React6.createElement(
|
|
429
|
+
Button2,
|
|
430
|
+
{
|
|
431
|
+
variant: "bordered",
|
|
432
|
+
startContent: "\u2795",
|
|
433
|
+
onPress: handleAdd,
|
|
434
|
+
className: "mt-2"
|
|
435
|
+
},
|
|
436
|
+
addButtonText
|
|
437
|
+
))));
|
|
438
|
+
}
|
|
439
|
+
|
|
275
440
|
// src/fields/FileField.tsx
|
|
276
|
-
import
|
|
441
|
+
import React7 from "react";
|
|
277
442
|
import { Controller as Controller3 } from "react-hook-form";
|
|
278
443
|
function CoercedFileInput(props) {
|
|
279
444
|
const {
|
|
@@ -281,13 +446,13 @@ function CoercedFileInput(props) {
|
|
|
281
446
|
description,
|
|
282
447
|
disabled,
|
|
283
448
|
errorMessage,
|
|
284
|
-
field,
|
|
449
|
+
field: field2,
|
|
285
450
|
fileProps,
|
|
286
451
|
label,
|
|
287
452
|
multiple
|
|
288
453
|
} = props;
|
|
289
454
|
const defaults = useHeroHookFormDefaults();
|
|
290
|
-
return /* @__PURE__ */
|
|
455
|
+
return /* @__PURE__ */ React7.createElement(
|
|
291
456
|
Input,
|
|
292
457
|
{
|
|
293
458
|
...defaults.input,
|
|
@@ -300,11 +465,11 @@ function CoercedFileInput(props) {
|
|
|
300
465
|
label,
|
|
301
466
|
multiple,
|
|
302
467
|
type: "file",
|
|
303
|
-
value:
|
|
304
|
-
onBlur:
|
|
468
|
+
value: field2.value ? "" : "",
|
|
469
|
+
onBlur: field2.onBlur,
|
|
305
470
|
onChange: (e) => {
|
|
306
471
|
const target = e.target;
|
|
307
|
-
|
|
472
|
+
field2.onChange(target.files);
|
|
308
473
|
}
|
|
309
474
|
}
|
|
310
475
|
);
|
|
@@ -323,12 +488,12 @@ function FileField(props) {
|
|
|
323
488
|
rules,
|
|
324
489
|
transform
|
|
325
490
|
} = props;
|
|
326
|
-
return /* @__PURE__ */
|
|
491
|
+
return /* @__PURE__ */ React7.createElement(
|
|
327
492
|
Controller3,
|
|
328
493
|
{
|
|
329
494
|
control,
|
|
330
495
|
name,
|
|
331
|
-
render: ({ field, fieldState }) => /* @__PURE__ */
|
|
496
|
+
render: ({ field: field2, fieldState }) => /* @__PURE__ */ React7.createElement("div", { className }, /* @__PURE__ */ React7.createElement(
|
|
332
497
|
CoercedFileInput,
|
|
333
498
|
{
|
|
334
499
|
accept,
|
|
@@ -336,8 +501,8 @@ function FileField(props) {
|
|
|
336
501
|
disabled: isDisabled,
|
|
337
502
|
errorMessage: fieldState.error?.message,
|
|
338
503
|
field: {
|
|
339
|
-
...
|
|
340
|
-
onChange: (value) =>
|
|
504
|
+
...field2,
|
|
505
|
+
onChange: (value) => field2.onChange(transform ? transform(value) : value)
|
|
341
506
|
},
|
|
342
507
|
fileProps,
|
|
343
508
|
label,
|
|
@@ -350,7 +515,7 @@ function FileField(props) {
|
|
|
350
515
|
}
|
|
351
516
|
|
|
352
517
|
// src/fields/FontPickerField.tsx
|
|
353
|
-
import
|
|
518
|
+
import React8 from "react";
|
|
354
519
|
import { Controller as Controller4 } from "react-hook-form";
|
|
355
520
|
var FontPickerComponent = null;
|
|
356
521
|
var fontPickerLoaded = false;
|
|
@@ -367,12 +532,12 @@ function FontPickerField(props) {
|
|
|
367
532
|
name,
|
|
368
533
|
rules
|
|
369
534
|
} = props;
|
|
370
|
-
const [fontPickerState, setFontPickerState] =
|
|
535
|
+
const [fontPickerState, setFontPickerState] = React8.useState({
|
|
371
536
|
component: FontPickerComponent,
|
|
372
537
|
loading: false,
|
|
373
538
|
error: null
|
|
374
539
|
});
|
|
375
|
-
|
|
540
|
+
React8.useEffect(() => {
|
|
376
541
|
if (fontPickerLoaded && FontPickerComponent) {
|
|
377
542
|
setFontPickerState({
|
|
378
543
|
component: FontPickerComponent,
|
|
@@ -430,23 +595,23 @@ function FontPickerField(props) {
|
|
|
430
595
|
void loadFontPicker();
|
|
431
596
|
}, []);
|
|
432
597
|
if (fontPickerState.loading) {
|
|
433
|
-
return /* @__PURE__ */
|
|
598
|
+
return /* @__PURE__ */ React8.createElement("div", { className }, /* @__PURE__ */ React8.createElement("div", { className: "space-y-2" }, label && /* @__PURE__ */ React8.createElement("label", { className: "block text-sm font-medium text-foreground" }, label), description && /* @__PURE__ */ React8.createElement("p", { className: "text-sm text-muted-foreground" }, description), /* @__PURE__ */ React8.createElement("div", { className: "p-4 border border-default-200 bg-default-50 rounded-medium" }, /* @__PURE__ */ React8.createElement("p", { className: "text-default-600 text-sm" }, "Loading font picker..."))));
|
|
434
599
|
}
|
|
435
600
|
if (!fontPickerState.component) {
|
|
436
|
-
return /* @__PURE__ */
|
|
601
|
+
return /* @__PURE__ */ React8.createElement("div", { className }, /* @__PURE__ */ React8.createElement("div", { className: "space-y-2" }, label && /* @__PURE__ */ React8.createElement("label", { className: "block text-sm font-medium text-foreground" }, label), description && /* @__PURE__ */ React8.createElement("p", { className: "text-sm text-muted-foreground" }, description), /* @__PURE__ */ React8.createElement("div", { className: "p-4 border border-warning-200 bg-warning-50 rounded-medium" }, /* @__PURE__ */ React8.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."))));
|
|
437
602
|
}
|
|
438
|
-
return /* @__PURE__ */
|
|
603
|
+
return /* @__PURE__ */ React8.createElement(
|
|
439
604
|
Controller4,
|
|
440
605
|
{
|
|
441
606
|
control,
|
|
442
607
|
name,
|
|
443
|
-
render: ({ field, fieldState }) => /* @__PURE__ */
|
|
608
|
+
render: ({ field: field2, fieldState }) => /* @__PURE__ */ React8.createElement(
|
|
444
609
|
fontPickerState.component,
|
|
445
610
|
{
|
|
446
611
|
label,
|
|
447
612
|
description,
|
|
448
|
-
value:
|
|
449
|
-
onSelectionChange: (value) =>
|
|
613
|
+
value: field2.value ?? "",
|
|
614
|
+
onSelectionChange: (value) => field2.onChange(value),
|
|
450
615
|
errorMessage: fieldState.error?.message,
|
|
451
616
|
isDisabled,
|
|
452
617
|
className,
|
|
@@ -459,12 +624,12 @@ function FontPickerField(props) {
|
|
|
459
624
|
}
|
|
460
625
|
|
|
461
626
|
// src/fields/InputField.tsx
|
|
462
|
-
import
|
|
627
|
+
import React9 from "react";
|
|
463
628
|
import { Controller as Controller5 } from "react-hook-form";
|
|
464
629
|
function CoercedInput(props) {
|
|
465
|
-
const { description, disabled, errorMessage, field, inputProps, label } = props;
|
|
630
|
+
const { description, disabled, errorMessage, field: field2, inputProps, label } = props;
|
|
466
631
|
const defaults = useHeroHookFormDefaults();
|
|
467
|
-
return /* @__PURE__ */
|
|
632
|
+
return /* @__PURE__ */ React9.createElement(
|
|
468
633
|
Input,
|
|
469
634
|
{
|
|
470
635
|
...defaults.input,
|
|
@@ -474,13 +639,13 @@ function CoercedInput(props) {
|
|
|
474
639
|
isDisabled: disabled,
|
|
475
640
|
isInvalid: Boolean(errorMessage),
|
|
476
641
|
label,
|
|
477
|
-
value:
|
|
478
|
-
onBlur:
|
|
479
|
-
onValueChange:
|
|
642
|
+
value: field2.value ?? "",
|
|
643
|
+
onBlur: field2.onBlur,
|
|
644
|
+
onValueChange: field2.onChange
|
|
480
645
|
}
|
|
481
646
|
);
|
|
482
647
|
}
|
|
483
|
-
|
|
648
|
+
var InputField = React9.memo((props) => {
|
|
484
649
|
const {
|
|
485
650
|
className,
|
|
486
651
|
control,
|
|
@@ -492,27 +657,27 @@ function InputField(props) {
|
|
|
492
657
|
rules,
|
|
493
658
|
transform
|
|
494
659
|
} = props;
|
|
495
|
-
return /* @__PURE__ */
|
|
660
|
+
return /* @__PURE__ */ React9.createElement(
|
|
496
661
|
Controller5,
|
|
497
662
|
{
|
|
498
663
|
control,
|
|
499
664
|
name,
|
|
500
|
-
render: ({ field, fieldState }) => /* @__PURE__ */
|
|
665
|
+
render: ({ field: field2, fieldState }) => /* @__PURE__ */ React9.createElement("div", { className }, /* @__PURE__ */ React9.createElement(
|
|
501
666
|
CoercedInput,
|
|
502
667
|
{
|
|
503
668
|
description,
|
|
504
669
|
disabled: isDisabled,
|
|
505
670
|
errorMessage: fieldState.error?.message,
|
|
506
671
|
field: {
|
|
507
|
-
...
|
|
672
|
+
...field2,
|
|
508
673
|
onChange: (value) => {
|
|
509
674
|
if (inputProps?.type === "number") {
|
|
510
675
|
const numValue = value === "" ? void 0 : Number(value);
|
|
511
|
-
|
|
676
|
+
field2.onChange(
|
|
512
677
|
transform ? transform(String(numValue)) : numValue
|
|
513
678
|
);
|
|
514
679
|
} else {
|
|
515
|
-
|
|
680
|
+
field2.onChange(transform ? transform(value) : value);
|
|
516
681
|
}
|
|
517
682
|
}
|
|
518
683
|
},
|
|
@@ -523,10 +688,10 @@ function InputField(props) {
|
|
|
523
688
|
rules
|
|
524
689
|
}
|
|
525
690
|
);
|
|
526
|
-
}
|
|
691
|
+
});
|
|
527
692
|
|
|
528
693
|
// src/fields/RadioGroupField.tsx
|
|
529
|
-
import
|
|
694
|
+
import React10 from "react";
|
|
530
695
|
import { Controller as Controller6 } from "react-hook-form";
|
|
531
696
|
function RadioGroupField(props) {
|
|
532
697
|
const {
|
|
@@ -541,12 +706,12 @@ function RadioGroupField(props) {
|
|
|
541
706
|
rules
|
|
542
707
|
} = props;
|
|
543
708
|
const defaults = useHeroHookFormDefaults();
|
|
544
|
-
return /* @__PURE__ */
|
|
709
|
+
return /* @__PURE__ */ React10.createElement(
|
|
545
710
|
Controller6,
|
|
546
711
|
{
|
|
547
712
|
control,
|
|
548
713
|
name,
|
|
549
|
-
render: ({ field, fieldState }) => /* @__PURE__ */
|
|
714
|
+
render: ({ field: field2, fieldState }) => /* @__PURE__ */ React10.createElement("div", { className }, /* @__PURE__ */ React10.createElement(
|
|
550
715
|
RadioGroup,
|
|
551
716
|
{
|
|
552
717
|
...defaults.radioGroup,
|
|
@@ -555,11 +720,11 @@ function RadioGroupField(props) {
|
|
|
555
720
|
isDisabled,
|
|
556
721
|
isInvalid: Boolean(fieldState.error),
|
|
557
722
|
label,
|
|
558
|
-
value: String(
|
|
559
|
-
onBlur:
|
|
560
|
-
onValueChange: (val) =>
|
|
723
|
+
value: String(field2.value ?? ""),
|
|
724
|
+
onBlur: field2.onBlur,
|
|
725
|
+
onValueChange: (val) => field2.onChange(val)
|
|
561
726
|
},
|
|
562
|
-
options.map((opt) => /* @__PURE__ */
|
|
727
|
+
options.map((opt) => /* @__PURE__ */ React10.createElement(
|
|
563
728
|
Radio,
|
|
564
729
|
{
|
|
565
730
|
key: String(opt.value),
|
|
@@ -568,14 +733,14 @@ function RadioGroupField(props) {
|
|
|
568
733
|
},
|
|
569
734
|
opt.label
|
|
570
735
|
))
|
|
571
|
-
), fieldState.error?.message ? /* @__PURE__ */
|
|
736
|
+
), fieldState.error?.message ? /* @__PURE__ */ React10.createElement("p", { className: "text-tiny text-danger mt-1" }, fieldState.error.message) : null),
|
|
572
737
|
rules
|
|
573
738
|
}
|
|
574
739
|
);
|
|
575
740
|
}
|
|
576
741
|
|
|
577
742
|
// src/fields/SelectField.tsx
|
|
578
|
-
import
|
|
743
|
+
import React11 from "react";
|
|
579
744
|
import { Controller as Controller7 } from "react-hook-form";
|
|
580
745
|
function SelectField(props) {
|
|
581
746
|
const {
|
|
@@ -591,14 +756,14 @@ function SelectField(props) {
|
|
|
591
756
|
selectProps
|
|
592
757
|
} = props;
|
|
593
758
|
const defaults = useHeroHookFormDefaults();
|
|
594
|
-
return /* @__PURE__ */
|
|
759
|
+
return /* @__PURE__ */ React11.createElement(
|
|
595
760
|
Controller7,
|
|
596
761
|
{
|
|
597
762
|
control,
|
|
598
763
|
name,
|
|
599
|
-
render: ({ field, fieldState }) => {
|
|
600
|
-
const selectedKey =
|
|
601
|
-
return /* @__PURE__ */
|
|
764
|
+
render: ({ field: field2, fieldState }) => {
|
|
765
|
+
const selectedKey = field2.value;
|
|
766
|
+
return /* @__PURE__ */ React11.createElement("div", { className }, /* @__PURE__ */ React11.createElement(
|
|
602
767
|
Select,
|
|
603
768
|
{
|
|
604
769
|
...defaults.select,
|
|
@@ -613,10 +778,10 @@ function SelectField(props) {
|
|
|
613
778
|
onSelectionChange: (keys) => {
|
|
614
779
|
const keyArray = Array.from(keys);
|
|
615
780
|
const next = keyArray[0] ?? "";
|
|
616
|
-
|
|
781
|
+
field2.onChange(next);
|
|
617
782
|
}
|
|
618
783
|
},
|
|
619
|
-
options.map((opt) => /* @__PURE__ */
|
|
784
|
+
options.map((opt) => /* @__PURE__ */ React11.createElement(
|
|
620
785
|
SelectItem,
|
|
621
786
|
{
|
|
622
787
|
key: String(opt.value),
|
|
@@ -633,12 +798,12 @@ function SelectField(props) {
|
|
|
633
798
|
}
|
|
634
799
|
|
|
635
800
|
// src/fields/SliderField.tsx
|
|
636
|
-
import
|
|
801
|
+
import React12 from "react";
|
|
637
802
|
import { Controller as Controller8 } from "react-hook-form";
|
|
638
803
|
function CoercedSlider(props) {
|
|
639
|
-
const { description, disabled, errorMessage, field, label, sliderProps } = props;
|
|
804
|
+
const { description, disabled, errorMessage, field: field2, label, sliderProps } = props;
|
|
640
805
|
const defaults = useHeroHookFormDefaults();
|
|
641
|
-
return /* @__PURE__ */
|
|
806
|
+
return /* @__PURE__ */ React12.createElement(
|
|
642
807
|
Slider,
|
|
643
808
|
{
|
|
644
809
|
...defaults.slider,
|
|
@@ -648,9 +813,9 @@ function CoercedSlider(props) {
|
|
|
648
813
|
isDisabled: disabled,
|
|
649
814
|
isInvalid: Boolean(errorMessage),
|
|
650
815
|
label,
|
|
651
|
-
value:
|
|
652
|
-
onBlur:
|
|
653
|
-
onValueChange:
|
|
816
|
+
value: field2.value ?? 0,
|
|
817
|
+
onBlur: field2.onBlur,
|
|
818
|
+
onValueChange: field2.onChange
|
|
654
819
|
}
|
|
655
820
|
);
|
|
656
821
|
}
|
|
@@ -666,20 +831,20 @@ function SliderField(props) {
|
|
|
666
831
|
sliderProps,
|
|
667
832
|
transform
|
|
668
833
|
} = props;
|
|
669
|
-
return /* @__PURE__ */
|
|
834
|
+
return /* @__PURE__ */ React12.createElement(
|
|
670
835
|
Controller8,
|
|
671
836
|
{
|
|
672
837
|
control,
|
|
673
838
|
name,
|
|
674
|
-
render: ({ field, fieldState }) => /* @__PURE__ */
|
|
839
|
+
render: ({ field: field2, fieldState }) => /* @__PURE__ */ React12.createElement("div", { className }, /* @__PURE__ */ React12.createElement(
|
|
675
840
|
CoercedSlider,
|
|
676
841
|
{
|
|
677
842
|
description,
|
|
678
843
|
disabled: isDisabled,
|
|
679
844
|
errorMessage: fieldState.error?.message,
|
|
680
845
|
field: {
|
|
681
|
-
...
|
|
682
|
-
onChange: (value) =>
|
|
846
|
+
...field2,
|
|
847
|
+
onChange: (value) => field2.onChange(transform ? transform(value) : value)
|
|
683
848
|
},
|
|
684
849
|
label,
|
|
685
850
|
sliderProps
|
|
@@ -691,7 +856,7 @@ function SliderField(props) {
|
|
|
691
856
|
}
|
|
692
857
|
|
|
693
858
|
// src/fields/SwitchField.tsx
|
|
694
|
-
import
|
|
859
|
+
import React13 from "react";
|
|
695
860
|
import { Controller as Controller9 } from "react-hook-form";
|
|
696
861
|
function SwitchField(props) {
|
|
697
862
|
const {
|
|
@@ -705,30 +870,30 @@ function SwitchField(props) {
|
|
|
705
870
|
switchProps
|
|
706
871
|
} = props;
|
|
707
872
|
const defaults = useHeroHookFormDefaults();
|
|
708
|
-
return /* @__PURE__ */
|
|
873
|
+
return /* @__PURE__ */ React13.createElement(
|
|
709
874
|
Controller9,
|
|
710
875
|
{
|
|
711
876
|
control,
|
|
712
877
|
name,
|
|
713
|
-
render: ({ field, fieldState }) => /* @__PURE__ */
|
|
878
|
+
render: ({ field: field2, fieldState }) => /* @__PURE__ */ React13.createElement("div", { className }, /* @__PURE__ */ React13.createElement(
|
|
714
879
|
Switch,
|
|
715
880
|
{
|
|
716
881
|
...defaults.switch,
|
|
717
882
|
...switchProps,
|
|
718
883
|
isDisabled,
|
|
719
|
-
isSelected: Boolean(
|
|
720
|
-
onBlur:
|
|
721
|
-
onValueChange: (val) =>
|
|
884
|
+
isSelected: Boolean(field2.value),
|
|
885
|
+
onBlur: field2.onBlur,
|
|
886
|
+
onValueChange: (val) => field2.onChange(val)
|
|
722
887
|
},
|
|
723
888
|
label
|
|
724
|
-
), description ? /* @__PURE__ */
|
|
889
|
+
), description ? /* @__PURE__ */ React13.createElement("p", { className: "text-small text-default-400" }, description) : null, fieldState.error?.message ? /* @__PURE__ */ React13.createElement("p", { className: "text-tiny text-danger mt-1" }, fieldState.error.message) : null),
|
|
725
890
|
rules
|
|
726
891
|
}
|
|
727
892
|
);
|
|
728
893
|
}
|
|
729
894
|
|
|
730
895
|
// src/fields/TextareaField.tsx
|
|
731
|
-
import
|
|
896
|
+
import React14 from "react";
|
|
732
897
|
import { Controller as Controller10 } from "react-hook-form";
|
|
733
898
|
function TextareaField(props) {
|
|
734
899
|
const {
|
|
@@ -742,12 +907,12 @@ function TextareaField(props) {
|
|
|
742
907
|
textareaProps
|
|
743
908
|
} = props;
|
|
744
909
|
const defaults = useHeroHookFormDefaults();
|
|
745
|
-
return /* @__PURE__ */
|
|
910
|
+
return /* @__PURE__ */ React14.createElement(
|
|
746
911
|
Controller10,
|
|
747
912
|
{
|
|
748
913
|
control,
|
|
749
914
|
name,
|
|
750
|
-
render: ({ field, fieldState }) => /* @__PURE__ */
|
|
915
|
+
render: ({ field: field2, fieldState }) => /* @__PURE__ */ React14.createElement("div", { className }, /* @__PURE__ */ React14.createElement(
|
|
751
916
|
Textarea,
|
|
752
917
|
{
|
|
753
918
|
...defaults.textarea,
|
|
@@ -757,9 +922,9 @@ function TextareaField(props) {
|
|
|
757
922
|
isDisabled,
|
|
758
923
|
isInvalid: Boolean(fieldState.error),
|
|
759
924
|
label,
|
|
760
|
-
value:
|
|
761
|
-
onBlur:
|
|
762
|
-
onValueChange:
|
|
925
|
+
value: field2.value ?? "",
|
|
926
|
+
onBlur: field2.onBlur,
|
|
927
|
+
onValueChange: field2.onChange
|
|
763
928
|
}
|
|
764
929
|
)),
|
|
765
930
|
rules
|
|
@@ -768,13 +933,16 @@ function TextareaField(props) {
|
|
|
768
933
|
}
|
|
769
934
|
|
|
770
935
|
// src/components/FormField.tsx
|
|
771
|
-
|
|
936
|
+
var FormField = React15.memo(({
|
|
772
937
|
config,
|
|
773
938
|
form,
|
|
774
939
|
submissionState
|
|
775
|
-
}) {
|
|
940
|
+
}) => {
|
|
941
|
+
if (!form || !form.control) {
|
|
942
|
+
return null;
|
|
943
|
+
}
|
|
776
944
|
const { control } = form;
|
|
777
|
-
const watchedValues =
|
|
945
|
+
const watchedValues = useWatch3({ control });
|
|
778
946
|
if (config.condition && !config.condition(watchedValues)) {
|
|
779
947
|
return null;
|
|
780
948
|
}
|
|
@@ -796,7 +964,7 @@ function FormField({
|
|
|
796
964
|
};
|
|
797
965
|
switch (config.type) {
|
|
798
966
|
case "input":
|
|
799
|
-
return /* @__PURE__ */
|
|
967
|
+
return /* @__PURE__ */ React15.createElement(
|
|
800
968
|
InputField,
|
|
801
969
|
{
|
|
802
970
|
...baseProps,
|
|
@@ -806,7 +974,7 @@ function FormField({
|
|
|
806
974
|
}
|
|
807
975
|
);
|
|
808
976
|
case "textarea":
|
|
809
|
-
return /* @__PURE__ */
|
|
977
|
+
return /* @__PURE__ */ React15.createElement(
|
|
810
978
|
TextareaField,
|
|
811
979
|
{
|
|
812
980
|
...baseProps,
|
|
@@ -816,7 +984,7 @@ function FormField({
|
|
|
816
984
|
}
|
|
817
985
|
);
|
|
818
986
|
case "select":
|
|
819
|
-
return /* @__PURE__ */
|
|
987
|
+
return /* @__PURE__ */ React15.createElement(
|
|
820
988
|
SelectField,
|
|
821
989
|
{
|
|
822
990
|
...baseProps,
|
|
@@ -830,7 +998,7 @@ function FormField({
|
|
|
830
998
|
}
|
|
831
999
|
);
|
|
832
1000
|
case "checkbox":
|
|
833
|
-
return /* @__PURE__ */
|
|
1001
|
+
return /* @__PURE__ */ React15.createElement(
|
|
834
1002
|
CheckboxField,
|
|
835
1003
|
{
|
|
836
1004
|
...baseProps,
|
|
@@ -840,7 +1008,7 @@ function FormField({
|
|
|
840
1008
|
}
|
|
841
1009
|
);
|
|
842
1010
|
case "radio":
|
|
843
|
-
return /* @__PURE__ */
|
|
1011
|
+
return /* @__PURE__ */ React15.createElement(
|
|
844
1012
|
RadioGroupField,
|
|
845
1013
|
{
|
|
846
1014
|
...baseProps,
|
|
@@ -854,7 +1022,7 @@ function FormField({
|
|
|
854
1022
|
}
|
|
855
1023
|
);
|
|
856
1024
|
case "switch":
|
|
857
|
-
return /* @__PURE__ */
|
|
1025
|
+
return /* @__PURE__ */ React15.createElement(
|
|
858
1026
|
SwitchField,
|
|
859
1027
|
{
|
|
860
1028
|
...baseProps,
|
|
@@ -864,7 +1032,7 @@ function FormField({
|
|
|
864
1032
|
}
|
|
865
1033
|
);
|
|
866
1034
|
case "slider":
|
|
867
|
-
return /* @__PURE__ */
|
|
1035
|
+
return /* @__PURE__ */ React15.createElement(
|
|
868
1036
|
SliderField,
|
|
869
1037
|
{
|
|
870
1038
|
...baseProps,
|
|
@@ -874,7 +1042,7 @@ function FormField({
|
|
|
874
1042
|
}
|
|
875
1043
|
);
|
|
876
1044
|
case "date":
|
|
877
|
-
return /* @__PURE__ */
|
|
1045
|
+
return /* @__PURE__ */ React15.createElement(
|
|
878
1046
|
DateField,
|
|
879
1047
|
{
|
|
880
1048
|
...baseProps,
|
|
@@ -884,7 +1052,7 @@ function FormField({
|
|
|
884
1052
|
}
|
|
885
1053
|
);
|
|
886
1054
|
case "file":
|
|
887
|
-
return /* @__PURE__ */
|
|
1055
|
+
return /* @__PURE__ */ React15.createElement(
|
|
888
1056
|
FileField,
|
|
889
1057
|
{
|
|
890
1058
|
...baseProps,
|
|
@@ -896,7 +1064,7 @@ function FormField({
|
|
|
896
1064
|
}
|
|
897
1065
|
);
|
|
898
1066
|
case "fontPicker":
|
|
899
|
-
return /* @__PURE__ */
|
|
1067
|
+
return /* @__PURE__ */ React15.createElement(
|
|
900
1068
|
FontPickerField,
|
|
901
1069
|
{
|
|
902
1070
|
...baseProps,
|
|
@@ -913,13 +1081,39 @@ function FormField({
|
|
|
913
1081
|
isSubmitting: submissionState.isSubmitting,
|
|
914
1082
|
name: config.name
|
|
915
1083
|
});
|
|
1084
|
+
case "conditional":
|
|
1085
|
+
return /* @__PURE__ */ React15.createElement(
|
|
1086
|
+
ConditionalField,
|
|
1087
|
+
{
|
|
1088
|
+
config,
|
|
1089
|
+
control,
|
|
1090
|
+
className: config.className
|
|
1091
|
+
}
|
|
1092
|
+
);
|
|
1093
|
+
case "fieldArray":
|
|
1094
|
+
return /* @__PURE__ */ React15.createElement(
|
|
1095
|
+
FieldArrayField,
|
|
1096
|
+
{
|
|
1097
|
+
config,
|
|
1098
|
+
className: config.className
|
|
1099
|
+
}
|
|
1100
|
+
);
|
|
1101
|
+
case "dynamicSection":
|
|
1102
|
+
return /* @__PURE__ */ React15.createElement(
|
|
1103
|
+
DynamicSectionField,
|
|
1104
|
+
{
|
|
1105
|
+
config,
|
|
1106
|
+
control,
|
|
1107
|
+
className: config.className
|
|
1108
|
+
}
|
|
1109
|
+
);
|
|
916
1110
|
default: {
|
|
917
1111
|
const fieldType = config.type;
|
|
918
1112
|
console.warn(`Unknown field type: ${fieldType}`);
|
|
919
1113
|
return null;
|
|
920
1114
|
}
|
|
921
1115
|
}
|
|
922
|
-
}
|
|
1116
|
+
});
|
|
923
1117
|
|
|
924
1118
|
// src/components/Form.tsx
|
|
925
1119
|
function ConfigurableForm({
|
|
@@ -956,16 +1150,16 @@ function ConfigurableForm({
|
|
|
956
1150
|
});
|
|
957
1151
|
const renderFields = () => {
|
|
958
1152
|
if (layout === "grid") {
|
|
959
|
-
return /* @__PURE__ */
|
|
1153
|
+
return /* @__PURE__ */ React16.createElement(
|
|
960
1154
|
"div",
|
|
961
1155
|
{
|
|
962
1156
|
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"}`
|
|
963
1157
|
},
|
|
964
|
-
fields.map((
|
|
1158
|
+
fields.map((field2) => /* @__PURE__ */ React16.createElement(
|
|
965
1159
|
FormField,
|
|
966
1160
|
{
|
|
967
|
-
key:
|
|
968
|
-
config:
|
|
1161
|
+
key: field2.name,
|
|
1162
|
+
config: field2,
|
|
969
1163
|
form,
|
|
970
1164
|
submissionState
|
|
971
1165
|
}
|
|
@@ -973,21 +1167,21 @@ function ConfigurableForm({
|
|
|
973
1167
|
);
|
|
974
1168
|
}
|
|
975
1169
|
if (layout === "horizontal") {
|
|
976
|
-
return /* @__PURE__ */
|
|
1170
|
+
return /* @__PURE__ */ React16.createElement("div", { className: `grid gap-${spacing} grid-cols-1 md:grid-cols-2` }, fields.map((field2) => /* @__PURE__ */ React16.createElement(
|
|
977
1171
|
FormField,
|
|
978
1172
|
{
|
|
979
|
-
key:
|
|
980
|
-
config:
|
|
1173
|
+
key: field2.name,
|
|
1174
|
+
config: field2,
|
|
981
1175
|
form,
|
|
982
1176
|
submissionState
|
|
983
1177
|
}
|
|
984
1178
|
)));
|
|
985
1179
|
}
|
|
986
|
-
return /* @__PURE__ */
|
|
1180
|
+
return /* @__PURE__ */ React16.createElement("div", { className: `space-y-${spacing}` }, fields.map((field2) => /* @__PURE__ */ React16.createElement(
|
|
987
1181
|
FormField,
|
|
988
1182
|
{
|
|
989
|
-
key:
|
|
990
|
-
config:
|
|
1183
|
+
key: field2.name,
|
|
1184
|
+
config: field2,
|
|
991
1185
|
form,
|
|
992
1186
|
submissionState
|
|
993
1187
|
}
|
|
@@ -997,24 +1191,24 @@ function ConfigurableForm({
|
|
|
997
1191
|
e.preventDefault();
|
|
998
1192
|
void handleSubmit();
|
|
999
1193
|
};
|
|
1000
|
-
return /* @__PURE__ */
|
|
1194
|
+
return /* @__PURE__ */ React16.createElement("form", { className, role: "form", onSubmit: handleFormSubmit }, title && /* @__PURE__ */ React16.createElement("div", { className: "mb-6" }, /* @__PURE__ */ React16.createElement("h2", { className: "text-xl font-semibold text-foreground mb-2" }, title), subtitle && /* @__PURE__ */ React16.createElement("p", { className: "text-sm text-muted-foreground" }, subtitle)), isSubmitted && isSuccess && /* @__PURE__ */ React16.createElement(
|
|
1001
1195
|
"div",
|
|
1002
1196
|
{
|
|
1003
1197
|
className: "mb-6 p-4 bg-success-50 border border-success-200 rounded-lg",
|
|
1004
1198
|
"data-testid": "success-message"
|
|
1005
1199
|
},
|
|
1006
|
-
/* @__PURE__ */
|
|
1007
|
-
/* @__PURE__ */
|
|
1008
|
-
), error && /* @__PURE__ */
|
|
1200
|
+
/* @__PURE__ */ React16.createElement("p", { className: "text-success-800 font-medium" }, "Success!"),
|
|
1201
|
+
/* @__PURE__ */ React16.createElement("p", { className: "text-success-700 text-sm mt-1" }, "Your request has been processed successfully.")
|
|
1202
|
+
), error && /* @__PURE__ */ React16.createElement(
|
|
1009
1203
|
"div",
|
|
1010
1204
|
{
|
|
1011
1205
|
className: "mb-6 p-4 bg-danger-50 border border-danger-200 rounded-lg",
|
|
1012
1206
|
"data-testid": "error-message"
|
|
1013
1207
|
},
|
|
1014
|
-
/* @__PURE__ */
|
|
1015
|
-
/* @__PURE__ */
|
|
1016
|
-
), renderFields(), /* @__PURE__ */
|
|
1017
|
-
|
|
1208
|
+
/* @__PURE__ */ React16.createElement("p", { className: "text-danger-800 font-medium" }, "Error"),
|
|
1209
|
+
/* @__PURE__ */ React16.createElement("p", { className: "text-danger-700 text-sm mt-1" }, error)
|
|
1210
|
+
), renderFields(), /* @__PURE__ */ React16.createElement("div", { className: "mt-6 flex gap-3 justify-end" }, /* @__PURE__ */ React16.createElement(
|
|
1211
|
+
Button3,
|
|
1018
1212
|
{
|
|
1019
1213
|
color: "primary",
|
|
1020
1214
|
isDisabled: isSubmitting,
|
|
@@ -1023,8 +1217,8 @@ function ConfigurableForm({
|
|
|
1023
1217
|
...submitButtonProps
|
|
1024
1218
|
},
|
|
1025
1219
|
submitButtonText
|
|
1026
|
-
), showResetButton && /* @__PURE__ */
|
|
1027
|
-
|
|
1220
|
+
), showResetButton && /* @__PURE__ */ React16.createElement(
|
|
1221
|
+
Button3,
|
|
1028
1222
|
{
|
|
1029
1223
|
isDisabled: isSubmitting,
|
|
1030
1224
|
type: "button",
|
|
@@ -1036,9 +1230,9 @@ function ConfigurableForm({
|
|
|
1036
1230
|
}
|
|
1037
1231
|
|
|
1038
1232
|
// src/hooks/useHeroForm.ts
|
|
1039
|
-
import { useFormContext } from "react-hook-form";
|
|
1233
|
+
import { useFormContext as useFormContext4 } from "react-hook-form";
|
|
1040
1234
|
function useHeroForm() {
|
|
1041
|
-
const form =
|
|
1235
|
+
const form = useFormContext4();
|
|
1042
1236
|
const defaults = useHeroHookFormDefaults();
|
|
1043
1237
|
return {
|
|
1044
1238
|
// All React Hook Form methods and state
|
|
@@ -1049,10 +1243,10 @@ function useHeroForm() {
|
|
|
1049
1243
|
}
|
|
1050
1244
|
|
|
1051
1245
|
// src/providers/FormProvider.tsx
|
|
1052
|
-
import
|
|
1246
|
+
import React17 from "react";
|
|
1053
1247
|
import { FormProvider as RHFProvider } from "react-hook-form";
|
|
1054
1248
|
function FormProvider(props) {
|
|
1055
|
-
return /* @__PURE__ */
|
|
1249
|
+
return /* @__PURE__ */ React17.createElement(RHFProvider, { ...props.methods }, /* @__PURE__ */ React17.createElement(
|
|
1056
1250
|
"form",
|
|
1057
1251
|
{
|
|
1058
1252
|
className: props.className,
|
|
@@ -1065,22 +1259,42 @@ function FormProvider(props) {
|
|
|
1065
1259
|
}
|
|
1066
1260
|
|
|
1067
1261
|
// src/submit/SubmitButton.tsx
|
|
1068
|
-
import
|
|
1262
|
+
import React18 from "react";
|
|
1069
1263
|
function SubmitButton(props) {
|
|
1070
|
-
const ctx =
|
|
1264
|
+
const ctx = useFormContext5();
|
|
1071
1265
|
const loading = props.isLoading ?? ctx.formState.isSubmitting;
|
|
1266
|
+
const enhancedState = props.enhancedState;
|
|
1072
1267
|
const isDisabledFromProps = props.buttonProps?.isDisabled ?? false;
|
|
1073
1268
|
const isDisabled = Boolean(isDisabledFromProps) || Boolean(loading);
|
|
1074
1269
|
const defaults = useHeroHookFormDefaults();
|
|
1075
|
-
|
|
1270
|
+
const getButtonContent = () => {
|
|
1271
|
+
if (enhancedState?.isSuccess) {
|
|
1272
|
+
return /* @__PURE__ */ React18.createElement("span", { className: "inline-flex items-center gap-2" }, "\u2705", props.successText || "Success!");
|
|
1273
|
+
}
|
|
1274
|
+
if (loading) {
|
|
1275
|
+
return /* @__PURE__ */ React18.createElement("span", { className: "inline-flex items-center gap-2" }, "\u23F3", props.loadingText || "Submitting...");
|
|
1276
|
+
}
|
|
1277
|
+
return props.children;
|
|
1278
|
+
};
|
|
1279
|
+
const getButtonColor = () => {
|
|
1280
|
+
if (enhancedState?.isSuccess) {
|
|
1281
|
+
return "success";
|
|
1282
|
+
}
|
|
1283
|
+
if (enhancedState?.isError) {
|
|
1284
|
+
return "danger";
|
|
1285
|
+
}
|
|
1286
|
+
return props.buttonProps?.color || defaults.submitButton.color;
|
|
1287
|
+
};
|
|
1288
|
+
return /* @__PURE__ */ React18.createElement(
|
|
1076
1289
|
Button,
|
|
1077
1290
|
{
|
|
1078
1291
|
type: "submit",
|
|
1079
1292
|
...defaults.submitButton,
|
|
1080
1293
|
...props.buttonProps,
|
|
1081
|
-
isDisabled
|
|
1294
|
+
isDisabled,
|
|
1295
|
+
color: getButtonColor()
|
|
1082
1296
|
},
|
|
1083
|
-
|
|
1297
|
+
getButtonContent()
|
|
1084
1298
|
);
|
|
1085
1299
|
}
|
|
1086
1300
|
|
|
@@ -1271,13 +1485,69 @@ var commonValidations = {
|
|
|
1271
1485
|
requiredCheckbox: (fieldName) => createRequiredCheckboxSchema(fieldName),
|
|
1272
1486
|
url: createUrlSchema()
|
|
1273
1487
|
};
|
|
1488
|
+
var crossFieldValidation = {
|
|
1489
|
+
/**
|
|
1490
|
+
* Password confirmation validation
|
|
1491
|
+
*/
|
|
1492
|
+
passwordConfirmation: (passwordField, confirmField) => {
|
|
1493
|
+
return z.object({
|
|
1494
|
+
[passwordField]: z.string(),
|
|
1495
|
+
[confirmField]: z.string()
|
|
1496
|
+
}).refine(
|
|
1497
|
+
(data) => data[passwordField] === data[confirmField],
|
|
1498
|
+
{
|
|
1499
|
+
message: "Passwords do not match",
|
|
1500
|
+
path: [confirmField]
|
|
1501
|
+
}
|
|
1502
|
+
);
|
|
1503
|
+
},
|
|
1504
|
+
/**
|
|
1505
|
+
* Date range validation
|
|
1506
|
+
*/
|
|
1507
|
+
dateRange: (startField, endField) => {
|
|
1508
|
+
return z.object({
|
|
1509
|
+
[startField]: z.string(),
|
|
1510
|
+
[endField]: z.string()
|
|
1511
|
+
}).refine(
|
|
1512
|
+
(data) => {
|
|
1513
|
+
const startDate = new Date(data[startField]);
|
|
1514
|
+
const endDate = new Date(data[endField]);
|
|
1515
|
+
return startDate < endDate;
|
|
1516
|
+
},
|
|
1517
|
+
{
|
|
1518
|
+
message: "End date must be after start date",
|
|
1519
|
+
path: [endField]
|
|
1520
|
+
}
|
|
1521
|
+
);
|
|
1522
|
+
},
|
|
1523
|
+
/**
|
|
1524
|
+
* Conditional required field validation
|
|
1525
|
+
*/
|
|
1526
|
+
conditionalRequired: (field2, conditionField, conditionValue) => {
|
|
1527
|
+
return z.object({
|
|
1528
|
+
[field2]: z.string(),
|
|
1529
|
+
[conditionField]: z.any()
|
|
1530
|
+
}).refine(
|
|
1531
|
+
(data) => {
|
|
1532
|
+
if (data[conditionField] === conditionValue) {
|
|
1533
|
+
return data[field2] && data[field2].trim().length > 0;
|
|
1534
|
+
}
|
|
1535
|
+
return true;
|
|
1536
|
+
},
|
|
1537
|
+
{
|
|
1538
|
+
message: "This field is required",
|
|
1539
|
+
path: [field2]
|
|
1540
|
+
}
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1543
|
+
};
|
|
1274
1544
|
|
|
1275
1545
|
// src/index.ts
|
|
1276
|
-
import { useFormContext as
|
|
1546
|
+
import { useFormContext as useFormContext5 } from "react-hook-form";
|
|
1277
1547
|
|
|
1278
1548
|
// src/components/ZodForm.tsx
|
|
1279
|
-
import
|
|
1280
|
-
import { Button as
|
|
1549
|
+
import React20 from "react";
|
|
1550
|
+
import { Button as Button5 } from "@heroui/react";
|
|
1281
1551
|
|
|
1282
1552
|
// src/zod-integration.ts
|
|
1283
1553
|
import { useForm as useForm2 } from "react-hook-form";
|
|
@@ -1320,6 +1590,146 @@ function createZodFormConfig(schema, fields, defaultValues) {
|
|
|
1320
1590
|
};
|
|
1321
1591
|
}
|
|
1322
1592
|
|
|
1593
|
+
// src/hooks/useEnhancedFormState.ts
|
|
1594
|
+
import { useCallback, useEffect, useState as useState2 } from "react";
|
|
1595
|
+
function useEnhancedFormState(form, options = {}) {
|
|
1596
|
+
const {
|
|
1597
|
+
onSuccess,
|
|
1598
|
+
onError,
|
|
1599
|
+
successMessage = "Form submitted successfully!",
|
|
1600
|
+
errorMessage = "An error occurred. Please try again.",
|
|
1601
|
+
autoReset = true,
|
|
1602
|
+
resetDelay = 3e3
|
|
1603
|
+
} = options;
|
|
1604
|
+
const [status, setStatus] = useState2("idle");
|
|
1605
|
+
const [error, setError] = useState2(void 0);
|
|
1606
|
+
const [submittedData, setSubmittedData] = useState2(void 0);
|
|
1607
|
+
const { formState, getValues } = form;
|
|
1608
|
+
const { errors, touchedFields, dirtyFields, isSubmitting } = formState;
|
|
1609
|
+
useEffect(() => {
|
|
1610
|
+
if (isSubmitting) {
|
|
1611
|
+
setStatus("submitting");
|
|
1612
|
+
}
|
|
1613
|
+
}, [isSubmitting]);
|
|
1614
|
+
useEffect(() => {
|
|
1615
|
+
if (status === "success" && autoReset) {
|
|
1616
|
+
const timer = setTimeout(() => {
|
|
1617
|
+
setStatus("idle");
|
|
1618
|
+
setSubmittedData(void 0);
|
|
1619
|
+
setError(void 0);
|
|
1620
|
+
}, resetDelay);
|
|
1621
|
+
return () => clearTimeout(timer);
|
|
1622
|
+
}
|
|
1623
|
+
}, [status, autoReset, resetDelay]);
|
|
1624
|
+
const handleSuccess = useCallback((data) => {
|
|
1625
|
+
setStatus("success");
|
|
1626
|
+
setSubmittedData(data);
|
|
1627
|
+
setError(void 0);
|
|
1628
|
+
onSuccess?.(data);
|
|
1629
|
+
}, [onSuccess]);
|
|
1630
|
+
const handleError = useCallback((errorMessage2) => {
|
|
1631
|
+
setStatus("error");
|
|
1632
|
+
setError(errorMessage2);
|
|
1633
|
+
setSubmittedData(void 0);
|
|
1634
|
+
onError?.(errorMessage2);
|
|
1635
|
+
}, [onError]);
|
|
1636
|
+
const reset = useCallback(() => {
|
|
1637
|
+
setStatus("idle");
|
|
1638
|
+
setError(void 0);
|
|
1639
|
+
setSubmittedData(void 0);
|
|
1640
|
+
}, []);
|
|
1641
|
+
return {
|
|
1642
|
+
status,
|
|
1643
|
+
isSubmitting,
|
|
1644
|
+
isSuccess: status === "success",
|
|
1645
|
+
isError: status === "error",
|
|
1646
|
+
error,
|
|
1647
|
+
submittedData,
|
|
1648
|
+
touchedFields: new Set(Object.keys(touchedFields)),
|
|
1649
|
+
dirtyFields: new Set(Object.keys(dirtyFields)),
|
|
1650
|
+
hasErrors: Object.keys(errors).length > 0,
|
|
1651
|
+
errorCount: Object.keys(errors).length,
|
|
1652
|
+
handleSuccess,
|
|
1653
|
+
handleError,
|
|
1654
|
+
reset
|
|
1655
|
+
};
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
// src/components/FormStatus.tsx
|
|
1659
|
+
import React19 from "react";
|
|
1660
|
+
import { Button as Button4 } from "@heroui/react";
|
|
1661
|
+
function FormStatus({
|
|
1662
|
+
state,
|
|
1663
|
+
onDismiss,
|
|
1664
|
+
className = "",
|
|
1665
|
+
showDetails = false
|
|
1666
|
+
}) {
|
|
1667
|
+
const { status, isSubmitting, isSuccess, isError, error, submittedData } = state;
|
|
1668
|
+
if (status === "idle") {
|
|
1669
|
+
return null;
|
|
1670
|
+
}
|
|
1671
|
+
if (isSubmitting) {
|
|
1672
|
+
return /* @__PURE__ */ React19.createElement("div", { className: `flex items-center gap-3 p-4 bg-blue-50 border border-blue-200 rounded-lg ${className}` }, /* @__PURE__ */ React19.createElement("span", { className: "text-blue-600" }, "\u23F3"), /* @__PURE__ */ React19.createElement("div", null, /* @__PURE__ */ React19.createElement("p", { className: "text-sm font-medium text-blue-900" }, "Submitting form..."), showDetails && /* @__PURE__ */ React19.createElement("p", { className: "text-xs text-blue-700" }, "Please wait while we process your request.")));
|
|
1673
|
+
}
|
|
1674
|
+
if (isSuccess) {
|
|
1675
|
+
return /* @__PURE__ */ React19.createElement("div", { className: `flex items-center gap-3 p-4 bg-green-50 border border-green-200 rounded-lg ${className}`, "data-testid": "success-message" }, /* @__PURE__ */ React19.createElement("span", { className: "text-green-600" }, "\u2705"), /* @__PURE__ */ React19.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React19.createElement("p", { className: "text-sm font-medium text-green-900" }, "Form submitted successfully!"), showDetails && submittedData && /* @__PURE__ */ React19.createElement("p", { className: "text-xs text-green-700" }, "Your data has been saved. Thank you for your submission.")), onDismiss && /* @__PURE__ */ React19.createElement(
|
|
1676
|
+
Button4,
|
|
1677
|
+
{
|
|
1678
|
+
size: "sm",
|
|
1679
|
+
variant: "light",
|
|
1680
|
+
isIconOnly: true,
|
|
1681
|
+
onPress: onDismiss,
|
|
1682
|
+
"aria-label": "Dismiss success message"
|
|
1683
|
+
},
|
|
1684
|
+
"\u2715"
|
|
1685
|
+
));
|
|
1686
|
+
}
|
|
1687
|
+
if (isError && error) {
|
|
1688
|
+
return /* @__PURE__ */ React19.createElement("div", { className: `flex items-center gap-3 p-4 bg-red-50 border border-red-200 rounded-lg ${className}`, "data-testid": "error-message" }, /* @__PURE__ */ React19.createElement("span", { className: "text-red-600" }, "\u26A0\uFE0F"), /* @__PURE__ */ React19.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React19.createElement("p", { className: "text-sm font-medium text-red-900" }, "Error submitting form"), /* @__PURE__ */ React19.createElement("p", { className: "text-xs text-red-700" }, error)), onDismiss && /* @__PURE__ */ React19.createElement(
|
|
1689
|
+
Button4,
|
|
1690
|
+
{
|
|
1691
|
+
size: "sm",
|
|
1692
|
+
variant: "light",
|
|
1693
|
+
isIconOnly: true,
|
|
1694
|
+
onPress: onDismiss,
|
|
1695
|
+
"aria-label": "Dismiss error message"
|
|
1696
|
+
},
|
|
1697
|
+
"\u2715"
|
|
1698
|
+
));
|
|
1699
|
+
}
|
|
1700
|
+
return null;
|
|
1701
|
+
}
|
|
1702
|
+
function FormToast({
|
|
1703
|
+
state,
|
|
1704
|
+
onDismiss,
|
|
1705
|
+
position = "top-right",
|
|
1706
|
+
duration = 5e3
|
|
1707
|
+
}) {
|
|
1708
|
+
const [isVisible, setIsVisible] = React19.useState(false);
|
|
1709
|
+
React19.useEffect(() => {
|
|
1710
|
+
if (state.isSuccess || state.isError) {
|
|
1711
|
+
setIsVisible(true);
|
|
1712
|
+
if (duration > 0) {
|
|
1713
|
+
const timer = setTimeout(() => {
|
|
1714
|
+
setIsVisible(false);
|
|
1715
|
+
onDismiss?.();
|
|
1716
|
+
}, duration);
|
|
1717
|
+
return () => clearTimeout(timer);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
}, [state.isSuccess, state.isError, duration, onDismiss]);
|
|
1721
|
+
if (!isVisible) {
|
|
1722
|
+
return null;
|
|
1723
|
+
}
|
|
1724
|
+
const positionClasses = {
|
|
1725
|
+
"top-right": "top-4 right-4",
|
|
1726
|
+
"top-left": "top-4 left-4",
|
|
1727
|
+
"bottom-right": "bottom-4 right-4",
|
|
1728
|
+
"bottom-left": "bottom-4 left-4"
|
|
1729
|
+
};
|
|
1730
|
+
return /* @__PURE__ */ React19.createElement("div", { className: `fixed z-50 ${positionClasses[position]}` }, /* @__PURE__ */ React19.createElement(FormStatus, { state, onDismiss }));
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1323
1733
|
// src/components/ZodForm.tsx
|
|
1324
1734
|
function ZodForm({
|
|
1325
1735
|
className,
|
|
@@ -1340,97 +1750,83 @@ function ZodForm({
|
|
|
1340
1750
|
title
|
|
1341
1751
|
}) {
|
|
1342
1752
|
const form = useZodForm(config);
|
|
1343
|
-
const
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1753
|
+
const enhancedState = useEnhancedFormState(form, {
|
|
1754
|
+
onSuccess,
|
|
1755
|
+
onError: (error) => onError?.({ message: error, field: "form" }),
|
|
1756
|
+
autoReset: true,
|
|
1757
|
+
resetDelay: 3e3
|
|
1348
1758
|
});
|
|
1349
1759
|
const handleSubmit = async () => {
|
|
1350
|
-
setSubmissionState((prev) => ({
|
|
1351
|
-
...prev,
|
|
1352
|
-
error: void 0,
|
|
1353
|
-
isSubmitting: true
|
|
1354
|
-
}));
|
|
1355
|
-
const isValid = await form.trigger();
|
|
1356
|
-
if (!isValid) {
|
|
1357
|
-
setSubmissionState({
|
|
1358
|
-
error: "Please fix the validation errors above",
|
|
1359
|
-
isSubmitted: true,
|
|
1360
|
-
isSubmitting: false,
|
|
1361
|
-
isSuccess: false
|
|
1362
|
-
});
|
|
1363
|
-
return;
|
|
1364
|
-
}
|
|
1365
1760
|
try {
|
|
1366
|
-
await form.handleSubmit(
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
onSuccess?.(form.getValues());
|
|
1761
|
+
await form.handleSubmit(
|
|
1762
|
+
async (formData) => {
|
|
1763
|
+
await onSubmit(formData);
|
|
1764
|
+
enhancedState.handleSuccess(formData);
|
|
1765
|
+
},
|
|
1766
|
+
(errors) => {
|
|
1767
|
+
enhancedState.handleError("Please fix the validation errors above");
|
|
1768
|
+
}
|
|
1769
|
+
)();
|
|
1376
1770
|
} catch (error) {
|
|
1377
1771
|
const errorMessage = error instanceof Error ? error.message : "An error occurred";
|
|
1378
|
-
|
|
1379
|
-
error: errorMessage,
|
|
1380
|
-
isSubmitted: true,
|
|
1381
|
-
isSubmitting: false,
|
|
1382
|
-
isSuccess: false
|
|
1383
|
-
});
|
|
1384
|
-
onError?.({
|
|
1385
|
-
message: errorMessage
|
|
1386
|
-
});
|
|
1772
|
+
enhancedState.handleError(errorMessage);
|
|
1387
1773
|
}
|
|
1388
1774
|
};
|
|
1389
1775
|
const resetForm = () => {
|
|
1390
1776
|
form.reset();
|
|
1391
|
-
|
|
1392
|
-
error: void 0,
|
|
1393
|
-
isSubmitted: false,
|
|
1394
|
-
isSubmitting: false,
|
|
1395
|
-
isSuccess: false
|
|
1396
|
-
});
|
|
1777
|
+
enhancedState.reset();
|
|
1397
1778
|
};
|
|
1398
1779
|
const renderFields = () => {
|
|
1399
1780
|
if (layout === "grid") {
|
|
1400
|
-
return /* @__PURE__ */
|
|
1781
|
+
return /* @__PURE__ */ React20.createElement(
|
|
1401
1782
|
"div",
|
|
1402
1783
|
{
|
|
1403
1784
|
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"}`
|
|
1404
1785
|
},
|
|
1405
|
-
config.fields.map((
|
|
1786
|
+
config.fields.map((field2) => /* @__PURE__ */ React20.createElement(
|
|
1406
1787
|
FormField,
|
|
1407
1788
|
{
|
|
1408
|
-
key:
|
|
1409
|
-
config:
|
|
1789
|
+
key: field2.name,
|
|
1790
|
+
config: field2,
|
|
1410
1791
|
form,
|
|
1411
|
-
submissionState
|
|
1792
|
+
submissionState: {
|
|
1793
|
+
isSubmitting: enhancedState.isSubmitting,
|
|
1794
|
+
isSubmitted: enhancedState.status !== "idle",
|
|
1795
|
+
isSuccess: enhancedState.isSuccess,
|
|
1796
|
+
error: enhancedState.error
|
|
1797
|
+
}
|
|
1412
1798
|
}
|
|
1413
1799
|
))
|
|
1414
1800
|
);
|
|
1415
1801
|
}
|
|
1416
1802
|
if (layout === "horizontal") {
|
|
1417
|
-
return /* @__PURE__ */
|
|
1803
|
+
return /* @__PURE__ */ React20.createElement("div", { className: `grid gap-${spacing} grid-cols-1 md:grid-cols-2` }, config.fields.map((field2) => /* @__PURE__ */ React20.createElement(
|
|
1418
1804
|
FormField,
|
|
1419
1805
|
{
|
|
1420
|
-
key:
|
|
1421
|
-
config:
|
|
1806
|
+
key: field2.name,
|
|
1807
|
+
config: field2,
|
|
1422
1808
|
form,
|
|
1423
|
-
submissionState
|
|
1809
|
+
submissionState: {
|
|
1810
|
+
isSubmitting: enhancedState.isSubmitting,
|
|
1811
|
+
isSubmitted: enhancedState.status !== "idle",
|
|
1812
|
+
isSuccess: enhancedState.isSuccess,
|
|
1813
|
+
error: enhancedState.error
|
|
1814
|
+
}
|
|
1424
1815
|
}
|
|
1425
1816
|
)));
|
|
1426
1817
|
}
|
|
1427
|
-
return /* @__PURE__ */
|
|
1818
|
+
return /* @__PURE__ */ React20.createElement("div", { className: `space-y-${spacing}` }, config.fields.map((field2) => /* @__PURE__ */ React20.createElement(
|
|
1428
1819
|
FormField,
|
|
1429
1820
|
{
|
|
1430
|
-
key:
|
|
1431
|
-
config:
|
|
1821
|
+
key: field2.name,
|
|
1822
|
+
config: field2,
|
|
1432
1823
|
form,
|
|
1433
|
-
submissionState
|
|
1824
|
+
submissionState: {
|
|
1825
|
+
isSubmitting: enhancedState.isSubmitting,
|
|
1826
|
+
isSubmitted: enhancedState.status !== "idle",
|
|
1827
|
+
isSuccess: enhancedState.isSuccess,
|
|
1828
|
+
error: enhancedState.error
|
|
1829
|
+
}
|
|
1434
1830
|
}
|
|
1435
1831
|
)));
|
|
1436
1832
|
};
|
|
@@ -1438,7 +1834,7 @@ function ZodForm({
|
|
|
1438
1834
|
e.preventDefault();
|
|
1439
1835
|
void handleSubmit();
|
|
1440
1836
|
};
|
|
1441
|
-
|
|
1837
|
+
React20.useEffect(() => {
|
|
1442
1838
|
if (config.onError && Object.keys(form.formState.errors).length > 0) {
|
|
1443
1839
|
config.onError(form.formState.errors);
|
|
1444
1840
|
}
|
|
@@ -1447,42 +1843,33 @@ function ZodForm({
|
|
|
1447
1843
|
return render({
|
|
1448
1844
|
errors: form.formState.errors,
|
|
1449
1845
|
form,
|
|
1450
|
-
isSubmitted:
|
|
1451
|
-
isSubmitting:
|
|
1452
|
-
isSuccess:
|
|
1846
|
+
isSubmitted: enhancedState.status !== "idle",
|
|
1847
|
+
isSubmitting: enhancedState.isSubmitting,
|
|
1848
|
+
isSuccess: enhancedState.isSuccess,
|
|
1453
1849
|
values: form.getValues()
|
|
1454
1850
|
});
|
|
1455
1851
|
}
|
|
1456
|
-
return /* @__PURE__ */
|
|
1457
|
-
|
|
1458
|
-
{
|
|
1459
|
-
className: "mb-6 p-4 bg-success-50 border border-success-200 rounded-lg",
|
|
1460
|
-
"data-testid": "success-message"
|
|
1461
|
-
},
|
|
1462
|
-
/* @__PURE__ */ React16.createElement("p", { className: "text-success-800 font-medium" }, "Success!"),
|
|
1463
|
-
/* @__PURE__ */ React16.createElement("p", { className: "text-success-700 text-sm mt-1" }, "Your request has been processed successfully.")
|
|
1464
|
-
), submissionState.error && errorDisplay !== "none" && /* @__PURE__ */ React16.createElement(
|
|
1465
|
-
"div",
|
|
1852
|
+
return /* @__PURE__ */ React20.createElement("form", { className, role: "form", onSubmit: handleFormSubmit }, title && /* @__PURE__ */ React20.createElement("div", { className: "mb-6" }, /* @__PURE__ */ React20.createElement("h2", { className: "text-xl font-semibold text-foreground mb-2" }, title), subtitle && /* @__PURE__ */ React20.createElement("p", { className: "text-sm text-muted-foreground" }, subtitle)), /* @__PURE__ */ React20.createElement(
|
|
1853
|
+
FormStatus,
|
|
1466
1854
|
{
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
Button3,
|
|
1855
|
+
state: enhancedState,
|
|
1856
|
+
onDismiss: () => enhancedState.reset(),
|
|
1857
|
+
showDetails: true
|
|
1858
|
+
}
|
|
1859
|
+
), renderFields(), /* @__PURE__ */ React20.createElement("div", { className: "mt-6 flex gap-3 justify-end" }, /* @__PURE__ */ React20.createElement(
|
|
1860
|
+
Button5,
|
|
1474
1861
|
{
|
|
1475
1862
|
color: "primary",
|
|
1476
|
-
isDisabled:
|
|
1477
|
-
isLoading:
|
|
1863
|
+
isDisabled: enhancedState.isSubmitting,
|
|
1864
|
+
isLoading: enhancedState.isSubmitting,
|
|
1478
1865
|
type: "submit",
|
|
1479
1866
|
...submitButtonProps
|
|
1480
1867
|
},
|
|
1481
|
-
submitButtonText
|
|
1482
|
-
), showResetButton && /* @__PURE__ */
|
|
1483
|
-
|
|
1868
|
+
enhancedState.isSuccess ? "Success!" : submitButtonText
|
|
1869
|
+
), showResetButton && /* @__PURE__ */ React20.createElement(
|
|
1870
|
+
Button5,
|
|
1484
1871
|
{
|
|
1485
|
-
isDisabled:
|
|
1872
|
+
isDisabled: enhancedState.isSubmitting,
|
|
1486
1873
|
type: "button",
|
|
1487
1874
|
variant: "bordered",
|
|
1488
1875
|
onPress: resetForm
|
|
@@ -1490,14 +1877,1209 @@ function ZodForm({
|
|
|
1490
1877
|
resetButtonText
|
|
1491
1878
|
)));
|
|
1492
1879
|
}
|
|
1880
|
+
|
|
1881
|
+
// src/builders/BasicFormBuilder.ts
|
|
1882
|
+
var BasicFormBuilder = class {
|
|
1883
|
+
constructor() {
|
|
1884
|
+
this.fields = [];
|
|
1885
|
+
}
|
|
1886
|
+
/**
|
|
1887
|
+
* Add an input field
|
|
1888
|
+
*/
|
|
1889
|
+
input(name, label, type = "text") {
|
|
1890
|
+
this.fields.push({
|
|
1891
|
+
name,
|
|
1892
|
+
label,
|
|
1893
|
+
type: "input",
|
|
1894
|
+
inputProps: { type }
|
|
1895
|
+
});
|
|
1896
|
+
return this;
|
|
1897
|
+
}
|
|
1898
|
+
/**
|
|
1899
|
+
* Add a textarea field
|
|
1900
|
+
*/
|
|
1901
|
+
textarea(name, label, placeholder) {
|
|
1902
|
+
this.fields.push({
|
|
1903
|
+
name,
|
|
1904
|
+
label,
|
|
1905
|
+
type: "textarea",
|
|
1906
|
+
textareaProps: { placeholder }
|
|
1907
|
+
});
|
|
1908
|
+
return this;
|
|
1909
|
+
}
|
|
1910
|
+
/**
|
|
1911
|
+
* Add a select field
|
|
1912
|
+
*/
|
|
1913
|
+
select(name, label, options) {
|
|
1914
|
+
this.fields.push({
|
|
1915
|
+
name,
|
|
1916
|
+
label,
|
|
1917
|
+
type: "select",
|
|
1918
|
+
options
|
|
1919
|
+
});
|
|
1920
|
+
return this;
|
|
1921
|
+
}
|
|
1922
|
+
/**
|
|
1923
|
+
* Add a checkbox field
|
|
1924
|
+
*/
|
|
1925
|
+
checkbox(name, label) {
|
|
1926
|
+
this.fields.push({
|
|
1927
|
+
name,
|
|
1928
|
+
label,
|
|
1929
|
+
type: "checkbox"
|
|
1930
|
+
});
|
|
1931
|
+
return this;
|
|
1932
|
+
}
|
|
1933
|
+
/**
|
|
1934
|
+
* Add a switch field
|
|
1935
|
+
*/
|
|
1936
|
+
switch(name, label) {
|
|
1937
|
+
this.fields.push({
|
|
1938
|
+
name,
|
|
1939
|
+
label,
|
|
1940
|
+
type: "switch"
|
|
1941
|
+
});
|
|
1942
|
+
return this;
|
|
1943
|
+
}
|
|
1944
|
+
/**
|
|
1945
|
+
* Build the final field configuration array
|
|
1946
|
+
*/
|
|
1947
|
+
build() {
|
|
1948
|
+
return this.fields;
|
|
1949
|
+
}
|
|
1950
|
+
};
|
|
1951
|
+
function createBasicFormBuilder() {
|
|
1952
|
+
return new BasicFormBuilder();
|
|
1953
|
+
}
|
|
1954
|
+
var FormFieldHelpers = {
|
|
1955
|
+
/**
|
|
1956
|
+
* Create an input field
|
|
1957
|
+
*/
|
|
1958
|
+
input: (name, label, type = "text") => ({
|
|
1959
|
+
name,
|
|
1960
|
+
label,
|
|
1961
|
+
type: "input",
|
|
1962
|
+
inputProps: { type }
|
|
1963
|
+
}),
|
|
1964
|
+
/**
|
|
1965
|
+
* Create a textarea field
|
|
1966
|
+
*/
|
|
1967
|
+
textarea: (name, label, placeholder) => ({
|
|
1968
|
+
name,
|
|
1969
|
+
label,
|
|
1970
|
+
type: "textarea",
|
|
1971
|
+
textareaProps: { placeholder }
|
|
1972
|
+
}),
|
|
1973
|
+
/**
|
|
1974
|
+
* Create a select field
|
|
1975
|
+
*/
|
|
1976
|
+
select: (name, label, options) => ({
|
|
1977
|
+
name,
|
|
1978
|
+
label,
|
|
1979
|
+
type: "select",
|
|
1980
|
+
options
|
|
1981
|
+
}),
|
|
1982
|
+
/**
|
|
1983
|
+
* Create a checkbox field
|
|
1984
|
+
*/
|
|
1985
|
+
checkbox: (name, label) => ({
|
|
1986
|
+
name,
|
|
1987
|
+
label,
|
|
1988
|
+
type: "checkbox"
|
|
1989
|
+
}),
|
|
1990
|
+
/**
|
|
1991
|
+
* Create a switch field
|
|
1992
|
+
*/
|
|
1993
|
+
switch: (name, label) => ({
|
|
1994
|
+
name,
|
|
1995
|
+
label,
|
|
1996
|
+
type: "switch"
|
|
1997
|
+
})
|
|
1998
|
+
};
|
|
1999
|
+
var CommonFields = {
|
|
2000
|
+
/**
|
|
2001
|
+
* Personal information fields
|
|
2002
|
+
*/
|
|
2003
|
+
personal: () => [
|
|
2004
|
+
FormFieldHelpers.input("firstName", "First Name"),
|
|
2005
|
+
FormFieldHelpers.input("lastName", "Last Name"),
|
|
2006
|
+
FormFieldHelpers.input("email", "Email", "email"),
|
|
2007
|
+
FormFieldHelpers.input("phone", "Phone", "tel")
|
|
2008
|
+
],
|
|
2009
|
+
/**
|
|
2010
|
+
* Address fields
|
|
2011
|
+
*/
|
|
2012
|
+
address: () => [
|
|
2013
|
+
FormFieldHelpers.input("street", "Street Address"),
|
|
2014
|
+
FormFieldHelpers.input("city", "City"),
|
|
2015
|
+
FormFieldHelpers.input("state", "State/Province"),
|
|
2016
|
+
FormFieldHelpers.input("zipCode", "ZIP/Postal Code"),
|
|
2017
|
+
FormFieldHelpers.select(
|
|
2018
|
+
"country",
|
|
2019
|
+
"Country",
|
|
2020
|
+
[
|
|
2021
|
+
{ label: "Select a country", value: "" },
|
|
2022
|
+
{ label: "United States", value: "us" },
|
|
2023
|
+
{ label: "Canada", value: "ca" },
|
|
2024
|
+
{ label: "United Kingdom", value: "uk" },
|
|
2025
|
+
{ label: "Australia", value: "au" },
|
|
2026
|
+
{ label: "Germany", value: "de" },
|
|
2027
|
+
{ label: "France", value: "fr" }
|
|
2028
|
+
]
|
|
2029
|
+
)
|
|
2030
|
+
],
|
|
2031
|
+
/**
|
|
2032
|
+
* Terms and conditions fields
|
|
2033
|
+
*/
|
|
2034
|
+
terms: () => [
|
|
2035
|
+
FormFieldHelpers.checkbox("terms", "I agree to the terms and conditions"),
|
|
2036
|
+
FormFieldHelpers.checkbox("privacy", "I agree to the privacy policy"),
|
|
2037
|
+
FormFieldHelpers.checkbox("newsletter", "Subscribe to newsletter")
|
|
2038
|
+
]
|
|
2039
|
+
};
|
|
2040
|
+
|
|
2041
|
+
// src/builders/AdvancedFormBuilder.ts
|
|
2042
|
+
function inputField(name, label, props) {
|
|
2043
|
+
return {
|
|
2044
|
+
name,
|
|
2045
|
+
label,
|
|
2046
|
+
type: "input",
|
|
2047
|
+
...props && {
|
|
2048
|
+
inputProps: {
|
|
2049
|
+
type: props.type || "text",
|
|
2050
|
+
placeholder: props.placeholder,
|
|
2051
|
+
description: props.description,
|
|
2052
|
+
disabled: props.isDisabled,
|
|
2053
|
+
className: props.className
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
};
|
|
2057
|
+
}
|
|
2058
|
+
function textareaField(name, label, props) {
|
|
2059
|
+
return {
|
|
2060
|
+
name,
|
|
2061
|
+
label,
|
|
2062
|
+
type: "textarea",
|
|
2063
|
+
...props && {
|
|
2064
|
+
textareaProps: {
|
|
2065
|
+
placeholder: props.placeholder,
|
|
2066
|
+
description: props.description,
|
|
2067
|
+
disabled: props.isDisabled,
|
|
2068
|
+
className: props.className,
|
|
2069
|
+
rows: props.rows
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
2074
|
+
function selectField(name, label, options, props) {
|
|
2075
|
+
return {
|
|
2076
|
+
name,
|
|
2077
|
+
label,
|
|
2078
|
+
type: "select",
|
|
2079
|
+
options
|
|
2080
|
+
};
|
|
2081
|
+
}
|
|
2082
|
+
function checkboxField(name, label, props) {
|
|
2083
|
+
return {
|
|
2084
|
+
name,
|
|
2085
|
+
label,
|
|
2086
|
+
type: "checkbox",
|
|
2087
|
+
...props && {
|
|
2088
|
+
checkboxProps: {
|
|
2089
|
+
disabled: props.isDisabled,
|
|
2090
|
+
className: props.className
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
};
|
|
2094
|
+
}
|
|
2095
|
+
function switchField(name, label, props) {
|
|
2096
|
+
return {
|
|
2097
|
+
name,
|
|
2098
|
+
label,
|
|
2099
|
+
type: "switch",
|
|
2100
|
+
...props && {
|
|
2101
|
+
switchProps: {
|
|
2102
|
+
disabled: props.isDisabled,
|
|
2103
|
+
className: props.className
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
};
|
|
2107
|
+
}
|
|
2108
|
+
function radioField(name, label, options, props) {
|
|
2109
|
+
return {
|
|
2110
|
+
name,
|
|
2111
|
+
label,
|
|
2112
|
+
type: "radio",
|
|
2113
|
+
radioOptions: options,
|
|
2114
|
+
...props && {
|
|
2115
|
+
radioProps: {
|
|
2116
|
+
isDisabled: props.isDisabled,
|
|
2117
|
+
className: props.className,
|
|
2118
|
+
orientation: props.orientation
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
};
|
|
2122
|
+
}
|
|
2123
|
+
function sliderField(name, label, props) {
|
|
2124
|
+
return {
|
|
2125
|
+
name,
|
|
2126
|
+
label,
|
|
2127
|
+
type: "slider",
|
|
2128
|
+
...props && {
|
|
2129
|
+
sliderProps: {
|
|
2130
|
+
min: props.min || 0,
|
|
2131
|
+
max: props.max || 100,
|
|
2132
|
+
step: props.step || 1,
|
|
2133
|
+
disabled: props.isDisabled || false,
|
|
2134
|
+
className: props.className || ""
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
};
|
|
2138
|
+
}
|
|
2139
|
+
function dateField(name, label, props) {
|
|
2140
|
+
return {
|
|
2141
|
+
name,
|
|
2142
|
+
label,
|
|
2143
|
+
type: "date",
|
|
2144
|
+
...props && {
|
|
2145
|
+
dateProps: {
|
|
2146
|
+
placeholder: props.placeholder || "",
|
|
2147
|
+
disabled: props.isDisabled || false,
|
|
2148
|
+
className: props.className || ""
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
};
|
|
2152
|
+
}
|
|
2153
|
+
function fileField(name, label, props) {
|
|
2154
|
+
return {
|
|
2155
|
+
name,
|
|
2156
|
+
label,
|
|
2157
|
+
type: "file",
|
|
2158
|
+
...props && {
|
|
2159
|
+
fileProps: {
|
|
2160
|
+
accept: props.accept || "",
|
|
2161
|
+
multiple: props.multiple || false,
|
|
2162
|
+
disabled: props.isDisabled || false,
|
|
2163
|
+
className: props.className || ""
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
};
|
|
2167
|
+
}
|
|
2168
|
+
function fontPickerField(name, label, props) {
|
|
2169
|
+
return {
|
|
2170
|
+
name,
|
|
2171
|
+
label,
|
|
2172
|
+
type: "fontPicker",
|
|
2173
|
+
...props && {
|
|
2174
|
+
fontPickerProps: {
|
|
2175
|
+
disabled: props.isDisabled || false,
|
|
2176
|
+
className: props.className || ""
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
};
|
|
2180
|
+
}
|
|
2181
|
+
function createField(type, name, label, optionsOrProps, props) {
|
|
2182
|
+
switch (type) {
|
|
2183
|
+
case "input":
|
|
2184
|
+
return inputField(name, label, optionsOrProps);
|
|
2185
|
+
case "textarea":
|
|
2186
|
+
return textareaField(name, label, optionsOrProps);
|
|
2187
|
+
case "select":
|
|
2188
|
+
return selectField(name, label, optionsOrProps, props);
|
|
2189
|
+
case "checkbox":
|
|
2190
|
+
return checkboxField(name, label, optionsOrProps);
|
|
2191
|
+
case "switch":
|
|
2192
|
+
return switchField(name, label, optionsOrProps);
|
|
2193
|
+
case "radio":
|
|
2194
|
+
return radioField(name, label, optionsOrProps, props);
|
|
2195
|
+
case "slider":
|
|
2196
|
+
return sliderField(name, label, optionsOrProps);
|
|
2197
|
+
case "date":
|
|
2198
|
+
return dateField(name, label, optionsOrProps);
|
|
2199
|
+
case "file":
|
|
2200
|
+
return fileField(name, label, optionsOrProps);
|
|
2201
|
+
case "fontPicker":
|
|
2202
|
+
return fontPickerField(name, label, optionsOrProps);
|
|
2203
|
+
default:
|
|
2204
|
+
throw new Error(`Unknown field type: ${type}`);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
var AdvancedFieldBuilder = class {
|
|
2208
|
+
constructor() {
|
|
2209
|
+
this.fields = [];
|
|
2210
|
+
}
|
|
2211
|
+
field(type, name, label, optionsOrProps, props) {
|
|
2212
|
+
this.fields.push(createField(type, name, label, optionsOrProps, props));
|
|
2213
|
+
return this;
|
|
2214
|
+
}
|
|
2215
|
+
/**
|
|
2216
|
+
* Add a conditional field that shows/hides based on form data
|
|
2217
|
+
*/
|
|
2218
|
+
conditionalField(name, condition, field2) {
|
|
2219
|
+
this.fields.push({
|
|
2220
|
+
name,
|
|
2221
|
+
type: "conditional",
|
|
2222
|
+
condition,
|
|
2223
|
+
field: field2
|
|
2224
|
+
});
|
|
2225
|
+
return this;
|
|
2226
|
+
}
|
|
2227
|
+
/**
|
|
2228
|
+
* Add a field array for dynamic repeating field groups
|
|
2229
|
+
*/
|
|
2230
|
+
fieldArray(name, label, fields, options) {
|
|
2231
|
+
this.fields.push({
|
|
2232
|
+
name,
|
|
2233
|
+
label,
|
|
2234
|
+
type: "fieldArray",
|
|
2235
|
+
fields,
|
|
2236
|
+
min: options?.min,
|
|
2237
|
+
max: options?.max,
|
|
2238
|
+
addButtonText: options?.addButtonText,
|
|
2239
|
+
removeButtonText: options?.removeButtonText
|
|
2240
|
+
});
|
|
2241
|
+
return this;
|
|
2242
|
+
}
|
|
2243
|
+
/**
|
|
2244
|
+
* Add a dynamic section that shows/hides based on form data
|
|
2245
|
+
*/
|
|
2246
|
+
dynamicSection(name, condition, fields, options) {
|
|
2247
|
+
this.fields.push({
|
|
2248
|
+
name,
|
|
2249
|
+
type: "dynamicSection",
|
|
2250
|
+
condition,
|
|
2251
|
+
fields,
|
|
2252
|
+
title: options?.title,
|
|
2253
|
+
description: options?.description
|
|
2254
|
+
});
|
|
2255
|
+
return this;
|
|
2256
|
+
}
|
|
2257
|
+
/**
|
|
2258
|
+
* Build the final field configuration array
|
|
2259
|
+
*/
|
|
2260
|
+
build() {
|
|
2261
|
+
return this.fields;
|
|
2262
|
+
}
|
|
2263
|
+
};
|
|
2264
|
+
var FieldArrayItemBuilder = class {
|
|
2265
|
+
constructor() {
|
|
2266
|
+
this.fields = [];
|
|
2267
|
+
}
|
|
2268
|
+
field(type, name, label, optionsOrProps, props) {
|
|
2269
|
+
this.fields.push(createField(type, name, label, optionsOrProps, props));
|
|
2270
|
+
return this;
|
|
2271
|
+
}
|
|
2272
|
+
/**
|
|
2273
|
+
* Build the field array item configuration
|
|
2274
|
+
*/
|
|
2275
|
+
build() {
|
|
2276
|
+
return this.fields;
|
|
2277
|
+
}
|
|
2278
|
+
};
|
|
2279
|
+
function createFieldArrayItemBuilder() {
|
|
2280
|
+
return new FieldArrayItemBuilder();
|
|
2281
|
+
}
|
|
2282
|
+
var FieldArrayBuilder = class {
|
|
2283
|
+
constructor(arrayName) {
|
|
2284
|
+
this.arrayName = arrayName;
|
|
2285
|
+
this.fields = [];
|
|
2286
|
+
}
|
|
2287
|
+
field(type, name, label, optionsOrProps, props) {
|
|
2288
|
+
const fullPath = `${this.arrayName}.${name}`;
|
|
2289
|
+
const fieldConfig = createField(type, fullPath, label, optionsOrProps, props);
|
|
2290
|
+
this.fields.push(fieldConfig);
|
|
2291
|
+
return this;
|
|
2292
|
+
}
|
|
2293
|
+
build() {
|
|
2294
|
+
return this.fields;
|
|
2295
|
+
}
|
|
2296
|
+
};
|
|
2297
|
+
function createFieldArrayBuilder(arrayName) {
|
|
2298
|
+
return new FieldArrayBuilder(arrayName);
|
|
2299
|
+
}
|
|
2300
|
+
function createAdvancedBuilder() {
|
|
2301
|
+
return new AdvancedFieldBuilder();
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
// src/builders/TypeInferredBuilder.ts
|
|
2305
|
+
import { z as z3 } from "zod";
|
|
2306
|
+
var TypeInferredBuilder = class {
|
|
2307
|
+
constructor() {
|
|
2308
|
+
this.schemaFields = {};
|
|
2309
|
+
this.formFields = [];
|
|
2310
|
+
}
|
|
2311
|
+
/**
|
|
2312
|
+
* Add a text field
|
|
2313
|
+
*/
|
|
2314
|
+
text(name, label, options) {
|
|
2315
|
+
const { minLength, maxLength, pattern, ...fieldOptions } = options || {};
|
|
2316
|
+
let zodType = z3.string();
|
|
2317
|
+
if (minLength) zodType = zodType.min(minLength, `${label} must be at least ${minLength} characters`);
|
|
2318
|
+
if (maxLength) zodType = zodType.max(maxLength, `${label} must be no more than ${maxLength} characters`);
|
|
2319
|
+
if (pattern) zodType = zodType.regex(new RegExp(pattern), `${label} format is invalid`);
|
|
2320
|
+
this.schemaFields[name] = zodType;
|
|
2321
|
+
this.formFields.push({
|
|
2322
|
+
name,
|
|
2323
|
+
label,
|
|
2324
|
+
type: "input",
|
|
2325
|
+
inputProps: { type: "text", ...fieldOptions }
|
|
2326
|
+
});
|
|
2327
|
+
return this;
|
|
2328
|
+
}
|
|
2329
|
+
/**
|
|
2330
|
+
* Add an email field
|
|
2331
|
+
*/
|
|
2332
|
+
email(name, label, options) {
|
|
2333
|
+
this.schemaFields[name] = z3.string().email(`Please enter a valid email address`);
|
|
2334
|
+
this.formFields.push({
|
|
2335
|
+
name,
|
|
2336
|
+
label,
|
|
2337
|
+
type: "input",
|
|
2338
|
+
inputProps: { type: "email", ...options }
|
|
2339
|
+
});
|
|
2340
|
+
return this;
|
|
2341
|
+
}
|
|
2342
|
+
/**
|
|
2343
|
+
* Add a number field
|
|
2344
|
+
*/
|
|
2345
|
+
number(name, label, options) {
|
|
2346
|
+
const { min, max, step, ...fieldOptions } = options || {};
|
|
2347
|
+
let zodType = z3.number();
|
|
2348
|
+
if (min !== void 0) zodType = zodType.min(min, `${label} must be at least ${min}`);
|
|
2349
|
+
if (max !== void 0) zodType = zodType.max(max, `${label} must be no more than ${max}`);
|
|
2350
|
+
this.schemaFields[name] = zodType;
|
|
2351
|
+
this.formFields.push({
|
|
2352
|
+
name,
|
|
2353
|
+
label,
|
|
2354
|
+
type: "input",
|
|
2355
|
+
inputProps: { type: "number", min, max, step, ...fieldOptions }
|
|
2356
|
+
});
|
|
2357
|
+
return this;
|
|
2358
|
+
}
|
|
2359
|
+
/**
|
|
2360
|
+
* Add a textarea field
|
|
2361
|
+
*/
|
|
2362
|
+
textarea(name, label, options) {
|
|
2363
|
+
const { minLength, ...fieldOptions } = options || {};
|
|
2364
|
+
let zodType = z3.string();
|
|
2365
|
+
if (minLength) zodType = zodType.min(minLength, `${label} must be at least ${minLength} characters`);
|
|
2366
|
+
this.schemaFields[name] = zodType;
|
|
2367
|
+
this.formFields.push({
|
|
2368
|
+
name,
|
|
2369
|
+
label,
|
|
2370
|
+
type: "textarea",
|
|
2371
|
+
textareaProps: fieldOptions
|
|
2372
|
+
});
|
|
2373
|
+
return this;
|
|
2374
|
+
}
|
|
2375
|
+
/**
|
|
2376
|
+
* Add a select field
|
|
2377
|
+
*/
|
|
2378
|
+
select(name, label, options, fieldOptions) {
|
|
2379
|
+
this.schemaFields[name] = z3.string().min(1, `Please select a ${label.toLowerCase()}`);
|
|
2380
|
+
this.formFields.push({
|
|
2381
|
+
name,
|
|
2382
|
+
label,
|
|
2383
|
+
type: "select",
|
|
2384
|
+
options
|
|
2385
|
+
});
|
|
2386
|
+
return this;
|
|
2387
|
+
}
|
|
2388
|
+
/**
|
|
2389
|
+
* Add a checkbox field
|
|
2390
|
+
*/
|
|
2391
|
+
checkbox(name, label, options) {
|
|
2392
|
+
const { required = false, ...fieldOptions } = options || {};
|
|
2393
|
+
let zodType = z3.boolean();
|
|
2394
|
+
if (required) {
|
|
2395
|
+
zodType = zodType.refine((val) => val === true, `You must agree to ${label.toLowerCase()}`);
|
|
2396
|
+
}
|
|
2397
|
+
this.schemaFields[name] = zodType;
|
|
2398
|
+
this.formFields.push({
|
|
2399
|
+
name,
|
|
2400
|
+
label,
|
|
2401
|
+
type: "checkbox",
|
|
2402
|
+
checkboxProps: fieldOptions
|
|
2403
|
+
});
|
|
2404
|
+
return this;
|
|
2405
|
+
}
|
|
2406
|
+
/**
|
|
2407
|
+
* Add a switch field
|
|
2408
|
+
*/
|
|
2409
|
+
switch(name, label, options) {
|
|
2410
|
+
this.schemaFields[name] = z3.boolean().optional();
|
|
2411
|
+
this.formFields.push({
|
|
2412
|
+
name,
|
|
2413
|
+
label,
|
|
2414
|
+
type: "switch",
|
|
2415
|
+
switchProps: options
|
|
2416
|
+
});
|
|
2417
|
+
return this;
|
|
2418
|
+
}
|
|
2419
|
+
/**
|
|
2420
|
+
* Add a radio field
|
|
2421
|
+
*/
|
|
2422
|
+
radio(name, label, options, fieldOptions) {
|
|
2423
|
+
this.schemaFields[name] = z3.string().min(1, `Please select a ${label.toLowerCase()}`);
|
|
2424
|
+
this.formFields.push({
|
|
2425
|
+
name,
|
|
2426
|
+
label,
|
|
2427
|
+
type: "radio",
|
|
2428
|
+
radioOptions: options,
|
|
2429
|
+
radioProps: fieldOptions
|
|
2430
|
+
});
|
|
2431
|
+
return this;
|
|
2432
|
+
}
|
|
2433
|
+
/**
|
|
2434
|
+
* Add a slider field
|
|
2435
|
+
*/
|
|
2436
|
+
slider(name, label, options) {
|
|
2437
|
+
const { min = 0, max = 100, step = 1, ...fieldOptions } = options || {};
|
|
2438
|
+
let zodType = z3.number();
|
|
2439
|
+
if (min !== void 0) zodType = zodType.min(min, `${label} must be at least ${min}`);
|
|
2440
|
+
if (max !== void 0) zodType = zodType.max(max, `${label} must be no more than ${max}`);
|
|
2441
|
+
this.schemaFields[name] = zodType;
|
|
2442
|
+
this.formFields.push({
|
|
2443
|
+
name,
|
|
2444
|
+
label,
|
|
2445
|
+
type: "slider",
|
|
2446
|
+
sliderProps: { min, max, step, ...fieldOptions }
|
|
2447
|
+
});
|
|
2448
|
+
return this;
|
|
2449
|
+
}
|
|
2450
|
+
/**
|
|
2451
|
+
* Add a date field
|
|
2452
|
+
*/
|
|
2453
|
+
date(name, label, options) {
|
|
2454
|
+
this.schemaFields[name] = z3.string().min(1, `${label} is required`);
|
|
2455
|
+
this.formFields.push({
|
|
2456
|
+
name,
|
|
2457
|
+
label,
|
|
2458
|
+
type: "date",
|
|
2459
|
+
dateProps: options
|
|
2460
|
+
});
|
|
2461
|
+
return this;
|
|
2462
|
+
}
|
|
2463
|
+
/**
|
|
2464
|
+
* Add a file field
|
|
2465
|
+
*/
|
|
2466
|
+
file(name, label, options) {
|
|
2467
|
+
this.schemaFields[name] = z3.any().optional();
|
|
2468
|
+
this.formFields.push({
|
|
2469
|
+
name,
|
|
2470
|
+
label,
|
|
2471
|
+
type: "file",
|
|
2472
|
+
fileProps: options
|
|
2473
|
+
});
|
|
2474
|
+
return this;
|
|
2475
|
+
}
|
|
2476
|
+
/**
|
|
2477
|
+
* Build the final schema and fields
|
|
2478
|
+
*/
|
|
2479
|
+
build() {
|
|
2480
|
+
return {
|
|
2481
|
+
schema: z3.object(this.schemaFields),
|
|
2482
|
+
fields: this.formFields
|
|
2483
|
+
};
|
|
2484
|
+
}
|
|
2485
|
+
};
|
|
2486
|
+
function createTypeInferredBuilder() {
|
|
2487
|
+
return new TypeInferredBuilder();
|
|
2488
|
+
}
|
|
2489
|
+
function defineInferredForm(fieldDefinitions) {
|
|
2490
|
+
const builder = createTypeInferredBuilder();
|
|
2491
|
+
fieldDefinitions(builder);
|
|
2492
|
+
return builder.build();
|
|
2493
|
+
}
|
|
2494
|
+
var field = {
|
|
2495
|
+
text: (name, label, options) => {
|
|
2496
|
+
const builder = new TypeInferredBuilder();
|
|
2497
|
+
return builder.text(name, label, options);
|
|
2498
|
+
},
|
|
2499
|
+
email: (name, label, options) => {
|
|
2500
|
+
const builder = new TypeInferredBuilder();
|
|
2501
|
+
return builder.email(name, label, options);
|
|
2502
|
+
},
|
|
2503
|
+
number: (name, label, options) => {
|
|
2504
|
+
const builder = new TypeInferredBuilder();
|
|
2505
|
+
return builder.number(name, label, options);
|
|
2506
|
+
},
|
|
2507
|
+
textarea: (name, label, options) => {
|
|
2508
|
+
const builder = new TypeInferredBuilder();
|
|
2509
|
+
return builder.textarea(name, label, options);
|
|
2510
|
+
},
|
|
2511
|
+
select: (name, label, options, fieldOptions) => {
|
|
2512
|
+
const builder = new TypeInferredBuilder();
|
|
2513
|
+
return builder.select(name, label, options, fieldOptions);
|
|
2514
|
+
},
|
|
2515
|
+
checkbox: (name, label, options) => {
|
|
2516
|
+
const builder = new TypeInferredBuilder();
|
|
2517
|
+
return builder.checkbox(name, label, options);
|
|
2518
|
+
},
|
|
2519
|
+
switch: (name, label, options) => {
|
|
2520
|
+
const builder = new TypeInferredBuilder();
|
|
2521
|
+
return builder.switch(name, label, options);
|
|
2522
|
+
},
|
|
2523
|
+
radio: (name, label, options, fieldOptions) => {
|
|
2524
|
+
const builder = new TypeInferredBuilder();
|
|
2525
|
+
return builder.radio(name, label, options, fieldOptions);
|
|
2526
|
+
},
|
|
2527
|
+
slider: (name, label, options) => {
|
|
2528
|
+
const builder = new TypeInferredBuilder();
|
|
2529
|
+
return builder.slider(name, label, options);
|
|
2530
|
+
},
|
|
2531
|
+
date: (name, label, options) => {
|
|
2532
|
+
const builder = new TypeInferredBuilder();
|
|
2533
|
+
return builder.date(name, label, options);
|
|
2534
|
+
},
|
|
2535
|
+
file: (name, label, options) => {
|
|
2536
|
+
const builder = new TypeInferredBuilder();
|
|
2537
|
+
return builder.file(name, label, options);
|
|
2538
|
+
}
|
|
2539
|
+
};
|
|
2540
|
+
|
|
2541
|
+
// src/builders/NestedPathBuilder.ts
|
|
2542
|
+
var NestedPathBuilder = class {
|
|
2543
|
+
constructor() {
|
|
2544
|
+
this.fields = [];
|
|
2545
|
+
}
|
|
2546
|
+
/**
|
|
2547
|
+
* Create a nested object path builder
|
|
2548
|
+
* Usage: builder.nest("address").field("street", "Street Address")
|
|
2549
|
+
*/
|
|
2550
|
+
nest(path) {
|
|
2551
|
+
return new NestedObjectBuilder(this, path);
|
|
2552
|
+
}
|
|
2553
|
+
/**
|
|
2554
|
+
* Create a section-based path builder
|
|
2555
|
+
* Usage: builder.section("shipping").field("street", "Street Address")
|
|
2556
|
+
*/
|
|
2557
|
+
section(path) {
|
|
2558
|
+
return new SectionBuilder(this, path);
|
|
2559
|
+
}
|
|
2560
|
+
/**
|
|
2561
|
+
* Add a field with single path
|
|
2562
|
+
* Usage: builder.field("firstName", "First Name")
|
|
2563
|
+
*/
|
|
2564
|
+
field(name, label, type = "input", props) {
|
|
2565
|
+
this.fields.push({
|
|
2566
|
+
name,
|
|
2567
|
+
label,
|
|
2568
|
+
type,
|
|
2569
|
+
...props
|
|
2570
|
+
});
|
|
2571
|
+
return this;
|
|
2572
|
+
}
|
|
2573
|
+
/**
|
|
2574
|
+
* Add a field with path segments
|
|
2575
|
+
* Usage: builder.fieldPath(["user", "profile", "name"], "Full Name")
|
|
2576
|
+
*/
|
|
2577
|
+
fieldPath(path, label, type = "input", props) {
|
|
2578
|
+
const name = path.join(".");
|
|
2579
|
+
this.fields.push({
|
|
2580
|
+
name,
|
|
2581
|
+
label,
|
|
2582
|
+
type,
|
|
2583
|
+
...props
|
|
2584
|
+
});
|
|
2585
|
+
return this;
|
|
2586
|
+
}
|
|
2587
|
+
/**
|
|
2588
|
+
* Add a field with template literal path
|
|
2589
|
+
* Usage: builder.field`user.profile.name`("Full Name")
|
|
2590
|
+
*/
|
|
2591
|
+
fieldTemplate(path, ...args) {
|
|
2592
|
+
const pathString = path[0];
|
|
2593
|
+
return new FieldTemplateBuilder(this, pathString);
|
|
2594
|
+
}
|
|
2595
|
+
/**
|
|
2596
|
+
* Return to the parent builder (no-op for root builder)
|
|
2597
|
+
*/
|
|
2598
|
+
end() {
|
|
2599
|
+
return this;
|
|
2600
|
+
}
|
|
2601
|
+
build() {
|
|
2602
|
+
return this.fields;
|
|
2603
|
+
}
|
|
2604
|
+
};
|
|
2605
|
+
var NestedObjectBuilder = class _NestedObjectBuilder {
|
|
2606
|
+
constructor(parent, path) {
|
|
2607
|
+
this.parent = parent;
|
|
2608
|
+
this.path = path;
|
|
2609
|
+
}
|
|
2610
|
+
/**
|
|
2611
|
+
* Add a field to the current nested path
|
|
2612
|
+
*/
|
|
2613
|
+
field(fieldName, label, type = "input", props) {
|
|
2614
|
+
const fullPath = `${this.path}.${fieldName}`;
|
|
2615
|
+
this.parent.fields.push({
|
|
2616
|
+
name: fullPath,
|
|
2617
|
+
label,
|
|
2618
|
+
type,
|
|
2619
|
+
...props
|
|
2620
|
+
});
|
|
2621
|
+
return this;
|
|
2622
|
+
}
|
|
2623
|
+
/**
|
|
2624
|
+
* Nest deeper into the object
|
|
2625
|
+
*/
|
|
2626
|
+
nest(subPath) {
|
|
2627
|
+
return new _NestedObjectBuilder(this.parent, `${this.path}.${subPath}`);
|
|
2628
|
+
}
|
|
2629
|
+
/**
|
|
2630
|
+
* Return to the parent builder
|
|
2631
|
+
*/
|
|
2632
|
+
end() {
|
|
2633
|
+
return this.parent;
|
|
2634
|
+
}
|
|
2635
|
+
};
|
|
2636
|
+
var SectionBuilder = class {
|
|
2637
|
+
constructor(parent, path) {
|
|
2638
|
+
this.parent = parent;
|
|
2639
|
+
this.path = path;
|
|
2640
|
+
}
|
|
2641
|
+
/**
|
|
2642
|
+
* Add a field to the current section
|
|
2643
|
+
*/
|
|
2644
|
+
field(fieldName, label, type = "input", props) {
|
|
2645
|
+
const fullPath = `${this.path}.${fieldName}`;
|
|
2646
|
+
this.parent.fields.push({
|
|
2647
|
+
name: fullPath,
|
|
2648
|
+
label,
|
|
2649
|
+
type,
|
|
2650
|
+
...props
|
|
2651
|
+
});
|
|
2652
|
+
return this;
|
|
2653
|
+
}
|
|
2654
|
+
/**
|
|
2655
|
+
* Add multiple fields to the section
|
|
2656
|
+
*/
|
|
2657
|
+
fields(fieldDefinitions) {
|
|
2658
|
+
fieldDefinitions.forEach((field2) => {
|
|
2659
|
+
this.field(field2.name, field2.label, field2.type, field2.props);
|
|
2660
|
+
});
|
|
2661
|
+
return this;
|
|
2662
|
+
}
|
|
2663
|
+
/**
|
|
2664
|
+
* Nest deeper into the section
|
|
2665
|
+
*/
|
|
2666
|
+
nest(subPath) {
|
|
2667
|
+
return new NestedObjectBuilder(this.parent, `${this.path}.${subPath}`);
|
|
2668
|
+
}
|
|
2669
|
+
/**
|
|
2670
|
+
* Return to the parent builder
|
|
2671
|
+
*/
|
|
2672
|
+
end() {
|
|
2673
|
+
return this.parent;
|
|
2674
|
+
}
|
|
2675
|
+
};
|
|
2676
|
+
var FieldTemplateBuilder = class {
|
|
2677
|
+
constructor(parent, path) {
|
|
2678
|
+
this.parent = parent;
|
|
2679
|
+
this.path = path;
|
|
2680
|
+
}
|
|
2681
|
+
/**
|
|
2682
|
+
* Complete the field definition
|
|
2683
|
+
*/
|
|
2684
|
+
complete(label, type = "input", props) {
|
|
2685
|
+
this.parent.fields.push({
|
|
2686
|
+
name: this.path,
|
|
2687
|
+
label,
|
|
2688
|
+
type,
|
|
2689
|
+
...props
|
|
2690
|
+
});
|
|
2691
|
+
return this.parent;
|
|
2692
|
+
}
|
|
2693
|
+
};
|
|
2694
|
+
function createNestedPathBuilder() {
|
|
2695
|
+
return new NestedPathBuilder();
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2698
|
+
// src/hooks/useDebouncedValidation.ts
|
|
2699
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef } from "react";
|
|
2700
|
+
function useDebouncedValidation(form, options = {}) {
|
|
2701
|
+
const { delay = 300, fields, enabled = true } = options;
|
|
2702
|
+
const timeoutRef = useRef(void 0);
|
|
2703
|
+
const lastValuesRef = useRef({});
|
|
2704
|
+
const debouncedTrigger = useCallback2(() => {
|
|
2705
|
+
if (!enabled) return;
|
|
2706
|
+
if (timeoutRef.current) {
|
|
2707
|
+
clearTimeout(timeoutRef.current);
|
|
2708
|
+
timeoutRef.current = void 0;
|
|
2709
|
+
}
|
|
2710
|
+
timeoutRef.current = setTimeout(async () => {
|
|
2711
|
+
const currentValues = form.getValues();
|
|
2712
|
+
const lastValues = lastValuesRef.current;
|
|
2713
|
+
const hasChanges = fields ? fields.some((field2) => currentValues[field2] !== lastValues[field2]) : Object.keys(currentValues).some((key) => currentValues[key] !== lastValues[key]);
|
|
2714
|
+
if (hasChanges) {
|
|
2715
|
+
lastValuesRef.current = { ...currentValues };
|
|
2716
|
+
if (fields && fields.length > 0) {
|
|
2717
|
+
await form.trigger(fields);
|
|
2718
|
+
} else {
|
|
2719
|
+
await form.trigger();
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
}, delay);
|
|
2723
|
+
}, [form, delay, fields, enabled]);
|
|
2724
|
+
useEffect2(() => {
|
|
2725
|
+
return () => {
|
|
2726
|
+
if (timeoutRef.current) {
|
|
2727
|
+
clearTimeout(timeoutRef.current);
|
|
2728
|
+
timeoutRef.current = void 0;
|
|
2729
|
+
}
|
|
2730
|
+
};
|
|
2731
|
+
}, []);
|
|
2732
|
+
useEffect2(() => {
|
|
2733
|
+
if (form.formState.isSubmitSuccessful) {
|
|
2734
|
+
lastValuesRef.current = {};
|
|
2735
|
+
}
|
|
2736
|
+
}, [form.formState.isSubmitSuccessful]);
|
|
2737
|
+
return {
|
|
2738
|
+
debouncedTrigger,
|
|
2739
|
+
isDebouncing: !!timeoutRef.current
|
|
2740
|
+
};
|
|
2741
|
+
}
|
|
2742
|
+
function useDebouncedFieldValidation(form, fieldName, options = {}) {
|
|
2743
|
+
const { delay = 300, enabled = true } = options;
|
|
2744
|
+
const timeoutRef = useRef(void 0);
|
|
2745
|
+
const debouncedFieldTrigger = useCallback2(() => {
|
|
2746
|
+
if (!enabled) return;
|
|
2747
|
+
if (timeoutRef.current) {
|
|
2748
|
+
clearTimeout(timeoutRef.current);
|
|
2749
|
+
}
|
|
2750
|
+
timeoutRef.current = setTimeout(async () => {
|
|
2751
|
+
await form.trigger(fieldName);
|
|
2752
|
+
}, delay);
|
|
2753
|
+
}, [form, fieldName, delay, enabled]);
|
|
2754
|
+
useEffect2(() => {
|
|
2755
|
+
return () => {
|
|
2756
|
+
if (timeoutRef.current) {
|
|
2757
|
+
clearTimeout(timeoutRef.current);
|
|
2758
|
+
timeoutRef.current = void 0;
|
|
2759
|
+
}
|
|
2760
|
+
};
|
|
2761
|
+
}, []);
|
|
2762
|
+
return {
|
|
2763
|
+
debouncedFieldTrigger,
|
|
2764
|
+
isDebouncing: !!timeoutRef.current
|
|
2765
|
+
};
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2768
|
+
// src/hooks/useInferredForm.ts
|
|
2769
|
+
import { useForm as useForm3 } from "react-hook-form";
|
|
2770
|
+
var zodResolver;
|
|
2771
|
+
try {
|
|
2772
|
+
zodResolver = __require("@hookform/resolvers/zod").zodResolver;
|
|
2773
|
+
} catch {
|
|
2774
|
+
}
|
|
2775
|
+
function useInferredForm(schema, fields, options = {}) {
|
|
2776
|
+
const {
|
|
2777
|
+
defaultValues,
|
|
2778
|
+
mode = "onChange",
|
|
2779
|
+
reValidateMode = "onChange",
|
|
2780
|
+
shouldFocusError = true,
|
|
2781
|
+
shouldUnregister = false,
|
|
2782
|
+
delayError = 0
|
|
2783
|
+
} = options;
|
|
2784
|
+
return useForm3({
|
|
2785
|
+
resolver: zodResolver ? zodResolver(schema) : void 0,
|
|
2786
|
+
defaultValues,
|
|
2787
|
+
mode,
|
|
2788
|
+
reValidateMode,
|
|
2789
|
+
shouldFocusError,
|
|
2790
|
+
shouldUnregister,
|
|
2791
|
+
delayError
|
|
2792
|
+
});
|
|
2793
|
+
}
|
|
2794
|
+
function useTypeInferredForm(formConfig, options = {}) {
|
|
2795
|
+
return useInferredForm(formConfig.schema, formConfig.fields, options);
|
|
2796
|
+
}
|
|
2797
|
+
|
|
2798
|
+
// src/utils/performance.ts
|
|
2799
|
+
import { useCallback as useCallback3, useMemo as useMemo2, useRef as useRef2 } from "react";
|
|
2800
|
+
function debounce(func, delay) {
|
|
2801
|
+
let timeoutId;
|
|
2802
|
+
return (...args) => {
|
|
2803
|
+
clearTimeout(timeoutId);
|
|
2804
|
+
timeoutId = setTimeout(() => func(...args), delay);
|
|
2805
|
+
};
|
|
2806
|
+
}
|
|
2807
|
+
function throttle(func, limit) {
|
|
2808
|
+
let inThrottle;
|
|
2809
|
+
return (...args) => {
|
|
2810
|
+
if (!inThrottle) {
|
|
2811
|
+
func(...args);
|
|
2812
|
+
inThrottle = true;
|
|
2813
|
+
setTimeout(() => inThrottle = false, limit);
|
|
2814
|
+
}
|
|
2815
|
+
};
|
|
2816
|
+
}
|
|
2817
|
+
function useMemoizedCallback(callback, deps) {
|
|
2818
|
+
const callbackRef = useRef2(callback);
|
|
2819
|
+
callbackRef.current = callback;
|
|
2820
|
+
return useCallback3(
|
|
2821
|
+
((...args) => callbackRef.current(...args)),
|
|
2822
|
+
deps
|
|
2823
|
+
);
|
|
2824
|
+
}
|
|
2825
|
+
function shallowEqual(prevProps, nextProps) {
|
|
2826
|
+
const prevKeys = Object.keys(prevProps);
|
|
2827
|
+
const nextKeys = Object.keys(nextProps);
|
|
2828
|
+
if (prevKeys.length !== nextKeys.length) {
|
|
2829
|
+
return false;
|
|
2830
|
+
}
|
|
2831
|
+
for (const key of prevKeys) {
|
|
2832
|
+
if (prevProps[key] !== nextProps[key]) {
|
|
2833
|
+
return false;
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
return true;
|
|
2837
|
+
}
|
|
2838
|
+
function deepEqual(prevProps, nextProps) {
|
|
2839
|
+
if (prevProps === nextProps) {
|
|
2840
|
+
return true;
|
|
2841
|
+
}
|
|
2842
|
+
if (typeof prevProps !== typeof nextProps) {
|
|
2843
|
+
return false;
|
|
2844
|
+
}
|
|
2845
|
+
if (typeof prevProps !== "object" || prevProps === null || nextProps === null) {
|
|
2846
|
+
return prevProps === nextProps;
|
|
2847
|
+
}
|
|
2848
|
+
const prevKeys = Object.keys(prevProps);
|
|
2849
|
+
const nextKeys = Object.keys(nextProps);
|
|
2850
|
+
if (prevKeys.length !== nextKeys.length) {
|
|
2851
|
+
return false;
|
|
2852
|
+
}
|
|
2853
|
+
for (const key of prevKeys) {
|
|
2854
|
+
if (!nextKeys.includes(key)) {
|
|
2855
|
+
return false;
|
|
2856
|
+
}
|
|
2857
|
+
if (!deepEqual(prevProps[key], nextProps[key])) {
|
|
2858
|
+
return false;
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
return true;
|
|
2862
|
+
}
|
|
2863
|
+
function usePerformanceMonitor(componentName, enabled = false) {
|
|
2864
|
+
const renderCountRef = useRef2(0);
|
|
2865
|
+
const lastRenderTimeRef = useRef2(Date.now());
|
|
2866
|
+
if (enabled) {
|
|
2867
|
+
renderCountRef.current += 1;
|
|
2868
|
+
const now = Date.now();
|
|
2869
|
+
const timeSinceLastRender = now - lastRenderTimeRef.current;
|
|
2870
|
+
console.log(`[Performance] ${componentName}:`, {
|
|
2871
|
+
renderCount: renderCountRef.current,
|
|
2872
|
+
timeSinceLastRender: `${timeSinceLastRender}ms`
|
|
2873
|
+
});
|
|
2874
|
+
lastRenderTimeRef.current = now;
|
|
2875
|
+
}
|
|
2876
|
+
return {
|
|
2877
|
+
renderCount: renderCountRef.current,
|
|
2878
|
+
resetRenderCount: () => {
|
|
2879
|
+
renderCountRef.current = 0;
|
|
2880
|
+
}
|
|
2881
|
+
};
|
|
2882
|
+
}
|
|
2883
|
+
function createOptimizedFieldHandler(onChange, options = {}) {
|
|
2884
|
+
const { debounce: debounceMs, throttle: throttleMs, validate = false } = options;
|
|
2885
|
+
let handler = onChange;
|
|
2886
|
+
if (throttleMs) {
|
|
2887
|
+
handler = throttle(handler, throttleMs);
|
|
2888
|
+
}
|
|
2889
|
+
if (debounceMs) {
|
|
2890
|
+
handler = debounce(handler, debounceMs);
|
|
2891
|
+
}
|
|
2892
|
+
return handler;
|
|
2893
|
+
}
|
|
2894
|
+
function useMemoizedFieldProps(props, deps) {
|
|
2895
|
+
return useMemo2(() => props, deps);
|
|
2896
|
+
}
|
|
2897
|
+
function useBatchedFieldUpdates(form, fields) {
|
|
2898
|
+
const batchRef = useRef2({});
|
|
2899
|
+
const timeoutRef = useRef2(void 0);
|
|
2900
|
+
const batchUpdate = useCallback3((fieldName, value) => {
|
|
2901
|
+
batchRef.current[fieldName] = value;
|
|
2902
|
+
if (timeoutRef.current) {
|
|
2903
|
+
clearTimeout(timeoutRef.current);
|
|
2904
|
+
}
|
|
2905
|
+
timeoutRef.current = setTimeout(() => {
|
|
2906
|
+
Object.entries(batchRef.current).forEach(([key, val]) => {
|
|
2907
|
+
form.setValue(key, val);
|
|
2908
|
+
});
|
|
2909
|
+
batchRef.current = {};
|
|
2910
|
+
}, 16);
|
|
2911
|
+
}, [form]);
|
|
2912
|
+
return { batchUpdate };
|
|
2913
|
+
}
|
|
2914
|
+
|
|
2915
|
+
// src/builders/validation-helpers.ts
|
|
2916
|
+
import { z as z4 } from "zod";
|
|
2917
|
+
var validationPatterns = {
|
|
2918
|
+
// Email validation
|
|
2919
|
+
email: z4.string().email("Please enter a valid email address"),
|
|
2920
|
+
// Phone number validation (US format)
|
|
2921
|
+
phoneUS: z4.string().regex(
|
|
2922
|
+
/^\(\d{3}\) \d{3}-\d{4}$/,
|
|
2923
|
+
"Please enter a valid phone number (XXX) XXX-XXXX"
|
|
2924
|
+
),
|
|
2925
|
+
// Phone number validation (international)
|
|
2926
|
+
phoneInternational: z4.string().regex(
|
|
2927
|
+
/^\+?[\d\s\-\(\)]+$/,
|
|
2928
|
+
"Please enter a valid phone number"
|
|
2929
|
+
),
|
|
2930
|
+
// URL validation
|
|
2931
|
+
url: z4.string().url("Please enter a valid URL"),
|
|
2932
|
+
// Password validation
|
|
2933
|
+
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(/[^A-Za-z0-9]/, "Password must contain at least one special character"),
|
|
2934
|
+
// Strong password validation
|
|
2935
|
+
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(/[^A-Za-z0-9]/, "Password must contain at least one special character"),
|
|
2936
|
+
// Credit card validation
|
|
2937
|
+
creditCard: z4.string().regex(
|
|
2938
|
+
/^[0-9]{4}[\s\-]?[0-9]{4}[\s\-]?[0-9]{4}[\s\-]?[0-9]{4}$/,
|
|
2939
|
+
"Please enter a valid credit card number"
|
|
2940
|
+
),
|
|
2941
|
+
// SSN validation
|
|
2942
|
+
ssn: z4.string().regex(
|
|
2943
|
+
/^\d{3}-\d{2}-\d{4}$/,
|
|
2944
|
+
"Please enter a valid SSN (XXX-XX-XXXX)"
|
|
2945
|
+
),
|
|
2946
|
+
// ZIP code validation
|
|
2947
|
+
zipCode: z4.string().regex(
|
|
2948
|
+
/^\d{5}(-\d{4})?$/,
|
|
2949
|
+
"Please enter a valid ZIP code"
|
|
2950
|
+
),
|
|
2951
|
+
// Date validation (MM/DD/YYYY)
|
|
2952
|
+
date: z4.string().regex(
|
|
2953
|
+
/^(0[1-9]|1[0-2])\/(0[1-9]|[12][0-9]|3[01])\/\d{4}$/,
|
|
2954
|
+
"Please enter a valid date (MM/DD/YYYY)"
|
|
2955
|
+
),
|
|
2956
|
+
// Time validation (HH:MM AM/PM)
|
|
2957
|
+
time: z4.string().regex(
|
|
2958
|
+
/^(0[1-9]|1[0-2]):[0-5][0-9] (AM|PM)$/i,
|
|
2959
|
+
"Please enter a valid time (HH:MM AM/PM)"
|
|
2960
|
+
)
|
|
2961
|
+
};
|
|
2962
|
+
var asyncValidation = {
|
|
2963
|
+
/**
|
|
2964
|
+
* Email availability check
|
|
2965
|
+
*/
|
|
2966
|
+
emailAvailability: async (email) => {
|
|
2967
|
+
return new Promise((resolve) => {
|
|
2968
|
+
setTimeout(() => {
|
|
2969
|
+
const takenEmails = ["test@example.com", "admin@example.com"];
|
|
2970
|
+
resolve(!takenEmails.includes(email));
|
|
2971
|
+
}, 1e3);
|
|
2972
|
+
});
|
|
2973
|
+
},
|
|
2974
|
+
/**
|
|
2975
|
+
* Username availability check
|
|
2976
|
+
*/
|
|
2977
|
+
usernameAvailability: async (username) => {
|
|
2978
|
+
return new Promise((resolve) => {
|
|
2979
|
+
setTimeout(() => {
|
|
2980
|
+
const takenUsernames = ["admin", "test", "user"];
|
|
2981
|
+
resolve(!takenUsernames.includes(username.toLowerCase()));
|
|
2982
|
+
}, 1e3);
|
|
2983
|
+
});
|
|
2984
|
+
}
|
|
2985
|
+
};
|
|
2986
|
+
var errorMessages = {
|
|
2987
|
+
required: (fieldName) => `${fieldName} is required`,
|
|
2988
|
+
minLength: (fieldName, min) => `${fieldName} must be at least ${min} characters`,
|
|
2989
|
+
maxLength: (fieldName, max) => `${fieldName} must be no more than ${max} characters`,
|
|
2990
|
+
min: (fieldName, min) => `${fieldName} must be at least ${min}`,
|
|
2991
|
+
max: (fieldName, max) => `${fieldName} must be no more than ${max}`,
|
|
2992
|
+
pattern: (fieldName) => `${fieldName} format is invalid`,
|
|
2993
|
+
email: () => "Please enter a valid email address",
|
|
2994
|
+
url: () => "Please enter a valid URL",
|
|
2995
|
+
phone: () => "Please enter a valid phone number",
|
|
2996
|
+
date: () => "Please enter a valid date",
|
|
2997
|
+
time: () => "Please enter a valid time"
|
|
2998
|
+
};
|
|
2999
|
+
var serverValidation = {
|
|
3000
|
+
/**
|
|
3001
|
+
* Apply server errors to form
|
|
3002
|
+
*/
|
|
3003
|
+
applyServerErrors: (errors, setError) => {
|
|
3004
|
+
Object.entries(errors).forEach(([field2, messages]) => {
|
|
3005
|
+
setError(field2, {
|
|
3006
|
+
type: "server",
|
|
3007
|
+
message: messages[0]
|
|
3008
|
+
// Use first error message
|
|
3009
|
+
});
|
|
3010
|
+
});
|
|
3011
|
+
},
|
|
3012
|
+
/**
|
|
3013
|
+
* Clear server errors
|
|
3014
|
+
*/
|
|
3015
|
+
clearServerErrors: (fields, clearErrors) => {
|
|
3016
|
+
fields.forEach((field2) => {
|
|
3017
|
+
clearErrors(field2, "server");
|
|
3018
|
+
});
|
|
3019
|
+
}
|
|
3020
|
+
};
|
|
3021
|
+
var validationUtils = {
|
|
3022
|
+
/**
|
|
3023
|
+
* Debounced validation
|
|
3024
|
+
*/
|
|
3025
|
+
debounceValidation: (fn, delay = 300) => {
|
|
3026
|
+
let timeoutId;
|
|
3027
|
+
return (...args) => {
|
|
3028
|
+
clearTimeout(timeoutId);
|
|
3029
|
+
timeoutId = setTimeout(() => fn(...args), delay);
|
|
3030
|
+
};
|
|
3031
|
+
},
|
|
3032
|
+
/**
|
|
3033
|
+
* Validate form data against schema
|
|
3034
|
+
*/
|
|
3035
|
+
validateForm: async (data, schema) => {
|
|
3036
|
+
try {
|
|
3037
|
+
await schema.parseAsync(data);
|
|
3038
|
+
return { success: true, errors: {} };
|
|
3039
|
+
} catch (error) {
|
|
3040
|
+
if (error instanceof z4.ZodError) {
|
|
3041
|
+
const errors = {};
|
|
3042
|
+
error.issues.forEach((err) => {
|
|
3043
|
+
const path = err.path.join(".");
|
|
3044
|
+
errors[path] = err.message;
|
|
3045
|
+
});
|
|
3046
|
+
return { success: false, errors };
|
|
3047
|
+
}
|
|
3048
|
+
throw error;
|
|
3049
|
+
}
|
|
3050
|
+
},
|
|
3051
|
+
/**
|
|
3052
|
+
* Get field error message
|
|
3053
|
+
*/
|
|
3054
|
+
getFieldError: (errors, field2) => {
|
|
3055
|
+
return errors[field2];
|
|
3056
|
+
},
|
|
3057
|
+
/**
|
|
3058
|
+
* Check if field has error
|
|
3059
|
+
*/
|
|
3060
|
+
hasFieldError: (errors, field2) => {
|
|
3061
|
+
return !!errors[field2];
|
|
3062
|
+
}
|
|
3063
|
+
};
|
|
1493
3064
|
export {
|
|
3065
|
+
AdvancedFieldBuilder,
|
|
3066
|
+
BasicFormBuilder,
|
|
1494
3067
|
CheckboxField,
|
|
3068
|
+
CommonFields,
|
|
3069
|
+
ConditionalField,
|
|
1495
3070
|
ConfigurableForm,
|
|
1496
3071
|
DateField,
|
|
3072
|
+
DynamicSectionField,
|
|
3073
|
+
FieldArrayBuilder,
|
|
3074
|
+
FieldArrayField,
|
|
3075
|
+
FieldArrayItemBuilder,
|
|
1497
3076
|
FileField,
|
|
1498
3077
|
FontPickerField,
|
|
1499
3078
|
FormField,
|
|
3079
|
+
FormFieldHelpers,
|
|
1500
3080
|
FormProvider,
|
|
3081
|
+
FormStatus,
|
|
3082
|
+
FormToast,
|
|
1501
3083
|
HeroHookFormProvider,
|
|
1502
3084
|
InputField,
|
|
1503
3085
|
RadioGroupField,
|
|
@@ -1506,13 +3088,20 @@ export {
|
|
|
1506
3088
|
SubmitButton,
|
|
1507
3089
|
SwitchField,
|
|
1508
3090
|
TextareaField,
|
|
3091
|
+
TypeInferredBuilder,
|
|
1509
3092
|
ZodForm,
|
|
1510
3093
|
applyServerErrors,
|
|
3094
|
+
asyncValidation,
|
|
1511
3095
|
commonValidations,
|
|
3096
|
+
createAdvancedBuilder,
|
|
3097
|
+
createBasicFormBuilder,
|
|
1512
3098
|
createConditionalSchema,
|
|
1513
3099
|
createConfirmPasswordSchema,
|
|
1514
3100
|
createDateSchema,
|
|
1515
3101
|
createEmailSchema,
|
|
3102
|
+
createField,
|
|
3103
|
+
createFieldArrayBuilder,
|
|
3104
|
+
createFieldArrayItemBuilder,
|
|
1516
3105
|
createFileSchema,
|
|
1517
3106
|
createFormTestUtils,
|
|
1518
3107
|
createFutureDateSchema,
|
|
@@ -1520,24 +3109,47 @@ export {
|
|
|
1520
3109
|
createMinLengthSchema,
|
|
1521
3110
|
createMockFormData,
|
|
1522
3111
|
createMockFormErrors,
|
|
3112
|
+
createNestedPathBuilder,
|
|
1523
3113
|
createNumberRangeSchema,
|
|
3114
|
+
createOptimizedFieldHandler,
|
|
1524
3115
|
createPasswordSchema,
|
|
1525
3116
|
createPastDateSchema,
|
|
1526
3117
|
createPhoneSchema,
|
|
1527
3118
|
createRequiredCheckboxSchema,
|
|
1528
3119
|
createRequiredSchema,
|
|
3120
|
+
createTypeInferredBuilder,
|
|
1529
3121
|
createUrlSchema,
|
|
1530
3122
|
createZodFormConfig,
|
|
3123
|
+
crossFieldValidation,
|
|
3124
|
+
debounce,
|
|
3125
|
+
deepEqual,
|
|
3126
|
+
defineInferredForm,
|
|
3127
|
+
errorMessages,
|
|
3128
|
+
field,
|
|
1531
3129
|
getFieldError,
|
|
1532
3130
|
getFormErrors,
|
|
1533
3131
|
hasFieldError,
|
|
1534
3132
|
hasFormErrors,
|
|
3133
|
+
serverValidation,
|
|
3134
|
+
shallowEqual,
|
|
1535
3135
|
simulateFieldInput,
|
|
1536
3136
|
simulateFormSubmission,
|
|
1537
|
-
|
|
3137
|
+
throttle,
|
|
3138
|
+
useBatchedFieldUpdates,
|
|
3139
|
+
useDebouncedFieldValidation,
|
|
3140
|
+
useDebouncedValidation,
|
|
3141
|
+
useEnhancedFormState,
|
|
3142
|
+
useFormContext5 as useFormContext,
|
|
1538
3143
|
useFormHelper,
|
|
1539
3144
|
useHeroForm,
|
|
1540
3145
|
useHeroHookFormDefaults,
|
|
3146
|
+
useInferredForm,
|
|
3147
|
+
useMemoizedCallback,
|
|
3148
|
+
useMemoizedFieldProps,
|
|
3149
|
+
usePerformanceMonitor,
|
|
3150
|
+
useTypeInferredForm,
|
|
1541
3151
|
useZodForm,
|
|
3152
|
+
validationPatterns,
|
|
3153
|
+
validationUtils,
|
|
1542
3154
|
waitForFormState
|
|
1543
3155
|
};
|