@wix/headless-forms 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +46 -0
  2. package/cjs/dist/react/Form.d.ts +802 -0
  3. package/cjs/dist/react/Form.js +776 -0
  4. package/cjs/dist/react/Phone.d.ts +47 -0
  5. package/cjs/dist/react/Phone.js +56 -0
  6. package/cjs/dist/react/constants/calling-country-codes.d.ts +242 -0
  7. package/cjs/dist/react/constants/calling-country-codes.js +242 -0
  8. package/cjs/dist/react/context/FieldContext.d.ts +12 -0
  9. package/cjs/dist/react/context/FieldContext.js +16 -0
  10. package/cjs/dist/react/context/FieldLayoutContext.d.ts +12 -0
  11. package/cjs/dist/react/context/FieldLayoutContext.js +21 -0
  12. package/cjs/dist/react/core/Form.d.ts +342 -0
  13. package/cjs/dist/react/core/Form.js +278 -0
  14. package/cjs/dist/react/index.d.ts +3 -0
  15. package/cjs/dist/react/index.js +42 -0
  16. package/cjs/dist/react/types.d.ts +3 -0
  17. package/cjs/dist/react/types.js +2 -0
  18. package/cjs/dist/react/utils.d.ts +13 -0
  19. package/cjs/dist/react/utils.js +20 -0
  20. package/cjs/dist/services/form-service.d.ts +114 -0
  21. package/cjs/dist/services/form-service.js +152 -0
  22. package/cjs/dist/services/index.d.ts +1 -0
  23. package/cjs/dist/services/index.js +17 -0
  24. package/cjs/package.json +3 -0
  25. package/dist/react/Form.d.ts +802 -0
  26. package/dist/react/Form.js +740 -0
  27. package/dist/react/Phone.d.ts +47 -0
  28. package/dist/react/Phone.js +50 -0
  29. package/dist/react/constants/calling-country-codes.d.ts +242 -0
  30. package/dist/react/constants/calling-country-codes.js +241 -0
  31. package/dist/react/context/FieldContext.d.ts +12 -0
  32. package/dist/react/context/FieldContext.js +9 -0
  33. package/dist/react/context/FieldLayoutContext.d.ts +12 -0
  34. package/dist/react/context/FieldLayoutContext.js +13 -0
  35. package/dist/react/core/Form.d.ts +342 -0
  36. package/dist/react/core/Form.js +269 -0
  37. package/dist/react/index.d.ts +3 -0
  38. package/dist/react/index.js +3 -0
  39. package/dist/react/types.d.ts +3 -0
  40. package/dist/react/types.js +1 -0
  41. package/dist/react/utils.d.ts +13 -0
  42. package/dist/react/utils.js +17 -0
  43. package/dist/services/form-service.d.ts +114 -0
  44. package/dist/services/form-service.js +148 -0
  45. package/dist/services/index.d.ts +1 -0
  46. package/dist/services/index.js +1 -0
  47. package/package.json +62 -0
  48. package/react/package.json +4 -0
  49. package/services/package.json +4 -0
