@wix/headless-forms 0.0.10 → 0.0.11

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.
@@ -6,10 +6,12 @@ exports.LoadingError = LoadingError;
6
6
  exports.Error = Error;
7
7
  exports.Submitted = Submitted;
8
8
  exports.Fields = Fields;
9
+ exports.Field = Field;
9
10
  const jsx_runtime_1 = require("react/jsx-runtime");
10
11
  const services_manager_react_1 = require("@wix/services-manager-react");
11
12
  const services_manager_1 = require("@wix/services-manager");
12
13
  const form_service_js_1 = require("../../services/form-service.js");
14
+ const utils_js_1 = require("../utils.js");
13
15
  const DEFAULT_SUCCESS_MESSAGE = 'Your form has been submitted successfully.';
14
16
  /**
15
17
  * Root component that provides the Form service context to its children.
@@ -235,3 +237,42 @@ function Fields(props) {
235
237
  submitForm,
236
238
  });
237
239
  }
240
+ /**
241
+ * Headless Field component that provides field layout data and grid styles.
242
+ * This component accesses field configuration and calculates grid positioning.
243
+ *
244
+ * @component
245
+ * @param {FieldProps} props - Component props
246
+ * @param {FieldProps['children']} props.children - Render prop function that receives field layout data
247
+ * @example
248
+ * ```tsx
249
+ * import { Form } from '@wix/headless-forms/react';
250
+ *
251
+ * function CustomField({ id, layout }) {
252
+ * return (
253
+ * <Form.Field id={id} layout={layout}>
254
+ * {({ id, layout, gridStyles }) => (
255
+ * <div data-field-id={id}>
256
+ * <div style={gridStyles.label}>Label</div>
257
+ * <div style={gridStyles.input}>Input</div>
258
+ * </div>
259
+ * )}
260
+ * </Form.Field>
261
+ * );
262
+ * }
263
+ * ```
264
+ */
265
+ function Field(props) {
266
+ const { id, children, layout } = props;
267
+ const { formSignal } = (0, services_manager_react_1.useService)(form_service_js_1.FormServiceDefinition);
268
+ const form = formSignal.get();
269
+ if (!form) {
270
+ return null;
271
+ }
272
+ const gridStyles = (0, utils_js_1.calculateGridStyles)(layout);
273
+ return children({
274
+ id,
275
+ layout,
276
+ gridStyles,
277
+ });
278
+ }
@@ -0,0 +1,13 @@
1
+ import { Layout } from './core/Form';
2
+ export declare function calculateGridStyles(layout: Layout): {
3
+ label: {
4
+ gridRow: string;
5
+ gridColumn: string;
6
+ display: string;
7
+ alignItems: string;
8
+ };
9
+ input: {
10
+ gridRow: string;
11
+ gridColumn: string;
12
+ };
13
+ };
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.calculateGridStyles = calculateGridStyles;
4
+ function calculateGridStyles(layout) {
5
+ const labelRow = 1;
6
+ const inputRow = 2;
7
+ const gridColumn = `${layout.column + 1} / span ${layout.width}`;
8
+ return {
9
+ label: {
10
+ gridRow: `${labelRow} / span 1`,
11
+ gridColumn,
12
+ display: 'flex',
13
+ alignItems: 'flex-end',
14
+ },
15
+ input: {
16
+ gridRow: `${inputRow} / span 1`,
17
+ gridColumn,
18
+ },
19
+ };
20
+ }
@@ -13,6 +13,8 @@ export type SubmitResponse = {
13
13
  message: string;
14
14
  } | {
15
15
  type: 'idle';
16
+ } | {
17
+ type: 'loading';
16
18
  };
