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