@wix/headless-forms 0.0.10 → 0.0.12
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/cjs/dist/react/Form.d.ts +235 -18
- package/cjs/dist/react/Form.js +251 -19
- package/cjs/dist/react/context/FieldContext.d.ts +12 -0
- package/cjs/dist/react/context/FieldContext.js +16 -0
- package/cjs/dist/react/context/FieldLayoutContext.d.ts +12 -0
- package/cjs/dist/react/context/FieldLayoutContext.js +21 -0
- package/cjs/dist/react/core/Form.d.ts +80 -5
- package/cjs/dist/react/core/Form.js +41 -0
- package/cjs/dist/react/utils.d.ts +13 -0
- package/cjs/dist/react/utils.js +20 -0
- package/cjs/dist/services/form-service.d.ts +2 -0
- package/cjs/dist/services/form-service.js +5 -2
- package/dist/react/Form.d.ts +235 -18
- package/dist/react/Form.js +252 -20
- package/dist/react/context/FieldContext.d.ts +12 -0
- package/dist/react/context/FieldContext.js +9 -0
- package/dist/react/context/FieldLayoutContext.d.ts +12 -0
- package/dist/react/context/FieldLayoutContext.js +13 -0
- package/dist/react/core/Form.d.ts +80 -5
- package/dist/react/core/Form.js +40 -0
- package/dist/react/utils.d.ts +13 -0
- package/dist/react/utils.js +17 -0
- package/dist/services/form-service.d.ts +2 -0
- package/dist/services/form-service.js +5 -2
- package/package.json +3 -3
package/cjs/dist/react/Form.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { type FormError } from '@wix/form-public';
|
|
2
3
|
import { type CheckboxGroupProps, type CheckboxProps, type PhoneInputProps, type DateInputProps, type DatePickerProps, type DateTimeInputProps, type DropdownProps, type FileUploadProps, type MultilineAddressProps, type NumberInputProps, type RadioGroupProps, type RatingInputProps, type RichTextProps, type SignatureProps, type SubmitButtonProps, type TagsProps, type TextAreaProps, type TextInputProps, type TimeInputProps, type ProductListProps, type FixedPaymentProps, type PaymentInputProps, type DonationProps, type AppointmentProps, type ImageChoiceProps } from './types.js';
|
|
3
4
|
import { type FormServiceConfig } from '../services/form-service.js';
|
|
4
5
|
/**
|
|
@@ -303,6 +304,9 @@ export declare const Submitted: React.ForwardRefExoticComponent<SubmittedProps &
|
|
|
303
304
|
/**
|
|
304
305
|
* Mapping of form field types to their corresponding React components.
|
|
305
306
|
*
|
|
307
|
+
* ALL field components in this map MUST use Form.Field for proper
|
|
308
|
+
* grid layout positioning.
|
|
309
|
+
*
|
|
306
310
|
* Each key represents a field type identifier that matches the field types defined
|
|
307
311
|
* in the form configuration, and each value is a React component that will receive
|
|
308
312
|
* the field's props and render the appropriate UI element.
|
|
@@ -401,6 +405,8 @@ export interface FieldMap {
|
|
|
401
405
|
*
|
|
402
406
|
* @interface FieldsProps
|
|
403
407
|
* @property {FieldMap} fieldMap - A mapping of field types to their corresponding React components
|
|
408
|
+
* @property {string} rowGapClassname - CSS class name for gap between rows
|
|
409
|
+
* @property {string} columnGapClassname - CSS class name for gap between columns
|
|
404
410
|
* @example
|
|
405
411
|
* ```tsx
|
|
406
412
|
* const FIELD_MAP = {
|
|
@@ -414,11 +420,13 @@ export interface FieldMap {
|
|
|
414
420
|
* // ... remaining field components
|
|
415
421
|
* };
|
|
416
422
|
*
|
|
417
|
-
* <Form.Fields fieldMap={FIELD_MAP} />
|
|
423
|
+
* <Form.Fields fieldMap={FIELD_MAP} rowGapClassname="gap-y-4" columnGapClassname="gap-x-2" />
|
|
418
424
|
* ```
|
|
419
425
|
*/
|
|
420
426
|
interface FieldsProps {
|
|
421
427
|
fieldMap: FieldMap;
|
|
428
|
+
rowGapClassname: string;
|
|
429
|
+
columnGapClassname: string;
|
|
422
430
|
}
|
|
423
431
|
/**
|
|
424
432
|
* Fields component for rendering a form with custom field renderers.
|
|
@@ -428,6 +436,8 @@ interface FieldsProps {
|
|
|
428
436
|
* @component
|
|
429
437
|
* @param {FieldsProps} props - Component props
|
|
430
438
|
* @param {FieldMap} props.fieldMap - A mapping of field types to their corresponding React components
|
|
439
|
+
* @param {string} props.rowGapClassname - CSS class name for gap between rows
|
|
440
|
+
* @param {string} props.columnGapClassname - CSS class name for gap between columns
|
|
431
441
|
* @example
|
|
432
442
|
* ```tsx
|
|
433
443
|
* import { Form } from '@wix/headless-forms/react';
|
|
@@ -446,7 +456,11 @@ interface FieldsProps {
|
|
|
446
456
|
* <Form.Root formServiceConfig={formServiceConfig}>
|
|
447
457
|
* <Form.Loading className="flex justify-center p-4" />
|
|
448
458
|
* <Form.LoadingError className="text-destructive px-4 py-3 rounded mb-4" />
|
|
449
|
-
* <Form.Fields
|
|
459
|
+
* <Form.Fields
|
|
460
|
+
* fieldMap={FIELD_MAP}
|
|
461
|
+
* rowGapClassname="gap-y-4"
|
|
462
|
+
* columnGapClassname="gap-x-2"
|
|
463
|
+
* />
|
|
450
464
|
* </Form.Root>
|
|
451
465
|
* );
|
|
452
466
|
* }
|
|
@@ -461,12 +475,15 @@ interface FieldsProps {
|
|
|
461
475
|
* - Field validation and error display
|
|
462
476
|
* - Form state management
|
|
463
477
|
* - Field value updates
|
|
478
|
+
* - Grid layout with configurable row and column gaps
|
|
464
479
|
*
|
|
465
480
|
* Must be used within Form.Root to access form context.
|
|
466
481
|
*
|
|
467
482
|
* @component
|
|
468
483
|
* @param {FieldsProps} props - The component props
|
|
469
484
|
* @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.
|
|
485
|
+
* @param {string} props.rowGapClassname - CSS class name for gap between form rows
|
|
486
|
+
* @param {string} props.columnGapClassname - CSS class name for gap between form columns
|
|
470
487
|
*
|
|
471
488
|
* @example
|
|
472
489
|
* ```tsx
|
|
@@ -534,7 +551,11 @@ interface FieldsProps {
|
|
|
534
551
|
* <Form.Root formServiceConfig={formServiceConfig}>
|
|
535
552
|
* <Form.Loading className="flex justify-center p-4" />
|
|
536
553
|
* <Form.LoadingError className="text-destructive px-4 py-3 rounded mb-4" />
|
|
537
|
-
* <Form.Fields
|
|
554
|
+
* <Form.Fields
|
|
555
|
+
* fieldMap={FIELD_MAP}
|
|
556
|
+
* rowGapClassname="gap-y-4"
|
|
557
|
+
* columnGapClassname="gap-x-2"
|
|
558
|
+
* />
|
|
538
559
|
* <Form.Error className="text-destructive p-4 rounded-lg mb-4" />
|
|
539
560
|
* <Form.Submitted className="text-green-500 p-4 rounded-lg mb-4" />
|
|
540
561
|
* </Form.Root>
|
|
@@ -544,25 +565,221 @@ interface FieldsProps {
|
|
|
544
565
|
*
|
|
545
566
|
* @example
|
|
546
567
|
* ```tsx
|
|
547
|
-
* //
|
|
548
|
-
*
|
|
549
|
-
*
|
|
550
|
-
*
|
|
551
|
-
*
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
*
|
|
556
|
-
*
|
|
557
|
-
*
|
|
558
|
-
*
|
|
559
|
-
*
|
|
568
|
+
* // Creating custom field components - ALL field components MUST use Form.Field
|
|
569
|
+
* // This example shows the REQUIRED structure for a TEXT_INPUT component
|
|
570
|
+
* import { Form, type TextInputProps } from '@wix/headless-forms/react';
|
|
571
|
+
*
|
|
572
|
+
* const TextInput = (props: TextInputProps) => {
|
|
573
|
+
* const { id, value, onChange, label, error, required, ...inputProps } = props;
|
|
574
|
+
*
|
|
575
|
+
* // Form.Field provides automatic grid layout positioning
|
|
576
|
+
* return (
|
|
577
|
+
* <Form.Field id={id}>
|
|
578
|
+
* <Form.Field.Label>
|
|
579
|
+
* <label className="text-foreground font-paragraph">
|
|
580
|
+
* {label}
|
|
581
|
+
* {required && <span className="text-destructive ml-1">*</span>}
|
|
582
|
+
* </label>
|
|
583
|
+
* </Form.Field.Label>
|
|
584
|
+
* <Form.Field.Input
|
|
585
|
+
* description={error && <span className="text-destructive text-sm">{error}</span>}
|
|
586
|
+
* >
|
|
587
|
+
* <input
|
|
588
|
+
* type="text"
|
|
589
|
+
* value={value || ''}
|
|
590
|
+
* onChange={(e) => onChange(e.target.value)}
|
|
591
|
+
* className="bg-background border-foreground text-foreground"
|
|
592
|
+
* aria-invalid={!!error}
|
|
593
|
+
* {...inputProps}
|
|
594
|
+
* />
|
|
595
|
+
* </Form.Field.Input>
|
|
596
|
+
* </Form.Field>
|
|
597
|
+
* );
|
|
598
|
+
* };
|
|
560
599
|
*
|
|
561
600
|
* const FIELD_MAP = {
|
|
562
|
-
* TEXT_INPUT:
|
|
563
|
-
* // ... other field components
|
|
601
|
+
* TEXT_INPUT: TextInput,
|
|
602
|
+
* // ... all other field components must also use Form.Field
|
|
564
603
|
* };
|
|
565
604
|
* ```
|
|
566
605
|
*/
|
|
567
606
|
export declare const Fields: React.ForwardRefExoticComponent<FieldsProps & React.RefAttributes<HTMLDivElement>>;
|
|
607
|
+
/**
|
|
608
|
+
* Props for Field container component
|
|
609
|
+
*/
|
|
610
|
+
export interface FieldProps {
|
|
611
|
+
/** The unique identifier for this field */
|
|
612
|
+
id: string;
|
|
613
|
+
/** Child components (Field.Label, Field.Input, etc.) */
|
|
614
|
+
children: React.ReactNode;
|
|
615
|
+
/** Whether to render as a child component */
|
|
616
|
+
asChild?: boolean;
|
|
617
|
+
/** CSS classes to apply to the root element */
|
|
618
|
+
className?: string;
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Field component with sub-components
|
|
622
|
+
*/
|
|
623
|
+
interface FieldComponent extends React.ForwardRefExoticComponent<FieldProps & React.RefAttributes<HTMLDivElement>> {
|
|
624
|
+
Label: typeof FieldLabel;
|
|
625
|
+
InputWrapper: typeof FieldInputWrapper;
|
|
626
|
+
Input: typeof FieldInput;
|
|
627
|
+
Error: typeof FieldError;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Props for Field.Label component
|
|
631
|
+
*/
|
|
632
|
+
export interface FieldLabelProps {
|
|
633
|
+
/** Label content to display */
|
|
634
|
+
children: React.ReactNode;
|
|
635
|
+
/** Whether to render as a child component */
|
|
636
|
+
asChild?: boolean;
|
|
637
|
+
/** CSS classes to apply to the label element */
|
|
638
|
+
className?: string;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Props for Field.InputWrapper component
|
|
642
|
+
*/
|
|
643
|
+
export interface FieldInputWrapperProps {
|
|
644
|
+
/** Child components (typically Field.Input and Field.Error) */
|
|
645
|
+
children: React.ReactNode;
|
|
646
|
+
/** Whether to render as a child component */
|
|
647
|
+
asChild?: boolean;
|
|
648
|
+
/** CSS classes to apply to the wrapper element */
|
|
649
|
+
className?: string;
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Props for Field.Input component
|
|
653
|
+
*/
|
|
654
|
+
export interface FieldInputProps {
|
|
655
|
+
/** Input element to render */
|
|
656
|
+
children: React.ReactNode;
|
|
657
|
+
/** Whether to render as a child component */
|
|
658
|
+
asChild?: boolean;
|
|
659
|
+
/** CSS classes to apply to the input element */
|
|
660
|
+
className?: string;
|
|
661
|
+
/** Description text to display below the input */
|
|
662
|
+
description?: React.ReactNode;
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Render props for Field.Error component
|
|
666
|
+
*/
|
|
667
|
+
export interface FieldErrorRenderProps {
|
|
668
|
+
/** The error type */
|
|
669
|
+
type: FormError['errorType'];
|
|
670
|
+
/** The error message */
|
|
671
|
+
message: string;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Props for Field.Error component
|
|
675
|
+
*/
|
|
676
|
+
export interface FieldErrorProps {
|
|
677
|
+
/** Whether to render as a child component */
|
|
678
|
+
asChild?: boolean;
|
|
679
|
+
/** CSS classes to apply to the error element */
|
|
680
|
+
className?: string;
|
|
681
|
+
/** The error message */
|
|
682
|
+
errorMessage?: string;
|
|
683
|
+
/** Child components to render */
|
|
684
|
+
children?: React.ReactNode;
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Label component for a form field with automatic grid positioning.
|
|
688
|
+
* Must be used within a Form.Field component.
|
|
689
|
+
* Renders in the label row of the field's grid layout.
|
|
690
|
+
*
|
|
691
|
+
* @component
|
|
692
|
+
* @example
|
|
693
|
+
* ```tsx
|
|
694
|
+
* import { Form } from '@wix/headless-forms/react';
|
|
695
|
+
*
|
|
696
|
+
* <Form.Field id="email">
|
|
697
|
+
* <Form.Field.Label>
|
|
698
|
+
* <label className="text-foreground font-paragraph">Email Address</label>
|
|
699
|
+
* </Form.Field.Label>
|
|
700
|
+
* <Form.Field.InputWrapper>
|
|
701
|
+
* <Form.Field.Input>
|
|
702
|
+
* <input type="email" className="bg-background border-foreground text-foreground" />
|
|
703
|
+
* </Form.Field.Input>
|
|
704
|
+
* </Form.Field.InputWrapper>
|
|
705
|
+
* </Form.Field>
|
|
706
|
+
* ```
|
|
707
|
+
*/
|
|
708
|
+
export declare const FieldLabel: React.ForwardRefExoticComponent<FieldLabelProps & React.RefAttributes<HTMLDivElement>>;
|
|
709
|
+
/**
|
|
710
|
+
* InputWrapper component that wraps input and error elements with grid positioning.
|
|
711
|
+
* Must be used within a Form.Field component.
|
|
712
|
+
* This wrapper applies the grid positioning styles to contain both the input and error.
|
|
713
|
+
*
|
|
714
|
+
* @component
|
|
715
|
+
* @example
|
|
716
|
+
* ```tsx
|
|
717
|
+
* import { Form } from '@wix/headless-forms/react';
|
|
718
|
+
*
|
|
719
|
+
* <Form.Field id="email">
|
|
720
|
+
* <Form.Field.Label>
|
|
721
|
+
* <label className="text-foreground font-paragraph">Email Address</label>
|
|
722
|
+
* </Form.Field.Label>
|
|
723
|
+
* <Form.Field.InputWrapper>
|
|
724
|
+
* <Form.Field.Input>
|
|
725
|
+
* <input type="email" className="bg-background border-foreground text-foreground" />
|
|
726
|
+
* </Form.Field.Input>
|
|
727
|
+
* <Form.Field.Error>
|
|
728
|
+
* <span className="text-destructive text-sm font-paragraph">Please enter a valid email</span>
|
|
729
|
+
* </Form.Field.Error>
|
|
730
|
+
* </Form.Field.InputWrapper>
|
|
731
|
+
* </Form.Field>
|
|
732
|
+
* ```
|
|
733
|
+
*/
|
|
734
|
+
export declare const FieldInputWrapper: React.ForwardRefExoticComponent<FieldInputWrapperProps & React.RefAttributes<HTMLDivElement>>;
|
|
735
|
+
/**
|
|
736
|
+
* Input component for a form field.
|
|
737
|
+
* Must be used within a Form.Field.InputWrapper component.
|
|
738
|
+
* Renders the actual input element without grid positioning.
|
|
739
|
+
*
|
|
740
|
+
* @component
|
|
741
|
+
* @example
|
|
742
|
+
* ```tsx
|
|
743
|
+
* import { Form } from '@wix/headless-forms/react';
|
|
744
|
+
*
|
|
745
|
+
* <Form.Field id="password">
|
|
746
|
+
* <Form.Field.Label>
|
|
747
|
+
* <label className="text-foreground font-paragraph">Password</label>
|
|
748
|
+
* </Form.Field.Label>
|
|
749
|
+
* <Form.Field.InputWrapper>
|
|
750
|
+
* <Form.Field.Input description={<span className="text-secondary-foreground">Min 8 characters</span>}>
|
|
751
|
+
* <input type="password" className="bg-background border-foreground text-foreground" />
|
|
752
|
+
* </Form.Field.Input>
|
|
753
|
+
* </Form.Field.InputWrapper>
|
|
754
|
+
* </Form.Field>
|
|
755
|
+
* ```
|
|
756
|
+
*/
|
|
757
|
+
export declare const FieldInput: React.ForwardRefExoticComponent<FieldInputProps & React.RefAttributes<HTMLDivElement>>;
|
|
758
|
+
/**
|
|
759
|
+
* Error component for displaying field-level validation errors.
|
|
760
|
+
* Must be used within a Form.Field.InputWrapper component.
|
|
761
|
+
* Only renders when there is an error for the current field.
|
|
762
|
+
*
|
|
763
|
+
* @component
|
|
764
|
+
* @example
|
|
765
|
+
* ```tsx
|
|
766
|
+
* import { Form } from '@wix/headless-forms/react';
|
|
767
|
+
*
|
|
768
|
+
* <Form.Field id="email">
|
|
769
|
+
* <Form.Field.Label>
|
|
770
|
+
* <label className="text-foreground font-paragraph">Email Address</label>
|
|
771
|
+
* </Form.Field.Label>
|
|
772
|
+
* <Form.Field.InputWrapper>
|
|
773
|
+
* <Form.Field.Input>
|
|
774
|
+
* <input type="email" className="bg-background border-foreground text-foreground" />
|
|
775
|
+
* </Form.Field.Input>
|
|
776
|
+
* <Form.Field.Error path="email">
|
|
777
|
+
* <span className="text-destructive text-sm font-paragraph">Please enter a valid email address</span>
|
|
778
|
+
* </Form.Field.Error>
|
|
779
|
+
* </Form.Field.InputWrapper>
|
|
780
|
+
* </Form.Field>
|
|
781
|
+
* ```
|
|
782
|
+
*/
|
|
783
|
+
export declare const FieldError: React.ForwardRefExoticComponent<FieldErrorProps & React.RefAttributes<HTMLDivElement>>;
|
|
784
|
+
export declare const Field: FieldComponent;
|
|
568
785
|
export {};
|
package/cjs/dist/react/Form.js
CHANGED
|
@@ -33,12 +33,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.Fields = exports.Submitted = exports.Error = exports.LoadingError = exports.Loading = exports.Root = void 0;
|
|
36
|
+
exports.Field = exports.FieldError = exports.FieldInput = exports.FieldInputWrapper = exports.FieldLabel = exports.Fields = exports.Submitted = exports.Error = exports.LoadingError = exports.Loading = exports.Root = void 0;
|
|
37
37
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
38
|
const react_1 = __importStar(require("react"));
|
|
39
39
|
const react_2 = require("@wix/headless-utils/react");
|
|
40
40
|
const form_public_1 = require("@wix/form-public");
|
|
41
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");
|
|
42
44
|
var TestIds;
|
|
43
45
|
(function (TestIds) {
|
|
44
46
|
TestIds["formRoot"] = "form-root";
|
|
@@ -47,6 +49,11 @@ var TestIds;
|
|
|
47
49
|
TestIds["formLoadingError"] = "form-loading-error";
|
|
48
50
|
TestIds["formError"] = "form-error";
|
|
49
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";
|
|
50
57
|
})(TestIds || (TestIds = {}));
|
|
51
58
|
/**
|
|
52
59
|
* Root component that provides all necessary service contexts for a complete form experience.
|
|
@@ -348,6 +355,8 @@ exports.Submitted = react_1.default.forwardRef((props, ref) => {
|
|
|
348
355
|
* @component
|
|
349
356
|
* @param {FieldsProps} props - Component props
|
|
350
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
|
|
351
360
|
* @example
|
|
352
361
|
* ```tsx
|
|
353
362
|
* import { Form } from '@wix/headless-forms/react';
|
|
@@ -366,7 +375,11 @@ exports.Submitted = react_1.default.forwardRef((props, ref) => {
|
|
|
366
375
|
* <Form.Root formServiceConfig={formServiceConfig}>
|
|
367
376
|
* <Form.Loading className="flex justify-center p-4" />
|
|
368
377
|
* <Form.LoadingError className="text-destructive px-4 py-3 rounded mb-4" />
|
|
369
|
-
* <Form.Fields
|
|
378
|
+
* <Form.Fields
|
|
379
|
+
* fieldMap={FIELD_MAP}
|
|
380
|
+
* rowGapClassname="gap-y-4"
|
|
381
|
+
* columnGapClassname="gap-x-2"
|
|
382
|
+
* />
|
|
370
383
|
* </Form.Root>
|
|
371
384
|
* );
|
|
372
385
|
* }
|
|
@@ -381,12 +394,15 @@ exports.Submitted = react_1.default.forwardRef((props, ref) => {
|
|
|
381
394
|
* - Field validation and error display
|
|
382
395
|
* - Form state management
|
|
383
396
|
* - Field value updates
|
|
397
|
+
* - Grid layout with configurable row and column gaps
|
|
384
398
|
*
|
|
385
399
|
* Must be used within Form.Root to access form context.
|
|
386
400
|
*
|
|
387
401
|
* @component
|
|
388
402
|
* @param {FieldsProps} props - The component props
|
|
389
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
|
|
390
406
|
*
|
|
391
407
|
* @example
|
|
392
408
|
* ```tsx
|
|
@@ -454,7 +470,11 @@ exports.Submitted = react_1.default.forwardRef((props, ref) => {
|
|
|
454
470
|
* <Form.Root formServiceConfig={formServiceConfig}>
|
|
455
471
|
* <Form.Loading className="flex justify-center p-4" />
|
|
456
472
|
* <Form.LoadingError className="text-destructive px-4 py-3 rounded mb-4" />
|
|
457
|
-
* <Form.Fields
|
|
473
|
+
* <Form.Fields
|
|
474
|
+
* fieldMap={FIELD_MAP}
|
|
475
|
+
* rowGapClassname="gap-y-4"
|
|
476
|
+
* columnGapClassname="gap-x-2"
|
|
477
|
+
* />
|
|
458
478
|
* <Form.Error className="text-destructive p-4 rounded-lg mb-4" />
|
|
459
479
|
* <Form.Submitted className="text-green-500 p-4 rounded-lg mb-4" />
|
|
460
480
|
* </Form.Root>
|
|
@@ -464,23 +484,41 @@ exports.Submitted = react_1.default.forwardRef((props, ref) => {
|
|
|
464
484
|
*
|
|
465
485
|
* @example
|
|
466
486
|
* ```tsx
|
|
467
|
-
* //
|
|
468
|
-
*
|
|
469
|
-
*
|
|
470
|
-
*
|
|
471
|
-
*
|
|
472
|
-
*
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
476
|
-
*
|
|
477
|
-
*
|
|
478
|
-
*
|
|
479
|
-
*
|
|
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
|
+
* };
|
|
480
518
|
*
|
|
481
519
|
* const FIELD_MAP = {
|
|
482
|
-
* TEXT_INPUT:
|
|
483
|
-
* // ... other field components
|
|
520
|
+
* TEXT_INPUT: TextInput,
|
|
521
|
+
* // ... all other field components must also use Form.Field
|
|
484
522
|
* };
|
|
485
523
|
* ```
|
|
486
524
|
*/
|
|
@@ -496,6 +534,200 @@ exports.Fields = react_1.default.forwardRef((props, ref) => {
|
|
|
496
534
|
return ((0, jsx_runtime_1.jsx)(Form_js_1.Fields, { children: ({ form, submitForm }) => {
|
|
497
535
|
if (!form)
|
|
498
536
|
return null;
|
|
499
|
-
return ((0, jsx_runtime_1.jsx)("div", { ref: ref, children: (0, jsx_runtime_1.jsx)(form_public_1.
|
|
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 }) }));
|
|
500
608
|
} }));
|
|
501
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">Email Address</label>
|
|
624
|
+
* </Form.Field.Label>
|
|
625
|
+
* <Form.Field.InputWrapper>
|
|
626
|
+
* <Form.Field.Input>
|
|
627
|
+
* <input type="email" className="bg-background border-foreground text-foreground" />
|
|
628
|
+
* </Form.Field.Input>
|
|
629
|
+
* </Form.Field.InputWrapper>
|
|
630
|
+
* </Form.Field>
|
|
631
|
+
* ```
|
|
632
|
+
*/
|
|
633
|
+
exports.FieldLabel = react_1.default.forwardRef((props, ref) => {
|
|
634
|
+
const { children, asChild, className, ...otherProps } = props;
|
|
635
|
+
const { gridStyles } = (0, FieldContext_js_1.useFieldContext)();
|
|
636
|
+
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 }) }));
|
|
637
|
+
});
|
|
638
|
+
exports.FieldLabel.displayName = 'Form.Field.Label';
|
|
639
|
+
/**
|
|
640
|
+
* InputWrapper component that wraps input and error elements with grid positioning.
|
|
641
|
+
* Must be used within a Form.Field component.
|
|
642
|
+
* This wrapper applies the grid positioning styles to contain both the input and error.
|
|
643
|
+
*
|
|
644
|
+
* @component
|
|
645
|
+
* @example
|
|
646
|
+
* ```tsx
|
|
647
|
+
* import { Form } from '@wix/headless-forms/react';
|
|
648
|
+
*
|
|
649
|
+
* <Form.Field id="email">
|
|
650
|
+
* <Form.Field.Label>
|
|
651
|
+
* <label className="text-foreground font-paragraph">Email Address</label>
|
|
652
|
+
* </Form.Field.Label>
|
|
653
|
+
* <Form.Field.InputWrapper>
|
|
654
|
+
* <Form.Field.Input>
|
|
655
|
+
* <input type="email" className="bg-background border-foreground text-foreground" />
|
|
656
|
+
* </Form.Field.Input>
|
|
657
|
+
* <Form.Field.Error>
|
|
658
|
+
* <span className="text-destructive text-sm font-paragraph">Please enter a valid email</span>
|
|
659
|
+
* </Form.Field.Error>
|
|
660
|
+
* </Form.Field.InputWrapper>
|
|
661
|
+
* </Form.Field>
|
|
662
|
+
* ```
|
|
663
|
+
*/
|
|
664
|
+
exports.FieldInputWrapper = react_1.default.forwardRef((props, ref) => {
|
|
665
|
+
const { children, asChild, className, ...otherProps } = props;
|
|
666
|
+
const { gridStyles } = (0, FieldContext_js_1.useFieldContext)();
|
|
667
|
+
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 }) }));
|
|
668
|
+
});
|
|
669
|
+
exports.FieldInputWrapper.displayName = 'Form.Field.InputWrapper';
|
|
670
|
+
/**
|
|
671
|
+
* Input component for a form field.
|
|
672
|
+
* Must be used within a Form.Field.InputWrapper component.
|
|
673
|
+
* Renders the actual input element without grid positioning.
|
|
674
|
+
*
|
|
675
|
+
* @component
|
|
676
|
+
* @example
|
|
677
|
+
* ```tsx
|
|
678
|
+
* import { Form } from '@wix/headless-forms/react';
|
|
679
|
+
*
|
|
680
|
+
* <Form.Field id="password">
|
|
681
|
+
* <Form.Field.Label>
|
|
682
|
+
* <label className="text-foreground font-paragraph">Password</label>
|
|
683
|
+
* </Form.Field.Label>
|
|
684
|
+
* <Form.Field.InputWrapper>
|
|
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.InputWrapper>
|
|
689
|
+
* </Form.Field>
|
|
690
|
+
* ```
|
|
691
|
+
*/
|
|
692
|
+
exports.FieldInput = react_1.default.forwardRef((props, ref) => {
|
|
693
|
+
const { children, description, asChild, className, ...otherProps } = props;
|
|
694
|
+
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 }) }));
|
|
695
|
+
});
|
|
696
|
+
exports.FieldInput.displayName = 'Form.Field.Input';
|
|
697
|
+
/**
|
|
698
|
+
* Error component for displaying field-level validation errors.
|
|
699
|
+
* Must be used within a Form.Field.InputWrapper component.
|
|
700
|
+
* Only renders when there is an error for the current field.
|
|
701
|
+
*
|
|
702
|
+
* @component
|
|
703
|
+
* @example
|
|
704
|
+
* ```tsx
|
|
705
|
+
* import { Form } from '@wix/headless-forms/react';
|
|
706
|
+
*
|
|
707
|
+
* <Form.Field id="email">
|
|
708
|
+
* <Form.Field.Label>
|
|
709
|
+
* <label className="text-foreground font-paragraph">Email Address</label>
|
|
710
|
+
* </Form.Field.Label>
|
|
711
|
+
* <Form.Field.InputWrapper>
|
|
712
|
+
* <Form.Field.Input>
|
|
713
|
+
* <input type="email" className="bg-background border-foreground text-foreground" />
|
|
714
|
+
* </Form.Field.Input>
|
|
715
|
+
* <Form.Field.Error path="email">
|
|
716
|
+
* <span className="text-destructive text-sm font-paragraph">Please enter a valid email address</span>
|
|
717
|
+
* </Form.Field.Error>
|
|
718
|
+
* </Form.Field.InputWrapper>
|
|
719
|
+
* </Form.Field>
|
|
720
|
+
* ```
|
|
721
|
+
*/
|
|
722
|
+
exports.FieldError = react_1.default.forwardRef((props, ref) => {
|
|
723
|
+
const { errorMessage, asChild, className, children, ...otherProps } = props;
|
|
724
|
+
if (!errorMessage && !children)
|
|
725
|
+
return null;
|
|
726
|
+
return ((0, jsx_runtime_1.jsx)(react_2.AsChildSlot, { "data-testid": TestIds.fieldError, ref: ref, asChild: asChild, className: className, ...otherProps, children: children || errorMessage }));
|
|
727
|
+
});
|
|
728
|
+
exports.FieldError.displayName = 'Form.Field.Error';
|
|
729
|
+
exports.Field = FieldRoot;
|
|
730
|
+
exports.Field.Label = exports.FieldLabel;
|
|
731
|
+
exports.Field.InputWrapper = exports.FieldInputWrapper;
|
|
732
|
+
exports.Field.Input = exports.FieldInput;
|
|
733
|
+
exports.Field.Error = exports.FieldError;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type Layout } from '../core/Form.js';
|
|
3
|
+
export interface FieldContextValue {
|
|
4
|
+
id: string;
|
|
5
|
+
layout: Layout;
|
|
6
|
+
gridStyles: {
|
|
7
|
+
label: React.CSSProperties;
|
|
8
|
+
input: React.CSSProperties;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export declare const FieldContext: React.Context<FieldContextValue | null>;
|
|
12
|
+
export declare function useFieldContext(): FieldContextValue;
|