17
19
  /**
18
20
  * API interface for the Form service, providing reactive form data management.
@@ -71,7 +71,10 @@ exports.FormService = services_definitions_1.implementService.withConfig()(expor
71
71
  }
72
72
  async function defaultSubmitHandler(formId, formValues) {
73
73
  try {
74
- await forms_1.submissions.createSubmission({ formId, ...formValues });
74
+ await forms_1.submissions.createSubmission({
75
+ formId,
76
+ submissions: formValues,
77
+ });
75
78
  // TODO: add message
76
79
  return { type: 'success' };
77
80
  }
@@ -92,7 +95,7 @@ exports.FormService = services_definitions_1.implementService.withConfig()(expor
92
95
  }
93
96
  // @ts-expect-error
94
97
  const formId = form._id ? form._id : form.id;
95
- submitResponseSignal.set({ type: 'idle' });
98
+ submitResponseSignal.set({ type: 'loading' });
96
99
  try {
97
100
  const handler = config.onSubmit || defaultSubmitHandler;
98
101
  const response = await handler(formId, formValues);
@@ -303,6 +303,9 @@ export declare const Submitted: React.ForwardRefExoticComponent<SubmittedProps &
303
303
  /**
304
304
  * Mapping of form field types to their corresponding React components.
305
305
  *
306
+ * ALL field components in this map MUST use Form.Field for proper
307
+ * grid layout positioning.
308
+ *
306
309
  * Each key represents a field type identifier that matches the field types defined
307
310
  * in the form configuration, and each value is a React component that will receive
308
311
  * the field's props and render the appropriate UI element.
@@ -401,6 +404,8 @@ export interface FieldMap {
401
404
  *
402
405
  * @interface FieldsProps
403
406
  * @property {FieldMap} fieldMap - A mapping of field types to their corresponding React components
407
+ * @property {string} rowGapClassname - CSS class name for gap between rows
408
+ * @property {string} columnGapClassname - CSS class name for gap between columns
404
409
  * @example
405
410
  * ```tsx
406
411
  * const FIELD_MAP = {
@@ -414,11 +419,13 @@ export interface FieldMap {
414
419
  * // ... remaining field components
415
420
  * };
416
421
  *
417
- * <Form.Fields fieldMap={FIELD_MAP} />
422
+ * <Form.Fields fieldMap={FIELD_MAP} rowGapClassname="gap-y-4" columnGapClassname="gap-x-2" />
418
423
  * ```
419
424
  */
420
425
  interface FieldsProps {
421
426
  fieldMap: FieldMap;
427
+ rowGapClassname: string;
428
+ columnGapClassname: string;
422
429
  }