@@ -0,0 +1,776 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.Field = exports.FieldError = exports.FieldInput = exports.FieldInputWrapper = exports.FieldLabel = exports.FieldLabelRequired = exports.Fields = exports.Submitted = exports.Error = exports.LoadingError = exports.Loading = exports.Root = void 0;
37
+ const jsx_runtime_1 = require("react/jsx-runtime");
38
+ const react_1 = __importStar(require("react"));
39
+ const react_2 = require("@wix/headless-utils/react");
40
+ const form_public_1 = require("@wix/form-public");
41
+ const Form_js_1 = require("./core/Form.js");
42
+ const FieldContext_js_1 = require("./context/FieldContext.js");
43
+ const FieldLayoutContext_js_1 = require("./context/FieldLayoutContext.js");
44
+ var TestIds;
45
+ (function (TestIds) {
46
+ TestIds["formRoot"] = "form-root";
47
+ TestIds["form"] = "form";
48
+ TestIds["formLoading"] = "form-loading";
49
+ TestIds["formLoadingError"] = "form-loading-error";
50
+ TestIds["formError"] = "form-error";
51
+ TestIds["formSubmitted"] = "form-submitted";
52
+ TestIds["fieldRoot"] = "field-root";
53
+ TestIds["fieldLabel"] = "field-label";
54
+ TestIds["fieldInputWrapper"] = "field-input-wrapper";
55
+ TestIds["fieldInput"] = "field-input";
56
+ TestIds["fieldError"] = "field-error";
57
+ })(TestIds || (TestIds = {}));
58
+ /**
59
+ * Root component that provides all necessary service contexts for a complete form experience.
60
+ * This component sets up the Form service and provides context to child components.
61
+ * Must be used as the top-level component for all form functionality.
62
+ *
63
+ * @component
64
+ * @param {RootProps} props - The component props
65
+ * @param {React.ReactNode} props.children - Child components that will have access to form context
66
+ * @param {FormServiceConfig} props.formServiceConfig - Form service configuration object
67
+ * @param {boolean} [props.asChild] - Whether to render as a child component
68
+ * @param {string} [props.className] - CSS classes to apply to the root element
69
+ * @example
70
+ * ```tsx
71
+ * import { Form } from '@wix/headless-forms/react';
72
+ * import { loadFormServiceConfig } from '@wix/headless-forms/services';
73
+ *
74
+ * const FIELD_MAP = {
75
+ * TEXT_INPUT: TextInput,
76
+ * TEXT_AREA: TextArea,
77
+ * CHECKBOX: Checkbox,
78
+ * // ... other field components
79
+ * };
80
+ *
81
+ * // Pattern 1: Pre-loaded form data (SSR/SSG)
82
+ * function FormPage({ formServiceConfig }) {
83
+ * return (
84
+ * <Form.Root formServiceConfig={formServiceConfig}>
85
+ * <Form.Loading className="flex justify-center p-4" />
86
+ * <Form.LoadingError className="text-destructive px-4 py-3 rounded mb-4" />
87
+ * <Form.Fields fieldMap={FIELD_MAP} />
88
+ * <Form.Error className="text-destructive p-4 rounded-lg mb-4" />
89
+ * <Form.Submitted className="text-green-500 p-4 rounded-lg mb-4" />
90
+ * </Form.Root>
91
+ * );
92
+ * }
93
+ *
94
+ * // Pattern 2: Lazy loading with formId (Client-side)
95
+ * function DynamicFormPage({ formId }) {
96
+ * return (
97
+ * <Form.Root formServiceConfig={{ formId }}>
98
+ * <Form.Loading className="flex justify-center p-4" />
99
+ * <Form.LoadingError className="text-destructive px-4 py-3 rounded mb-4" />
100
+ * <Form.Fields fieldMap={FIELD_MAP} />
101
+ * <Form.Error className="text-destructive p-4 rounded-lg mb-4" />
102
+ * <Form.Submitted className="text-green-500 p-4 rounded-lg mb-4" />
103
+ * </Form.Root>
104
+ * );
105
+ * }
106
+ * ```
107
+ */
108
+ exports.Root = react_1.default.forwardRef((props, ref) => {
109
+ const { children, formServiceConfig, asChild, ...otherProps } = props;
110
+ return ((0, jsx_runtime_1.jsx)(Form_js_1.Root, { formServiceConfig: formServiceConfig, children: (0, jsx_runtime_1.jsx)(RootContent, { asChild: asChild, ref: ref, ...otherProps, children: children }) }));
111
+ });
112
+ /**
113
+ * Internal component to handle the Root content with service access.
114
+ * This component wraps the children with the necessary div container and applies styling.
115
+ *
116
+ * @internal
117
+ * @param {RootContentProps} props - Component props
118
+ * @param {React.ReactNode} props.children - Child components to render
119
+ * @param {string} [props.className] - CSS classes to apply to the container
120
+ * @param {boolean} [props.asChild] - Whether to render as a child component
121
+ * @returns {JSX.Element} The wrapped content
122
+ */
123
+ const RootContent = react_1.default.forwardRef((props, ref) => {
124
+ const { asChild, children, className, ...otherProps } = props;
125
+ return ((0, jsx_runtime_1.jsx)(react_2.AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.formRoot, customElement: children, customElementProps: {}, ...otherProps, children: (0, jsx_runtime_1.jsx)("div", { children: children }) }));
126
+ });
127
+ /**
128
+ * Component that renders content during loading state.
129
+ * Only displays its children when the form is currently loading.
130
+ *
131
+ * @component
132
+ * @param {LoadingProps} props - The component props
133
+ * @param {boolean} [props.asChild] - Whether to render as a child component
134
+ * @param {React.ReactNode} [props.children] - Content to display during loading state
135
+ * @param {string} [props.className] - CSS classes to apply to the default element
136
+ * @example
137
+ * ```tsx
138
+ * import { Form } from '@wix/headless-forms/react';
139
+ *
140
+ * // Default usage with className
141
+ * function FormLoading() {
142
+ * return (
143
+ * <Form.Loading className="flex justify-center p-4" />
144
+ * );
145
+ * }
146
+ *
147
+ * // Custom content
148
+ * function CustomFormLoading() {
149
+ * return (
150
+ * <Form.Loading>
151
+ * <div className="flex justify-center items-center p-4">
152
+ * <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
153
+ * <span className="ml-2 text-foreground font-paragraph">Loading form...</span>
154
+ * </div>
155
+ * </Form.Loading>
156
+ * );
157
+ * }
158
+ *
159
+ * // With asChild for custom components
160
+ * function CustomFormLoadingAsChild() {
161
+ * return (
162
+ * <Form.Loading asChild>
163
+ * <div className="custom-loading-container">
164
+ * <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
165
+ * <span className="ml-2 text-foreground font-paragraph">Loading form...</span>
166
+ * </div>
167
+ * </Form.Loading>
168
+ * );
169
+ * }
170
+ * ```
171
+ */
172
+ exports.Loading = react_1.default.forwardRef((props, ref) => {
173
+ const { asChild, children, className, ...otherProps } = props;
174
+ return ((0, jsx_runtime_1.jsx)(Form_js_1.Loading, { children: ({ isLoading }) => {
175
+ if (!isLoading)
176
+ return null;
177
+ return ((0, jsx_runtime_1.jsx)(react_2.AsChildSlot, { "data-testid": TestIds.formLoading, ref: ref, asChild: asChild, className: className, customElement: children, content: "Loading form...", ...otherProps, children: (0, jsx_runtime_1.jsx)("div", { children: "Loading form..." }) }));
178
+ } }));
179
+ });
180
+ /**
181
+ * Component that renders content when there's an error loading the form.
182
+ * Only displays its children when an error has occurred.
183
+ *
184
+ * @component
185
+ * @param {LoadingErrorProps} props - The component props
186
+ * @param {boolean} [props.asChild] - Whether to render as a child component
187
+ * @param {React.ReactNode} [props.children] - Content to display during error state
188
+ * @param {string} [props.className] - CSS classes to apply to the default element
189
+ * @example
190
+ * ```tsx
191
+ * import { Form } from '@wix/headless-forms/react';
192
+ *
193
+ * // Default usage with className
194
+ * function FormLoadingError() {
195
+ * return (
196
+ * <Form.LoadingError className="text-destructive px-4 py-3 rounded mb-4" />
197
+ * );
198
+ * }
199
+ *
200
+ * // Custom content
201
+ * function CustomLoadingError() {
202
+ * return (
203
+ * <Form.LoadingError>
204
+ * <div className="bg-destructive/10 border border-destructive text-destructive px-4 py-3 rounded mb-4">
205
+ * <h3 className="font-heading text-lg">Error loading form</h3>
206
+ * <p className="font-paragraph">Something went wrong. Please try again.</p>
207
+ * </div>
208
+ * </Form.LoadingError>
209
+ * );
210
+ * }
211
+ *
212
+ * // With asChild for custom components
213
+ * function CustomLoadingErrorAsChild() {
214
+ * return (
215
+ * <Form.LoadingError asChild>
216
+ * {React.forwardRef<HTMLDivElement, { error: string | null; hasError: boolean }>(
217
+ * ({ error }, ref) => (
218
+ * <div ref={ref} className="custom-error-container">
219
+ * <h3 className="font-heading">Error Loading Form</h3>
220
+ * <p className="font-paragraph">{error}</p>
221
+ * </div>
222
+ * )
223
+ * )}
224
+ * </Form.LoadingError>
225
+ * );
226
+ * }
227
+ * ```
228
+ */
229
+ exports.LoadingError = react_1.default.forwardRef((props, ref) => {
230
+ const { asChild, children, className, ...otherProps } = props;
231
+ return ((0, jsx_runtime_1.jsx)(Form_js_1.LoadingError, { children: ({ error, hasError }) => {
232
+ if (!hasError)
233
+ return null;
234
+ const errorData = { error, hasError };
235
+ return ((0, jsx_runtime_1.jsx)(react_2.AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.formLoadingError, customElement: children, customElementProps: errorData, content: error, ...otherProps, children: (0, jsx_runtime_1.jsx)("div", { children: error }) }));
236
+ } }));
237
+ });
238
+ /**
239
+ * Component that renders content when there's an error during form submission.
240
+ * Only displays its children when a submission error has occurred.
241
+ *
242
+ * @component
243
+ * @param {ErrorProps} props - The component props
244
+ * @param {boolean} [props.asChild] - Whether to render as a child component
245
+ * @param {React.ReactNode} [props.children] - Content to display during submit error state
246
+ * @param {string} [props.className] - CSS classes to apply to the default element
247
+ * @example
248
+ * ```tsx
249
+ * import { Form } from '@wix/headless-forms/react';
250
+ *
251
+ * // Default usage with className
252
+ * function FormError() {
253
+ * return <Form.Error className="text-destructive p-4 rounded-lg mb-4" />;
254
+ * }
255
+ *
256
+ * // Custom content
257
+ * function CustomFormError() {
258
+ * return (
259
+ * <Form.Error>
260
+ * <div className="bg-destructive/10 border border-destructive text-destructive p-4 rounded-lg mb-4">
261
+ * <h3 className="font-heading text-lg">Submission Failed</h3>
262
+ * <p className="font-paragraph">Please check your input and try again.</p>
263
+ * </div>
264
+ * </Form.Error>
265
+ * );
266
+ * }
267
+ *
268
+ * // With asChild for custom components
269
+ * function CustomFormErrorAsChild() {
270
+ * return (
271
+ * <Form.Error asChild>
272
+ * {React.forwardRef<HTMLDivElement, { error: string | null; hasError: boolean }>(
273
+ * ({ error }, ref) => (
274
+ * <div ref={ref} className="custom-error-container">
275
+ * <h3 className="font-heading">Submission Failed</h3>
276
+ * <p className="font-paragraph">{error}</p>
277
+ * </div>
278
+ * )
279
+ * )}
280
+ * </Form.Error>
281
+ * );
282
+ * }
283
+ * ```
284
+ */
285
+ exports.Error = react_1.default.forwardRef((props, ref) => {
286
+ const { asChild, children, className, ...otherProps } = props;
287
+ return ((0, jsx_runtime_1.jsx)(Form_js_1.Error, { children: ({ error, hasError }) => {
288
+ if (!hasError)
289
+ return null;
290
+ const errorData = { error, hasError };
291
+ return ((0, jsx_runtime_1.jsx)(react_2.AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.formError, customElement: children, customElementProps: errorData, content: error, ...otherProps, children: (0, jsx_runtime_1.jsx)("div", { className: "text-destructive text-sm sm:text-base", children: error }) }));
292
+ } }));
293
+ });
294
+ /**
295
+ * Component that renders content after successful form submission.
296
+ * Only displays its children when the form has been successfully submitted.
297
+ *
298
+ * @component
299
+ * @param {SubmittedProps} props - The component props
300
+ * @param {boolean} [props.asChild] - Whether to render as a child component
301
+ * @param {React.ReactNode} [props.children] - Content to display after successful submission
302
+ * @param {string} [props.className] - CSS classes to apply to the default element
303
+ * @example
304
+ * ```tsx
305
+ * import { Form } from '@wix/headless-forms/react';
306
+ *
307
+ * // Default usage with className
308
+ * function FormSubmitted() {
309
+ * return <Form.Submitted className="text-green-500 p-4 rounded-lg mb-4" />;
310
+ * }
311
+ *
312
+ * // Custom content
313
+ * function CustomFormSubmitted() {
314
+ * return (
315
+ * <Form.Submitted>
316
+ * <div className="bg-green-50 border border-green-200 text-green-800 p-6 rounded-lg mb-4">
317
+ * <h2 className="font-heading text-xl mb-2">Thank You!</h2>
318
+ * <p className="font-paragraph">Your form has been submitted successfully.</p>
319
+ * </div>
320
+ * </Form.Submitted>
321
+ * );
322
+ * }
323
+ *
324
+ * // With asChild for custom components
325
+ * function CustomFormSubmittedAsChild() {
326
+ * return (
327
+ * <Form.Submitted asChild>
328
+ * {React.forwardRef<HTMLDivElement, { isSubmitted: boolean; message: string }>(
329
+ * ({ message }, ref) => (
330
+ * <div ref={ref} className="custom-success-container">
331
+ * <h2 className="font-heading">Thank You!</h2>
332
+ * <p className="font-paragraph">{message}</p>
333
+ * </div>
334
+ * )
335
+ * )}
336
+ * </Form.Submitted>
337
+ * );
338
+ * }
339
+ * ```
340
+ */
341
+ exports.Submitted = react_1.default.forwardRef((props, ref) => {
342
+ const { asChild, children, className, ...otherProps } = props;
343
+ return ((0, jsx_runtime_1.jsx)(Form_js_1.Submitted, { children: ({ isSubmitted, message }) => {
344
+ if (!isSubmitted)
345
+ return null;
346
+ const submittedData = { isSubmitted, message };
347
+ return ((0, jsx_runtime_1.jsx)(react_2.AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.formSubmitted, customElement: children, customElementProps: submittedData, content: message, ...otherProps, children: (0, jsx_runtime_1.jsx)("div", { className: "text-green-500 text-sm sm:text-base", children: message }) }));
348
+ } }));
349
+ });
350
+ /**
351
+ * Fields component for rendering a form with custom field renderers.
352
+ * This component handles the rendering of form fields based on the provided fieldMap.
353
+ * Must be used within Form.Root to access form context.
354
+ *
355
+ * @component
356
+ * @param {FieldsProps} props - Component props
357
+ * @param {FieldMap} props.fieldMap - A mapping of field types to their corresponding React components
358
+ * @param {string} props.rowGapClassname - CSS class name for gap between rows
359
+ * @param {string} props.columnGapClassname - CSS class name for gap between columns
360
+ * @example
361
+ * ```tsx
362
+ * import { Form } from '@wix/headless-forms/react';
363
+ * import { TextInput, TextArea, Checkbox } from './field-components';
364
+ *
365
+ * const FIELD_MAP = {
366
+ * TEXT_INPUT: TextInput,
367
+ * TEXT_AREA: TextArea,
368
+ * CHECKBOX: Checkbox,
369
+ * NUMBER_INPUT: NumberInput,
370
+ * // ... remaining field components
371
+ * };
372
+ *
373
+ * function ContactForm({ formServiceConfig }) {
374
+ * return (
375
+ * <Form.Root formServiceConfig={formServiceConfig}>
376
+ * <Form.Loading className="flex justify-center p-4" />
377
+ * <Form.LoadingError className="text-destructive px-4 py-3 rounded mb-4" />
378
+ * <Form.Fields
379
+ * fieldMap={FIELD_MAP}
380
+ * rowGapClassname="gap-y-4"
381
+ * columnGapClassname="gap-x-2"
382
+ * />
383
+ * </Form.Root>
384
+ * );
385
+ * }
386
+ * ```
387
+ */
388
+ /**
389
+ * Fields component for rendering a form with custom field renderers.
390
+ * It maps each field type from the form configuration to its corresponding React component
391
+ * and renders them in the order and layout defined by the form structure.
392
+ *
393
+ * The component automatically handles:
394
+ * - Field validation and error display
395
+ * - Form state management
396
+ * - Field value updates
397
+ * - Grid layout with configurable row and column gaps
398
+ *
399
+ * Must be used within Form.Root to access form context.
400
+ *
401
+ * @component
402
+ * @param {FieldsProps} props - The component props
403
+ * @param {FieldMap} props.fieldMap - A mapping of field types to their corresponding React components. Each key represents a field type (e.g., 'TEXT_INPUT', 'CHECKBOX') and the value is the React component that should render that field type.
404
+ * @param {string} props.rowGapClassname - CSS class name for gap between form rows
405
+ * @param {string} props.columnGapClassname - CSS class name for gap between form columns
406
+ *
407
+ * @example
408
+ * ```tsx
409
+ * import { Form } from '@wix/headless-forms/react';
410
+ * import { loadFormServiceConfig } from '@wix/headless-forms/services';
411
+ * import {
412
+ * TextInput,
413
+ * TextArea,
414
+ * PhoneInput,
415
+ * MultilineAddress,
416
+ * DateInput,
417
+ * DatePicker,
418
+ * DateTimeInput,
419
+ * FileUpload,
420
+ * NumberInput,
421
+ * Checkbox,
422
+ * Signature,
423
+ * RatingInput,
424
+ * RadioGroup,
425
+ * CheckboxGroup,
426
+ * Dropdown,
427
+ * Tags,
428
+ * TimeInput,
429
+ * RichText,
430
+ * SubmitButton,
431
+ * ProductList,
432
+ * FixedPayment,
433
+ * PaymentInput,
434
+ * Donation,
435
+ * Appointment,
436
+ * ImageChoice
437
+ * } from './components';
438
+ *
439
+ * // Define your field mapping - this tells the Fields component which React component to use for each field type
440
+ * const FIELD_MAP = {
441
+ * TEXT_INPUT: TextInput,
442
+ * TEXT_AREA: TextArea,
443
+ * PHONE_INPUT: PhoneInput,
444
+ * MULTILINE_ADDRESS: MultilineAddress,
445
+ * DATE_INPUT: DateInput,
446
+ * DATE_PICKER: DatePicker,
447
+ * DATE_TIME_INPUT: DateTimeInput,
448
+ * FILE_UPLOAD: FileUpload,
449
+ * NUMBER_INPUT: NumberInput,
450
+ * CHECKBOX: Checkbox,
451
+ * SIGNATURE: Signature,
452
+ * RATING_INPUT: RatingInput,
453
+ * RADIO_GROUP: RadioGroup,
454
+ * CHECKBOX_GROUP: CheckboxGroup,
455
+ * DROPDOWN: Dropdown,
456
+ * TAGS: Tags,
457
+ * TIME_INPUT: TimeInput,
458
+ * TEXT: RichText,
459
+ * SUBMIT_BUTTON: SubmitButton,
460
+ * PRODUCT_LIST: ProductList,
461
+ * FIXED_PAYMENT: FixedPayment,
462
+ * PAYMENT_INPUT: PaymentInput,
463
+ * DONATION: Donation,
464
+ * APPOINTMENT: Appointment,
465
+ * IMAGE_CHOICE: ImageChoice,
466
+ * };
467
+ *
468
+ * function ContactForm({ formServiceConfig }) {
469
+ * return (
470
+ * <Form.Root formServiceConfig={formServiceConfig}>
471
+ * <Form.Loading className="flex justify-center p-4" />
472
+ * <Form.LoadingError className="text-destructive px-4 py-3 rounded mb-4" />
473
+ * <Form.Fields
474
+ * fieldMap={FIELD_MAP}
475
+ * rowGapClassname="gap-y-4"
476
+ * columnGapClassname="gap-x-2"
477
+ * />
478
+ * <Form.Error className="text-destructive p-4 rounded-lg mb-4" />
479
+ * <Form.Submitted className="text-green-500 p-4 rounded-lg mb-4" />
480
+ * </Form.Root>
481
+ * );
482
+ * }
483
+ * ```
484
+ *
485
+ * @example
486
+ * ```tsx
487
+ * // Creating custom field components - ALL field components MUST use Form.Field
488
+ * // This example shows the REQUIRED structure for a TEXT_INPUT component
489
+ * import { Form, type TextInputProps } from '@wix/headless-forms/react';
490
+ *
491
+ * const TextInput = (props: TextInputProps) => {
492
+ * const { id, value, onChange, label, error, required, ...inputProps } = props;
493
+ *
494
+ * // Form.Field provides automatic grid layout positioning
495
+ * return (
496
+ * <Form.Field id={id}>
497
+ * <Form.Field.Label>
498
+ * <label className="text-foreground font-paragraph">
499
+ * {label}
500
+ * {required && <span className="text-destructive ml-1">*</span>}
501
+ * </label>
502
+ * </Form.Field.Label>
503
+ * <Form.Field.Input
504
+ * description={error && <span className="text-destructive text-sm">{error}</span>}
505
+ * >
506
+ * <input
507
+ * type="text"
508
+ * value={value || ''}
509
+ * onChange={(e) => onChange(e.target.value)}
510
+ * className="bg-background border-foreground text-foreground"
511
+ * aria-invalid={!!error}
512
+ * {...inputProps}
513
+ * />
514
+ * </Form.Field.Input>
515
+ * </Form.Field>
516
+ * );
517
+ * };
518
+ *
519
+ * const FIELD_MAP = {
520
+ * TEXT_INPUT: TextInput,
521
+ * // ... all other field components must also use Form.Field
522
+ * };
523
+ * ```
524
+ */
525
+ exports.Fields = react_1.default.forwardRef((props, ref) => {
526
+ const [formValues, setFormValues] = (0, react_1.useState)({});
527
+ const [formErrors, setFormErrors] = (0, react_1.useState)([]);
528
+ const handleFormChange = (0, react_1.useCallback)((values) => {
529
+ setFormValues(values);
530
+ }, []);
531
+ const handleFormValidate = (0, react_1.useCallback)((errors) => {
532
+ setFormErrors(errors);
533
+ }, []);
534
+ return ((0, jsx_runtime_1.jsx)(Form_js_1.Fields, { children: ({ form, submitForm }) => {
535
+ if (!form)
536
+ return null;
537
+ return ((0, jsx_runtime_1.jsx)("div", { ref: ref, children: (0, jsx_runtime_1.jsx)(form_public_1.FormProvider, { currency: 'USD', locale: 'en', children: (0, jsx_runtime_1.jsx)(FieldsWithForm, { form: form, values: formValues, onChange: handleFormChange, errors: formErrors, onValidate: handleFormValidate, fields: props.fieldMap, submitForm: () => submitForm(formValues), rowGapClassname: props.rowGapClassname, columnGapClassname: props.columnGapClassname }) }) }));
538
+ } }));
539
+ });
540
+ const FieldsWithForm = ({ form, submitForm, values, onChange, errors, onValidate, fields: fieldMap, rowGapClassname, columnGapClassname, }) => {
541
+ const formData = (0, form_public_1.useForm)({
542
+ form,
543
+ values,
544
+ errors,
545
+ onChange,
546
+ onValidate,
547
+ submitForm,
548
+ fieldMap,
549
+ });
550
+ if (!formData)
551
+ return null;
552
+ const { columnCount, fieldElements, fieldsLayout } = formData;
553
+ return (
554
+ // TODO: use readOnly, isDisabled
555
+ // TODO: step title a11y support
556
+ // TODO: mobile support?
557
+ (0, jsx_runtime_1.jsx)(FieldLayoutContext_js_1.FieldLayoutProvider, { value: fieldsLayout, children: (0, jsx_runtime_1.jsx)("form", { onSubmit: (e) => e.preventDefault(), children: (0, jsx_runtime_1.jsx)("fieldset", { style: { display: 'flex', flexDirection: 'column' }, className: rowGapClassname, children: fieldElements.map((rowElements, index) => {
558
+ return ((0, jsx_runtime_1.jsx)("div", { style: {
559
+ display: 'grid',
560
+ width: '100%',
561
+ gridTemplateColumns: `repeat(${columnCount}, 1fr)`,
562
+ gridAutoRows: 'minmax(min-content, max-content)',
563
+ }, className: columnGapClassname, children: rowElements }, index));
564
+ }) }) }) }));
565
+ };
566
+ /**
567
+ * Container component for a form field with grid layout support.
568
+ * Provides context to Field.Label, Field.InputWrapper, Field.Input, and Field.Error child components.
569
+ * Based on the default-field-layout functionality.
570
+ *
571
+ * @component
572
+ * @example
573
+ * ```tsx
574
+ * import { Form } from '@wix/headless-forms/react';
575
+ *
576
+ * function FormFields() {
577
+ * return (
578
+ * <Form.Field id="username">
579
+ * <Form.Field.Label>
580
+ * <label className="text-foreground font-paragraph">Username</label>
581
+ * </Form.Field.Label>
582
+ * <Form.Field.InputWrapper>
583
+ * <Form.Field.Input description={<span className="text-secondary-foreground">Required</span>}>
584
+ * <input className="bg-background border-foreground text-foreground" />
585
+ * </Form.Field.Input>
586
+ * <Form.Field.Error>
587
+ * <span className="text-destructive text-sm font-paragraph">Username is required</span>
588
+ * </Form.Field.Error>
589
+ * </Form.Field.InputWrapper>
590
+ * </Form.Field>
591
+ * );
592
+ * }
593
+ * ```
594
+ */
595
+ const FieldRoot = react_1.default.forwardRef((props, ref) => {
596
+ const { id, children, asChild, className, ...otherProps } = props;
597
+ const layout = (0, FieldLayoutContext_js_1.useFieldLayout)(id);
598
+ if (!layout) {
599
+ return null;
600
+ }
601
+ return ((0, jsx_runtime_1.jsx)(Form_js_1.Field, { id: id, layout: layout, children: (fieldData) => {
602
+ const contextValue = {
603
+ id,
604
+ layout: fieldData.layout,
605
+ gridStyles: fieldData.gridStyles,
606
+ };
607
+ return ((0, jsx_runtime_1.jsx)(FieldContext_js_1.FieldContext.Provider, { value: contextValue, children: (0, jsx_runtime_1.jsx)(react_2.AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.fieldRoot, customElement: children, customElementProps: {}, ...otherProps, children: children }) }));
608
+ } }));
609
+ });
610
+ FieldRoot.displayName = 'Form.Field';
611
+ /**
612
+ * Label component for a form field with automatic grid positioning.
613
+ * Must be used within a Form.Field component.
614
+ * Renders in the label row of the field's grid layout.
615
+ *
616
+ * @component
617
+ * @example
618
+ * ```tsx
619
+ * import { Form } from '@wix/headless-forms/react';
620
+ *
621
+ * <Form.Field id="email">
622
+ * <Form.Field.Label>
623
+ * <label className="text-foreground font-paragraph">
624
+ * Email Address
625
+ * <Form.Field.Label.Required required={true} />
626
+ * </label>
627
+ * </Form.Field.Label>
628
+ * <Form.Field.InputWrapper>
629
+ * <Form.Field.Input>
630
+ * <input type="email" className="bg-background border-foreground text-foreground" />
631
+ * </Form.Field.Input>
632
+ * </Form.Field.InputWrapper>
633
+ * </Form.Field>
634
+ * ```
635
+ */
636
+ const FieldLabelRoot = react_1.default.forwardRef((props, ref) => {
637
+ const { children, asChild, className, ...otherProps } = props;
638
+ const { gridStyles } = (0, FieldContext_js_1.useFieldContext)();
639
+ return ((0, jsx_runtime_1.jsx)(react_2.AsChildSlot, { ref: ref, asChild: asChild, className: className, style: gridStyles.label, "data-testid": TestIds.fieldLabel, customElement: children, customElementProps: {}, ...otherProps, children: (0, jsx_runtime_1.jsx)("div", { children: children }) }));
640
+ });
641
+ FieldLabelRoot.displayName = 'Form.Field.Label';
642
+ /**
643
+ * Required indicator component for form field labels.
644
+ * Must be used within a Form.Field.Label component.
645
+ *
646
+ * @component
647
+ * @example
648
+ * ```tsx
649
+ * import { Form } from '@wix/headless-forms/react';
650
+ *
651
+ * // Basic usage with required prop
652
+ * <Form.Field.Label>
653
+ * <label className="text-foreground font-paragraph">
654
+ * Email Address
655
+ * <Form.Field.Label.Required />
656
+ * </label>
657
+ * </Form.Field.Label>
658
+ *
659
+ * // Custom styling
660
+ * <Form.Field.Label>
661
+ * <label className="text-foreground font-paragraph">
662
+ * Username
663
+ * <Form.Field.Label.Required required={true} className="text-destructive ml-2" />
664
+ * </label>
665
+ * </Form.Field.Label>
666
+ */
667
+ exports.FieldLabelRequired = react_1.default.forwardRef((props, ref) => {
668
+ const { required = false, children, asChild, className, ...otherProps } = props;
669
+ const requiredIndicator = 'asterisk';
670
+ // @ts-expect-error
671
+ if (!required || requiredIndicator === 'none')
672
+ return null;
673
+ return ((0, jsx_runtime_1.jsx)(react_2.AsChildSlot, { ref: ref, asChild: asChild, className: className, customElement: children, ...otherProps, children: (0, jsx_runtime_1.jsx)("span", { children: requiredIndicator === 'asterisk'
674
+ ? '*'
675
+ : requiredIndicator === 'text'
676
+ ? '(Required)'
677
+ : null }) }));
678
+ });
679
+ exports.FieldLabelRequired.displayName = 'Form.Field.Label.Required';
680
+ exports.FieldLabel = FieldLabelRoot;
681
+ exports.FieldLabel.Required = exports.FieldLabelRequired;
682
+ /**
683
+ * InputWrapper component that wraps input and error elements with grid positioning.
684
+ * Must be used within a Form.Field component.
685
+ * This wrapper applies the grid positioning styles to contain both the input and error.
686
+ *
687
+ * @component
688
+ * @example
689
+ * ```tsx
690
+ * import { Form } from '@wix/headless-forms/react';
691
+ *
692
+ * <Form.Field id="email">
693
+ * <Form.Field.Label>
694
+ * <label className="text-foreground font-paragraph">Email Address</label>
695
+ * </Form.Field.Label>
696
+ * <Form.Field.InputWrapper>
697
+ * <Form.Field.Input>
698
+ * <input type="email" className="bg-background border-foreground text-foreground" />
699
+ * </Form.Field.Input>
700
+ * <Form.Field.Error>
701
+ * <span className="text-destructive text-sm font-paragraph">Please enter a valid email</span>
702
+ * </Form.Field.Error>
703
+ * </Form.Field.InputWrapper>
704
+ * </Form.Field>
705
+ * ```
706
+ */
707
+ exports.FieldInputWrapper = react_1.default.forwardRef((props, ref) => {
708
+ const { children, asChild, className, ...otherProps } = props;
709
+ const { gridStyles } = (0, FieldContext_js_1.useFieldContext)();
710
+ return ((0, jsx_runtime_1.jsx)(react_2.AsChildSlot, { ref: ref, asChild: asChild, className: className, style: gridStyles.input, "data-testid": TestIds.fieldInputWrapper, customElement: children, customElementProps: {}, ...otherProps, children: (0, jsx_runtime_1.jsx)("div", { children: children }) }));
711
+ });
712
+ exports.FieldInputWrapper.displayName = 'Form.Field.InputWrapper';
713
+ /**
714
+ * Input component for a form field.
715
+ * Must be used within a Form.Field.InputWrapper component.
716
+ * Renders the actual input element without grid positioning.
717
+ *
718
+ * @component
719
+ * @example
720
+ * ```tsx
721
+ * import { Form } from '@wix/headless-forms/react';
722
+ *
723
+ * <Form.Field id="password">
724
+ * <Form.Field.Label>
725
+ * <label className="text-foreground font-paragraph">Password</label>
726
+ * </Form.Field.Label>
727
+ * <Form.Field.InputWrapper>
728
+ * <Form.Field.Input description={<span className="text-secondary-foreground">Min 8 characters</span>}>
729
+ * <input type="password" className="bg-background border-foreground text-foreground" />
730
+ * </Form.Field.Input>
731
+ * </Form.Field.InputWrapper>
732
+ * </Form.Field>
733
+ * ```
734
+ */
735
+ exports.FieldInput = react_1.default.forwardRef((props, ref) => {
736
+ const { children, description, asChild, className, ...otherProps } = props;
737
+ return ((0, jsx_runtime_1.jsx)(react_2.AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.fieldInput, customElement: children, customElementProps: {}, ...otherProps, children: (0, jsx_runtime_1.jsx)("div", { children: children }) }));
738
+ });
739
+ exports.FieldInput.displayName = 'Form.Field.Input';
740
+ /**
741
+ * Error component for displaying field-level validation errors.
742
+ * Must be used within a Form.Field.InputWrapper component.
743
+ * Only renders when there is an error for the current field.
744
+ *
745
+ * @component
746
+ * @example
747
+ * ```tsx
748
+ * import { Form } from '@wix/headless-forms/react';
749
+ *
750
+ * <Form.Field id="email">
751
+ * <Form.Field.Label>
752
+ * <label className="text-foreground font-paragraph">Email Address</label>
753
+ * </Form.Field.Label>
754
+ * <Form.Field.InputWrapper>
755
+ * <Form.Field.Input>
756
+ * <input type="email" className="bg-background border-foreground text-foreground" />
757
+ * </Form.Field.Input>
758
+ * <Form.Field.Error path="email">
759
+ * <span className="text-destructive text-sm font-paragraph">Please enter a valid email address</span>
760
+ * </Form.Field.Error>
761
+ * </Form.Field.InputWrapper>
762
+ * </Form.Field>
763
+ * ```
764
+ */
765
+ exports.FieldError = react_1.default.forwardRef((props, ref) => {
766
+ const { errorMessage, asChild, className, children, ...otherProps } = props;
767
+ if (!errorMessage && !children)
768
+ return null;
769
+ return ((0, jsx_runtime_1.jsx)(react_2.AsChildSlot, { "data-testid": TestIds.fieldError, ref: ref, asChild: asChild, className: className, ...otherProps, children: children || errorMessage }));
770
+ });
771
+ exports.FieldError.displayName = 'Form.Field.Error';
772
+ exports.Field = FieldRoot;
773
+ exports.Field.Label = exports.FieldLabel;
774
+ exports.Field.InputWrapper = exports.FieldInputWrapper;
775
+ exports.Field.Input = exports.FieldInput;
776
+ exports.Field.Error = exports.FieldError;