423
430
  /**
424
431
  * Fields component for rendering a form with custom field renderers.
@@ -428,6 +435,8 @@ interface FieldsProps {
428
435
  * @component
429
436
  * @param {FieldsProps} props - Component props
430
437
  * @param {FieldMap} props.fieldMap - A mapping of field types to their corresponding React components
438
+ * @param {string} props.rowGapClassname - CSS class name for gap between rows
439
+ * @param {string} props.columnGapClassname - CSS class name for gap between columns
431
440
  * @example
432
441
  * ```tsx
433
442
  * import { Form } from '@wix/headless-forms/react';
@@ -446,7 +455,11 @@ interface FieldsProps {
446
455
  * <Form.Root formServiceConfig={formServiceConfig}>
447
456
  * <Form.Loading className="flex justify-center p-4" />
448
457
  * <Form.LoadingError className="text-destructive px-4 py-3 rounded mb-4" />
449
- * <Form.Fields fieldMap={FIELD_MAP} />
458
+ * <Form.Fields
459
+ * fieldMap={FIELD_MAP}
460
+ * rowGapClassname="gap-y-4"
461
+ * columnGapClassname="gap-x-2"
462
+ * />
450
463
  * </Form.Root>
451
464
  * );
452
465
  * }
@@ -461,12 +474,15 @@ interface FieldsProps {
461
474
  * - Field validation and error display
462
475
  * - Form state management
463
476
  * - Field value updates
477
+ * - Grid layout with configurable row and column gaps
464
478
  *
465
479
  * Must be used within Form.Root to access form context.
466
480
  *
467
481
  * @component
468
482
  * @param {FieldsProps} props - The component props
469
483
  * @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.
484
+ * @param {string} props.rowGapClassname - CSS class name for gap between form rows
485
+ * @param {string} props.columnGapClassname - CSS class name for gap between form columns
470
486
  *
471
487
  * @example
472
488
  * ```tsx
@@ -534,7 +550,11 @@ interface FieldsProps {
534
550
  * <Form.Root formServiceConfig={formServiceConfig}>
535
551
  * <Form.Loading className="flex justify-center p-4" />
536
552
  * <Form.LoadingError className="text-destructive px-4 py-3 rounded mb-4" />
537
- * <Form.Fields fieldMap={FIELD_MAP} />
553
+ * <Form.Fields
554
+ * fieldMap={FIELD_MAP}
555
+ * rowGapClassname="gap-y-4"
556
+ * columnGapClassname="gap-x-2"
557
+ * />
538
558
  * <Form.Error className="text-destructive p-4 rounded-lg mb-4" />
539
559
  * <Form.Submitted className="text-green-500 p-4 rounded-lg mb-4" />
540
560
  * </Form.Root>
@@ -544,25 +564,130 @@ interface FieldsProps {
544
564
  *
545
565
  * @example
546
566
  * ```tsx
547
- * // Advanced usage with custom field components
548
- * const CustomTextField = ({ value, onChange, label, error, ...props }) => (
549
- * <div className="form-field">
550
- * <label className="text-foreground font-paragraph">{label}</label>
551
- * <input
552
- * value={value || ''}
553
- * onChange={(e) => onChange(e.target.value)}
554
- * className="bg-background border-foreground text-foreground"
555
- * {...props}
556
- * />
557
- * {error && <span className="text-destructive">{error}</span>}
558
- * </div>
559
- * );
567
+ * // Creating custom field components - ALL field components MUST use Form.Field
568
+ * // This example shows the REQUIRED structure for a TEXT_INPUT component
569
+ * import { Form, type TextInputProps } from '@wix/headless-forms/react';
570
+ *
571
+ * const TextInput = (props: TextInputProps) => {
572
+ * const { id, value, onChange, label, error, required, ...inputProps } = props;
573
+ *
574
+ * // Form.Field provides automatic grid layout positioning
575
+ * return (
576
+ * <Form.Field id={id}>
577
+ * <Form.Field.Label>
578
+ * <label className="text-foreground font-paragraph">
579
+ * {label}
580
+ * {required && <span className="text-destructive ml-1">*</span>}
581
+ * </label>
582
+ * </Form.Field.Label>
583
+ * <Form.Field.Input
584
+ * description={error && <span className="text-destructive text-sm">{error}</span>}
585
+ * >
586
+ * <input
587
+ * type="text"
588
+ * value={value || ''}
589
+ * onChange={(e) => onChange(e.target.value)}
590
+ * className="bg-background border-foreground text-foreground"
591
+ * aria-invalid={!!error}
592
+ * {...inputProps}
593
+ * />
594
+ * </Form.Field.Input>
595
+ * </Form.Field>
596
+ * );
597
+ * };
560
598
  *
561
599
  * const FIELD_MAP = {
562
- * TEXT_INPUT: CustomTextField,
563
- * // ... other field components
600
+ * TEXT_INPUT: TextInput,
601
+ * // ... all other field components must also use Form.Field
564
602
  * };
565
603
  * ```
566
604
  */
567
605
  export declare const Fields: React.ForwardRefExoticComponent<FieldsProps & React.RefAttributes<HTMLDivElement>>;
606
+ /**
607
+ * Props for Field container component
608
+ */
609
+ export interface FieldProps {
610
+ /** The unique identifier for this field */
611
+ id: string;
612
+ /** Child components (Field.Label, Field.Input, etc.) */
613
+ children: React.ReactNode;
614
+ /** Whether to render as a child component */
615
+ asChild?: boolean;
616
+ /** CSS classes to apply to the root element */
617
+ className?: string;
618
+ }
619
+ /**
620
+ * Field component with sub-components
621
+ */
622
+ interface FieldComponent extends React.ForwardRefExoticComponent<FieldProps & React.RefAttributes<HTMLDivElement>> {
623
+ Label: typeof FieldLabel;
624
+ Input: typeof FieldInput;
625
+ }
626
+ /**
627
+ * Props for Field.Label component
628
+ */
629
+ export interface FieldLabelProps {
630
+ /** Label content to display */
631
+ children: React.ReactNode;
632
+ /** Whether to render as a child component */
633
+ asChild?: boolean;
634
+ /** CSS classes to apply to the label element */
635
+ className?: string;
636
+ }
637
+ /**
638
+ * Props for Field.Input component
639
+ */
640
+ export interface FieldInputProps {
641
+ /** Input element to render */
642
+ children: React.ReactNode;
643
+ /** Whether to render as a child component */
644
+ asChild?: boolean;
645
+ /** CSS classes to apply to the input element */
646
+ className?: string;
647
+ /** Description text to display below the input */
648
+ description?: React.ReactNode;
649
+ }
650
+ /**
651
+ * Label component for a form field with automatic grid positioning.
652
+ * Must be used within a Form.Field component.
653
+ * Renders in the label row of the field's grid layout.
654
+ *
655
+ * @component
656
+ * @example
657
+ * ```tsx
658
+ * import { Form } from '@wix/headless-forms/react';
659
+ *
660
+ * <Form.Field id="email">
661
+ * <Form.Field.Label>
662
+ * <label className="text-foreground font-paragraph">Email Address</label>
663
+ * </Form.Field.Label>
664
+ * <Form.Field.Input>
665
+ * <input type="email" className="bg-background border-foreground" />
666
+ * </Form.Field.Input>
667
+ * </Form.Field>
668
+ * ```
669
+ */
670
+ export declare const FieldLabel: React.ForwardRefExoticComponent<FieldLabelProps & React.RefAttributes<HTMLDivElement>>;
671
+ /**
672
+ * Input component for a form field with automatic grid positioning.
673
+ * Must be used within a Form.Field component.
674
+ * Renders in the input row of the field's grid layout with optional description.
675
+ *
676
+ * @component
677
+ * @example
678
+ * ```tsx
679
+ * import { Form } from '@wix/headless-forms/react';
680
+ *
681
+ * <Form.Field id="password">
682
+ * <Form.Field.Label>
683
+ * <label className="text-foreground font-paragraph">Password</label>
684
+ * </Form.Field.Label>
685
+ * <Form.Field.Input description={<span className="text-secondary-foreground">Min 8 characters</span>}>
686
+ * <input type="password" className="bg-background border-foreground text-foreground" />
687
+ * </Form.Field.Input>
688
+ * </Form.Field>
689
+ * ```
690
+ */
691
+ export declare const FieldInput: React.ForwardRefExoticComponent<FieldInputProps & React.RefAttributes<HTMLDivElement>>;
692
+ export declare const Field: FieldComponent;
568
693
  export {};
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import React, { useState, useCallback } from 'react';
3
3
  import { AsChildSlot } from '@wix/headless-utils/react';
4
- import { Form as FormViewer, } from '@wix/form-public';
5
- import { Root as CoreRoot, Loading as CoreLoading, LoadingError as CoreLoadingError, Error as CoreError, Submitted as CoreSubmitted, Fields as CoreFields, } from './core/Form.js';
4
+ import { useForm, FormProvider, } from '@wix/form-public';
5
+ import { Root as CoreRoot, Loading as CoreLoading, LoadingError as CoreLoadingError, Error as CoreError, Submitted as CoreSubmitted, Fields as CoreFields, Field as CoreField, } from './core/Form.js';
6
6
  var TestIds;
7
7
  (function (TestIds) {
8
8
  TestIds["formRoot"] = "form-root";
@@ -11,6 +11,9 @@ var TestIds;
11
11
  TestIds["formLoadingError"] = "form-loading-error";
12
12
  TestIds["formError"] = "form-error";
13
13
  TestIds["formSubmitted"] = "form-submitted";
14
+ TestIds["fieldRoot"] = "field-root";
15
+ TestIds["fieldLabel"] = "field-label";
16
+ TestIds["fieldInput"] = "field-input";
14
17
  })(TestIds || (TestIds = {}));
15
18
  /**
16
19
  * Root component that provides all necessary service contexts for a complete form experience.
@@ -312,6 +315,8 @@ export const Submitted = React.forwardRef((props, ref) => {
312
315
  * @component
313
316
  * @param {FieldsProps} props - Component props
314
317
  * @param {FieldMap} props.fieldMap - A mapping of field types to their corresponding React components
318
+ * @param {string} props.rowGapClassname - CSS class name for gap between rows
319
+ * @param {string} props.columnGapClassname - CSS class name for gap between columns
315
320
  * @example
316
321
  * ```tsx
317
322
  * import { Form } from '@wix/headless-forms/react';
@@ -330,7 +335,11 @@ export const Submitted = React.forwardRef((props, ref) => {
330
335
  * <Form.Root formServiceConfig={formServiceConfig}>
331
336
  * <Form.Loading className="flex justify-center p-4" />
332
337
  * <Form.LoadingError className="text-destructive px-4 py-3 rounded mb-4" />
333
- * <Form.Fields fieldMap={FIELD_MAP} />
338
+ * <Form.Fields
339
+ * fieldMap={FIELD_MAP}
340
+ * rowGapClassname="gap-y-4"
341
+ * columnGapClassname="gap-x-2"
342
+ * />
334
343
  * </Form.Root>
335
344
  * );
336
345
  * }
@@ -345,12 +354,15 @@ export const Submitted = React.forwardRef((props, ref) => {
345
354
  * - Field validation and error display
346
355
  * - Form state management
347
356
  * - Field value updates
357
+ * - Grid layout with configurable row and column gaps
348
358
  *
349
359
  * Must be used within Form.Root to access form context.
350
360
  *
351
361
  * @component
352
362
  * @param {FieldsProps} props - The component props
353
363
  * @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.
364
+ * @param {string} props.rowGapClassname - CSS class name for gap between form rows
365
+ * @param {string} props.columnGapClassname - CSS class name for gap between form columns
354
366
  *
355
367
  * @example
356
368
  * ```tsx
@@ -418,7 +430,11 @@ export const Submitted = React.forwardRef((props, ref) => {
418
430
  * <Form.Root formServiceConfig={formServiceConfig}>
419
431
  * <Form.Loading className="flex justify-center p-4" />
420
432
  * <Form.LoadingError className="text-destructive px-4 py-3 rounded mb-4" />
421
- * <Form.Fields fieldMap={FIELD_MAP} />
433
+ * <Form.Fields
434
+ * fieldMap={FIELD_MAP}
435
+ * rowGapClassname="gap-y-4"
436
+ * columnGapClassname="gap-x-2"
437
+ * />
422
438
  * <Form.Error className="text-destructive p-4 rounded-lg mb-4" />
423
439
  * <Form.Submitted className="text-green-500 p-4 rounded-lg mb-4" />
424
440
  * </Form.Root>
@@ -428,23 +444,41 @@ export const Submitted = React.forwardRef((props, ref) => {
428
444
  *
429
445
  * @example
430
446
  * ```tsx
431
- * // Advanced usage with custom field components
432
- * const CustomTextField = ({ value, onChange, label, error, ...props }) => (
433
- * <div className="form-field">
434
- * <label className="text-foreground font-paragraph">{label}</label>
435
- * <input
436
- * value={value || ''}
437
- * onChange={(e) => onChange(e.target.value)}
438
- * className="bg-background border-foreground text-foreground"
439
- * {...props}
440
- * />
441
- * {error && <span className="text-destructive">{error}</span>}
442
- * </div>
443
- * );
447
+ * // Creating custom field components - ALL field components MUST use Form.Field
448
+ * // This example shows the REQUIRED structure for a TEXT_INPUT component
449
+ * import { Form, type TextInputProps } from '@wix/headless-forms/react';
450
+ *
451
+ * const TextInput = (props: TextInputProps) => {
452
+ * const { id, value, onChange, label, error, required, ...inputProps } = props;
453
+ *
454
+ * // Form.Field provides automatic grid layout positioning
455
+ * return (
456
+ * <Form.Field id={id}>
457
+ * <Form.Field.Label>
458
+ * <label className="text-foreground font-paragraph">
459
+ * {label}
460
+ * {required && <span className="text-destructive ml-1">*</span>}
461
+ * </label>
462
+ * </Form.Field.Label>
463
+ * <Form.Field.Input
464
+ * description={error && <span className="text-destructive text-sm">{error}</span>}
465
+ * >
466
+ * <input
467
+ * type="text"
468
+ * value={value || ''}
469
+ * onChange={(e) => onChange(e.target.value)}
470
+ * className="bg-background border-foreground text-foreground"
471
+ * aria-invalid={!!error}
472
+ * {...inputProps}
473
+ * />
474
+ * </Form.Field.Input>
475
+ * </Form.Field>
476
+ * );
477
+ * };
444
478
  *
445
479
  * const FIELD_MAP = {
446
- * TEXT_INPUT: CustomTextField,
447
- * // ... other field components
480
+ * TEXT_INPUT: TextInput,
481
+ * // ... all other field components must also use Form.Field
448
482
  * };
449
483
  * ```
450
484
  */
@@ -460,6 +494,164 @@ export const Fields = React.forwardRef((props, ref) => {
460
494
  return (_jsx(CoreFields, { children: ({ form, submitForm }) => {
461
495
  if (!form)
462
496
  return null;
463
- return (_jsx("div", { ref: ref, children: _jsx(FormViewer, { form: form, values: formValues, onChange: handleFormChange, errors: formErrors, onValidate: handleFormValidate, fields: props.fieldMap, submitForm: () => submitForm(formValues) }) }));
497
+ return (_jsx("div", { ref: ref, children: _jsx(FormProvider, { children: _jsx(FieldsWithForm, { form: form, values: formValues, onChange: handleFormChange, errors: formErrors, onValidate: handleFormValidate, fields: props.fieldMap, submitForm: () => submitForm(formValues), rowGapClassname: props.rowGapClassname, columnGapClassname: props.columnGapClassname }) }) }));
498
+ } }));
499
+ });
500
+ const FieldsWithForm = ({ form, submitForm, values, onChange, errors, onValidate, fields: fieldMap, rowGapClassname, columnGapClassname, }) => {
501
+ const formData = useForm({
502
+ form,
503
+ values,
504
+ onChange,
505
+ errors,
506
+ onValidate,
507
+ submitForm,
508
+ fieldMap,
509
+ });
510
+ if (!formData)
511
+ return null;
512
+ console.log('formData', formData);
513
+ const { columnCount, fieldElements, fieldsLayout } = formData;
514
+ return (
515
+ // TODO: use readOnly, isDisabled
516
+ // TODO: step title a11y support
517
+ // TODO: mobile support?
518
+ _jsx(FieldLayoutProvider, { value: fieldsLayout, children: _jsx("form", { onSubmit: (e) => e.preventDefault(), children: _jsx("fieldset", { style: { display: 'flex', flexDirection: 'column' }, className: rowGapClassname, children: fieldElements.map((rowElements, index) => {
519
+ return (_jsx("div", { style: {
520
+ display: 'grid',
521
+ width: '100%',
522
+ gridTemplateColumns: `repeat(${columnCount}, 1fr)`,
523
+ gridAutoRows: 'minmax(min-content, max-content)',
524
+ }, className: columnGapClassname, children: rowElements }, index));
525
+ }) }) }) }));
526
+ };
527
+ /**
528
+ * Context for sharing field layout data across the form
529
+ * @internal
530
+ */
531
+ const FieldLayoutContext = React.createContext(null);
532
+ /**
533
+ * Provider component that makes field layout data available to child components
534
+ * @internal
535
+ */
536
+ const FieldLayoutProvider = ({ value, children, }) => {
537
+ return (_jsx(FieldLayoutContext.Provider, { value: value, children: children }));
538
+ };
539
+ /**
540
+ * Hook to access layout configuration for a specific field
541
+ * @internal
542
+ * @param {string} fieldId - The unique identifier of the field
543
+ * @returns {Layout | null} The layout configuration for the field, or null if not found
544
+ */
545
+ function useFieldLayout(fieldId) {
546
+ const layoutMap = React.useContext(FieldLayoutContext);
547
+ if (!layoutMap) {
548
+ return null;
549
+ }
550
+ return layoutMap[fieldId] || null;
551
+ }
552
+ const FieldContext = React.createContext(null);
553
+ /**
554
+ * Hook to access field context
555
+ */
556
+ function useFieldContext() {
557
+ const context = React.useContext(FieldContext);
558
+ if (!context) {
559
+ throw new globalThis.Error('Field components must be used within a Form.Field component');
560
+ }
561
+ return context;
562
+ }
563
+ /**
564
+ * Container component for a form field with grid layout support.
565
+ * Provides context to Field.Label and Field.Input child components.
566
+ * Based on the default-field-layout functionality.
567
+ *
568
+ * @component
569
+ * @example
570
+ * ```tsx
571
+ * import { Form } from '@wix/headless-forms/react';
572
+ *
573
+ * function FormFields() {
574
+ * return (
575
+ * <Form.Field id="username">
576
+ * <Form.Field.Label>
577
+ * <label className="text-foreground font-paragraph">Username</label>
578
+ * </Form.Field.Label>
579
+ * <Form.Field.Input description={<span className="text-secondary-foreground">Required</span>}>
580
+ * <input className="bg-background border-foreground text-foreground" />
581
+ * </Form.Field.Input>
582
+ * </Form.Field>
583
+ * );
584
+ * }
585
+ * ```
586
+ */
587
+ const FieldRoot = React.forwardRef((props, ref) => {
588
+ const { id, children, asChild, className, ...otherProps } = props;
589
+ const layout = useFieldLayout(id);
590
+ if (!layout) {
591
+ return null;
592
+ }
593
+ return (_jsx(CoreField, { id: id, layout: layout, children: (fieldData) => {
594
+ const contextValue = {
595
+ id,
596
+ layout: fieldData.layout,
597
+ gridStyles: fieldData.gridStyles,
598
+ };
599
+ return (_jsx(FieldContext.Provider, { value: contextValue, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.fieldRoot, customElement: children, customElementProps: {}, ...otherProps, children: children }) }));
464
600
  } }));
465
601
  });
602
+ FieldRoot.displayName = 'Form.Field';
603
+ /**
604
+ * Label component for a form field with automatic grid positioning.
605
+ * Must be used within a Form.Field component.
606
+ * Renders in the label row of the field's grid layout.
607
+ *
608
+ * @component
609
+ * @example
610
+ * ```tsx
611
+ * import { Form } from '@wix/headless-forms/react';
612
+ *
613
+ * <Form.Field id="email">
614
+ * <Form.Field.Label>
615
+ * <label className="text-foreground font-paragraph">Email Address</label>
616
+ * </Form.Field.Label>
617
+ * <Form.Field.Input>
618
+ * <input type="email" className="bg-background border-foreground" />
619
+ * </Form.Field.Input>
620
+ * </Form.Field>
621
+ * ```
622
+ */
623
+ export const FieldLabel = React.forwardRef((props, ref) => {
624
+ const { children, asChild, className, ...otherProps } = props;
625
+ const { gridStyles } = useFieldContext();
626
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, style: gridStyles.label, "data-testid": TestIds.fieldLabel, customElement: children, customElementProps: {}, ...otherProps, children: _jsx("div", { children: children }) }));
627
+ });
628
+ FieldLabel.displayName = 'Form.Field.Label';
629
+ /**
630
+ * Input component for a form field with automatic grid positioning.
631
+ * Must be used within a Form.Field component.
632
+ * Renders in the input row of the field's grid layout with optional description.
633
+ *
634
+ * @component
635
+ * @example
636
+ * ```tsx
637
+ * import { Form } from '@wix/headless-forms/react';
638
+ *
639
+ * <Form.Field id="password">
640
+ * <Form.Field.Label>
641
+ * <label className="text-foreground font-paragraph">Password</label>
642
+ * </Form.Field.Label>
643
+ * <Form.Field.Input description={<span className="text-secondary-foreground">Min 8 characters</span>}>
644
+ * <input type="password" className="bg-background border-foreground text-foreground" />
645
+ * </Form.Field.Input>
646
+ * </Form.Field>
647
+ * ```
648
+ */
649
+ export const FieldInput = React.forwardRef((props, ref) => {
650
+ const { children, description, asChild, className, ...otherProps } = props;
651
+ const { gridStyles } = useFieldContext();
652
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, style: gridStyles.input, "data-testid": TestIds.fieldInput, customElement: children, customElementProps: {}, ...otherProps, children: _jsx("div", { children: children }) }));
653
+ });
654
+ FieldInput.displayName = 'Form.Field.Input';
655
+ export const Field = FieldRoot;
656
+ Field.Label = FieldLabel;
657
+ Field.Input = FieldInput;