@wix/headless-forms 0.0.11 → 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 +100 -8
- package/cjs/dist/react/Form.js +98 -58
- 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/dist/react/Form.d.ts +100 -8
- package/dist/react/Form.js +93 -53
- 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/package.json +2 -2
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
|
/**
|
|
@@ -621,7 +622,9 @@ export interface FieldProps {
|
|
|
621
622
|
*/
|
|
622
623
|
interface FieldComponent extends React.ForwardRefExoticComponent<FieldProps & React.RefAttributes<HTMLDivElement>> {
|
|
623
624
|
Label: typeof FieldLabel;
|
|
625
|
+
InputWrapper: typeof FieldInputWrapper;
|
|
624
626
|
Input: typeof FieldInput;
|
|
627
|
+
Error: typeof FieldError;
|
|
625
628
|
}
|
|
626
629
|
/**
|
|
627
630
|
* Props for Field.Label component
|
|
@@ -634,6 +637,17 @@ export interface FieldLabelProps {
|
|
|
634
637
|
/** CSS classes to apply to the label element */
|
|
635
638
|
className?: string;
|
|
636
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
|
+
}
|
|
637
651
|
/**
|
|
638
652
|
* Props for Field.Input component
|
|
639
653
|
*/
|
|
@@ -647,6 +661,28 @@ export interface FieldInputProps {
|
|
|
647
661
|
/** Description text to display below the input */
|
|
648
662
|
description?: React.ReactNode;
|
|
649
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
|
+
}
|
|
650
686
|
/**
|
|
651
687
|
* Label component for a form field with automatic grid positioning.
|
|
652
688
|
* Must be used within a Form.Field component.
|
|
@@ -661,17 +697,45 @@ export interface FieldInputProps {
|
|
|
661
697
|
* <Form.Field.Label>
|
|
662
698
|
* <label className="text-foreground font-paragraph">Email Address</label>
|
|
663
699
|
* </Form.Field.Label>
|
|
664
|
-
* <Form.Field.
|
|
665
|
-
* <
|
|
666
|
-
*
|
|
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>
|
|
667
705
|
* </Form.Field>
|
|
668
706
|
* ```
|
|
669
707
|
*/
|
|
670
708
|
export declare const FieldLabel: React.ForwardRefExoticComponent<FieldLabelProps & React.RefAttributes<HTMLDivElement>>;
|
|
671
709
|
/**
|
|
672
|
-
*
|
|
710
|
+
* InputWrapper component that wraps input and error elements with grid positioning.
|
|
673
711
|
* Must be used within a Form.Field component.
|
|
674
|
-
*
|
|
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.
|
|
675
739
|
*
|
|
676
740
|
* @component
|
|
677
741
|
* @example
|
|
@@ -682,12 +746,40 @@ export declare const FieldLabel: React.ForwardRefExoticComponent<FieldLabelProps
|
|
|
682
746
|
* <Form.Field.Label>
|
|
683
747
|
* <label className="text-foreground font-paragraph">Password</label>
|
|
684
748
|
* </Form.Field.Label>
|
|
685
|
-
* <Form.Field.
|
|
686
|
-
* <
|
|
687
|
-
*
|
|
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>
|
|
688
754
|
* </Form.Field>
|
|
689
755
|
* ```
|
|
690
756
|
*/
|
|
691
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>>;
|
|
692
784
|
export declare const Field: FieldComponent;
|
|
693
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.Field = exports.FieldInput = exports.FieldLabel = 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";
|
|
@@ -49,7 +51,9 @@ var TestIds;
|
|
|
49
51
|
TestIds["formSubmitted"] = "form-submitted";
|
|
50
52
|
TestIds["fieldRoot"] = "field-root";
|
|
51
53
|
TestIds["fieldLabel"] = "field-label";
|
|
54
|
+
TestIds["fieldInputWrapper"] = "field-input-wrapper";
|
|
52
55
|
TestIds["fieldInput"] = "field-input";
|
|
56
|
+
TestIds["fieldError"] = "field-error";
|
|
53
57
|
})(TestIds || (TestIds = {}));
|
|
54
58
|
/**
|
|
55
59
|
* Root component that provides all necessary service contexts for a complete form experience.
|
|
@@ -530,28 +534,27 @@ exports.Fields = react_1.default.forwardRef((props, ref) => {
|
|
|
530
534
|
return ((0, jsx_runtime_1.jsx)(Form_js_1.Fields, { children: ({ form, submitForm }) => {
|
|
531
535
|
if (!form)
|
|
532
536
|
return null;
|
|
533
|
-
return ((0, jsx_runtime_1.jsx)("div", { ref: ref, children: (0, jsx_runtime_1.jsx)(form_public_1.FormProvider, { 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 }) }) }));
|
|
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 }) }) }));
|
|
534
538
|
} }));
|
|
535
539
|
});
|
|
536
540
|
const FieldsWithForm = ({ form, submitForm, values, onChange, errors, onValidate, fields: fieldMap, rowGapClassname, columnGapClassname, }) => {
|
|
537
541
|
const formData = (0, form_public_1.useForm)({
|
|
538
542
|
form,
|
|
539
543
|
values,
|
|
540
|
-
onChange,
|
|
541
544
|
errors,
|
|
545
|
+
onChange,
|
|
542
546
|
onValidate,
|
|
543
547
|
submitForm,
|
|
544
548
|
fieldMap,
|
|
545
549
|
});
|
|
546
550
|
if (!formData)
|
|
547
551
|
return null;
|
|
548
|
-
console.log('formData', formData);
|
|
549
552
|
const { columnCount, fieldElements, fieldsLayout } = formData;
|
|
550
553
|
return (
|
|
551
554
|
// TODO: use readOnly, isDisabled
|
|
552
555
|
// TODO: step title a11y support
|
|
553
556
|
// TODO: mobile support?
|
|
554
|
-
(0, jsx_runtime_1.jsx)(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) => {
|
|
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) => {
|
|
555
558
|
return ((0, jsx_runtime_1.jsx)("div", { style: {
|
|
556
559
|
display: 'grid',
|
|
557
560
|
width: '100%',
|
|
@@ -560,45 +563,9 @@ const FieldsWithForm = ({ form, submitForm, values, onChange, errors, onValidate
|
|
|
560
563
|
}, className: columnGapClassname, children: rowElements }, index));
|
|
561
564
|
}) }) }) }));
|
|
562
565
|
};
|
|
563
|
-
/**
|
|
564
|
-
* Context for sharing field layout data across the form
|
|
565
|
-
* @internal
|
|
566
|
-
*/
|
|
567
|
-
const FieldLayoutContext = react_1.default.createContext(null);
|
|
568
|
-
/**
|
|
569
|
-
* Provider component that makes field layout data available to child components
|
|
570
|
-
* @internal
|
|
571
|
-
*/
|
|
572
|
-
const FieldLayoutProvider = ({ value, children, }) => {
|
|
573
|
-
return ((0, jsx_runtime_1.jsx)(FieldLayoutContext.Provider, { value: value, children: children }));
|
|
574
|
-
};
|
|
575
|
-
/**
|
|
576
|
-
* Hook to access layout configuration for a specific field
|
|
577
|
-
* @internal
|
|
578
|
-
* @param {string} fieldId - The unique identifier of the field
|
|
579
|
-
* @returns {Layout | null} The layout configuration for the field, or null if not found
|
|
580
|
-
*/
|
|
581
|
-
function useFieldLayout(fieldId) {
|
|
582
|
-
const layoutMap = react_1.default.useContext(FieldLayoutContext);
|
|
583
|
-
if (!layoutMap) {
|
|
584
|
-
return null;
|
|
585
|
-
}
|
|
586
|
-
return layoutMap[fieldId] || null;
|
|
587
|
-
}
|
|
588
|
-
const FieldContext = react_1.default.createContext(null);
|
|
589
|
-
/**
|
|
590
|
-
* Hook to access field context
|
|
591
|
-
*/
|
|
592
|
-
function useFieldContext() {
|
|
593
|
-
const context = react_1.default.useContext(FieldContext);
|
|
594
|
-
if (!context) {
|
|
595
|
-
throw new globalThis.Error('Field components must be used within a Form.Field component');
|
|
596
|
-
}
|
|
597
|
-
return context;
|
|
598
|
-
}
|
|
599
566
|
/**
|
|
600
567
|
* Container component for a form field with grid layout support.
|
|
601
|
-
* Provides context to Field.Label
|
|
568
|
+
* Provides context to Field.Label, Field.InputWrapper, Field.Input, and Field.Error child components.
|
|
602
569
|
* Based on the default-field-layout functionality.
|
|
603
570
|
*
|
|
604
571
|
* @component
|
|
@@ -612,9 +579,14 @@ function useFieldContext() {
|
|
|
612
579
|
* <Form.Field.Label>
|
|
613
580
|
* <label className="text-foreground font-paragraph">Username</label>
|
|
614
581
|
* </Form.Field.Label>
|
|
615
|
-
* <Form.Field.
|
|
616
|
-
* <
|
|
617
|
-
*
|
|
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>
|
|
618
590
|
* </Form.Field>
|
|
619
591
|
* );
|
|
620
592
|
* }
|
|
@@ -622,7 +594,7 @@ function useFieldContext() {
|
|
|
622
594
|
*/
|
|
623
595
|
const FieldRoot = react_1.default.forwardRef((props, ref) => {
|
|
624
596
|
const { id, children, asChild, className, ...otherProps } = props;
|
|
625
|
-
const layout = useFieldLayout(id);
|
|
597
|
+
const layout = (0, FieldLayoutContext_js_1.useFieldLayout)(id);
|
|
626
598
|
if (!layout) {
|
|
627
599
|
return null;
|
|
628
600
|
}
|
|
@@ -632,7 +604,7 @@ const FieldRoot = react_1.default.forwardRef((props, ref) => {
|
|
|
632
604
|
layout: fieldData.layout,
|
|
633
605
|
gridStyles: fieldData.gridStyles,
|
|
634
606
|
};
|
|
635
|
-
return ((0, jsx_runtime_1.jsx)(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 }) }));
|
|
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 }) }));
|
|
636
608
|
} }));
|
|
637
609
|
});
|
|
638
610
|
FieldRoot.displayName = 'Form.Field';
|
|
@@ -650,22 +622,55 @@ FieldRoot.displayName = 'Form.Field';
|
|
|
650
622
|
* <Form.Field.Label>
|
|
651
623
|
* <label className="text-foreground font-paragraph">Email Address</label>
|
|
652
624
|
* </Form.Field.Label>
|
|
653
|
-
* <Form.Field.
|
|
654
|
-
* <
|
|
655
|
-
*
|
|
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>
|
|
656
630
|
* </Form.Field>
|
|
657
631
|
* ```
|
|
658
632
|
*/
|
|
659
633
|
exports.FieldLabel = react_1.default.forwardRef((props, ref) => {
|
|
660
634
|
const { children, asChild, className, ...otherProps } = props;
|
|
661
|
-
const { gridStyles } = useFieldContext();
|
|
635
|
+
const { gridStyles } = (0, FieldContext_js_1.useFieldContext)();
|
|
662
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 }) }));
|
|
663
637
|
});
|
|
664
638
|
exports.FieldLabel.displayName = 'Form.Field.Label';
|
|
665
639
|
/**
|
|
666
|
-
*
|
|
640
|
+
* InputWrapper component that wraps input and error elements with grid positioning.
|
|
667
641
|
* Must be used within a Form.Field component.
|
|
668
|
-
*
|
|
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.
|
|
669
674
|
*
|
|
670
675
|
* @component
|
|
671
676
|
* @example
|
|
@@ -676,18 +681,53 @@ exports.FieldLabel.displayName = 'Form.Field.Label';
|
|
|
676
681
|
* <Form.Field.Label>
|
|
677
682
|
* <label className="text-foreground font-paragraph">Password</label>
|
|
678
683
|
* </Form.Field.Label>
|
|
679
|
-
* <Form.Field.
|
|
680
|
-
* <
|
|
681
|
-
*
|
|
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>
|
|
682
689
|
* </Form.Field>
|
|
683
690
|
* ```
|
|
684
691
|
*/
|
|
685
692
|
exports.FieldInput = react_1.default.forwardRef((props, ref) => {
|
|
686
693
|
const { children, description, asChild, className, ...otherProps } = props;
|
|
687
|
-
|
|
688
|
-
return ((0, jsx_runtime_1.jsx)(react_2.AsChildSlot, { ref: ref, asChild: asChild, className: className, style: gridStyles.input, "data-testid": TestIds.fieldInput, customElement: children, customElementProps: {}, ...otherProps, children: (0, jsx_runtime_1.jsx)("div", { children: children }) }));
|
|
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 }) }));
|
|
689
695
|
});
|
|
690
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';
|
|
691
729
|
exports.Field = FieldRoot;
|
|
692
730
|
exports.Field.Label = exports.FieldLabel;
|
|
731
|
+
exports.Field.InputWrapper = exports.FieldInputWrapper;
|
|
693
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;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FieldContext = void 0;
|
|
7
|
+
exports.useFieldContext = useFieldContext;
|
|
8
|
+
const react_1 = __importDefault(require("react"));
|
|
9
|
+
exports.FieldContext = react_1.default.createContext(null);
|
|
10
|
+
function useFieldContext() {
|
|
11
|
+
const context = react_1.default.useContext(exports.FieldContext);
|
|
12
|
+
if (!context) {
|
|
13
|
+
throw new Error('Field components must be used within a Form.Field component');
|
|
14
|
+
}
|
|
15
|
+
return context;
|
|
16
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type Layout } from '../core/Form.js';
|
|
3
|
+
export interface FieldLayoutMap {
|
|
4
|
+
[fieldId: string]: Layout;
|
|
5
|
+
}
|
|
6
|
+
export declare const FieldLayoutContext: React.Context<FieldLayoutMap | null>;
|
|
7
|
+
export interface FieldLayoutProviderProps {
|
|
8
|
+
value: FieldLayoutMap;
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
}
|
|
11
|
+
export declare const FieldLayoutProvider: React.FC<FieldLayoutProviderProps>;
|
|
12
|
+
export declare function useFieldLayout(fieldId: string): Layout | null;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FieldLayoutProvider = exports.FieldLayoutContext = void 0;
|
|
7
|
+
exports.useFieldLayout = useFieldLayout;
|
|
8
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
9
|
+
const react_1 = __importDefault(require("react"));
|
|
10
|
+
exports.FieldLayoutContext = react_1.default.createContext(null);
|
|
11
|
+
const FieldLayoutProvider = ({ value, children, }) => {
|
|
12
|
+
return ((0, jsx_runtime_1.jsx)(exports.FieldLayoutContext.Provider, { value: value, children: children }));
|
|
13
|
+
};
|
|
14
|
+
exports.FieldLayoutProvider = FieldLayoutProvider;
|
|
15
|
+
function useFieldLayout(fieldId) {
|
|
16
|
+
const layoutMap = react_1.default.useContext(exports.FieldLayoutContext);
|
|
17
|
+
if (!layoutMap) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
return layoutMap[fieldId] || null;
|
|
21
|
+
}
|
package/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
|
/**
|
|
@@ -621,7 +622,9 @@ export interface FieldProps {
|
|
|
621
622
|
*/
|
|
622
623
|
interface FieldComponent extends React.ForwardRefExoticComponent<FieldProps & React.RefAttributes<HTMLDivElement>> {
|
|
623
624
|
Label: typeof FieldLabel;
|
|
625
|
+
InputWrapper: typeof FieldInputWrapper;
|
|
624
626
|
Input: typeof FieldInput;
|
|
627
|
+
Error: typeof FieldError;
|
|
625
628
|
}
|
|
626
629
|
/**
|
|
627
630
|
* Props for Field.Label component
|
|
@@ -634,6 +637,17 @@ export interface FieldLabelProps {
|
|
|
634
637
|
/** CSS classes to apply to the label element */
|
|
635
638
|
className?: string;
|
|
636
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
|
+
}
|
|
637
651
|
/**
|
|
638
652
|
* Props for Field.Input component
|
|
639
653
|
*/
|
|
@@ -647,6 +661,28 @@ export interface FieldInputProps {
|
|
|
647
661
|
/** Description text to display below the input */
|
|
648
662
|
description?: React.ReactNode;
|
|
649
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
|
+
}
|
|
650
686
|
/**
|
|
651
687
|
* Label component for a form field with automatic grid positioning.
|
|
652
688
|
* Must be used within a Form.Field component.
|
|
@@ -661,17 +697,45 @@ export interface FieldInputProps {
|
|
|
661
697
|
* <Form.Field.Label>
|
|
662
698
|
* <label className="text-foreground font-paragraph">Email Address</label>
|
|
663
699
|
* </Form.Field.Label>
|
|
664
|
-
* <Form.Field.
|
|
665
|
-
* <
|
|
666
|
-
*
|
|
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>
|
|
667
705
|
* </Form.Field>
|
|
668
706
|
* ```
|
|
669
707
|
*/
|
|
670
708
|
export declare const FieldLabel: React.ForwardRefExoticComponent<FieldLabelProps & React.RefAttributes<HTMLDivElement>>;
|
|
671
709
|
/**
|
|
672
|
-
*
|
|
710
|
+
* InputWrapper component that wraps input and error elements with grid positioning.
|
|
673
711
|
* Must be used within a Form.Field component.
|
|
674
|
-
*
|
|
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.
|
|
675
739
|
*
|
|
676
740
|
* @component
|
|
677
741
|
* @example
|
|
@@ -682,12 +746,40 @@ export declare const FieldLabel: React.ForwardRefExoticComponent<FieldLabelProps
|
|
|
682
746
|
* <Form.Field.Label>
|
|
683
747
|
* <label className="text-foreground font-paragraph">Password</label>
|
|
684
748
|
* </Form.Field.Label>
|
|
685
|
-
* <Form.Field.
|
|
686
|
-
* <
|
|
687
|
-
*
|
|
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>
|
|
688
754
|
* </Form.Field>
|
|
689
755
|
* ```
|
|
690
756
|
*/
|
|
691
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>>;
|
|
692
784
|
export declare const Field: FieldComponent;
|
|
693
785
|
export {};
|
package/dist/react/Form.js
CHANGED
|
@@ -3,6 +3,8 @@ import React, { useState, useCallback } from 'react';
|
|
|
3
3
|
import { AsChildSlot } from '@wix/headless-utils/react';
|
|
4
4
|
import { useForm, FormProvider, } from '@wix/form-public';
|
|
5
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
|
+
import { FieldContext, useFieldContext, } from './context/FieldContext.js';
|
|
7
|
+
import { FieldLayoutProvider, useFieldLayout, } from './context/FieldLayoutContext.js';
|
|
6
8
|
var TestIds;
|
|
7
9
|
(function (TestIds) {
|
|
8
10
|
TestIds["formRoot"] = "form-root";
|
|
@@ -13,7 +15,9 @@ var TestIds;
|
|
|
13
15
|
TestIds["formSubmitted"] = "form-submitted";
|
|
14
16
|
TestIds["fieldRoot"] = "field-root";
|
|
15
17
|
TestIds["fieldLabel"] = "field-label";
|
|
18
|
+
TestIds["fieldInputWrapper"] = "field-input-wrapper";
|
|
16
19
|
TestIds["fieldInput"] = "field-input";
|
|
20
|
+
TestIds["fieldError"] = "field-error";
|
|
17
21
|
})(TestIds || (TestIds = {}));
|
|
18
22
|
/**
|
|
19
23
|
* Root component that provides all necessary service contexts for a complete form experience.
|
|
@@ -494,22 +498,21 @@ export const Fields = React.forwardRef((props, ref) => {
|
|
|
494
498
|
return (_jsx(CoreFields, { children: ({ form, submitForm }) => {
|
|
495
499
|
if (!form)
|
|
496
500
|
return null;
|
|
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 }) }) }));
|
|
501
|
+
return (_jsx("div", { ref: ref, children: _jsx(FormProvider, { currency: 'USD', locale: 'en', 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
502
|
} }));
|
|
499
503
|
});
|
|
500
504
|
const FieldsWithForm = ({ form, submitForm, values, onChange, errors, onValidate, fields: fieldMap, rowGapClassname, columnGapClassname, }) => {
|
|
501
505
|
const formData = useForm({
|
|
502
506
|
form,
|
|
503
507
|
values,
|
|
504
|
-
onChange,
|
|
505
508
|
errors,
|
|
509
|
+
onChange,
|
|
506
510
|
onValidate,
|
|
507
511
|
submitForm,
|
|
508
512
|
fieldMap,
|
|
509
513
|
});
|
|
510
514
|
if (!formData)
|
|
511
515
|
return null;
|
|
512
|
-
console.log('formData', formData);
|
|
513
516
|
const { columnCount, fieldElements, fieldsLayout } = formData;
|
|
514
517
|
return (
|
|
515
518
|
// TODO: use readOnly, isDisabled
|
|
@@ -524,45 +527,9 @@ const FieldsWithForm = ({ form, submitForm, values, onChange, errors, onValidate
|
|
|
524
527
|
}, className: columnGapClassname, children: rowElements }, index));
|
|
525
528
|
}) }) }) }));
|
|
526
529
|
};
|
|
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
530
|
/**
|
|
564
531
|
* Container component for a form field with grid layout support.
|
|
565
|
-
* Provides context to Field.Label
|
|
532
|
+
* Provides context to Field.Label, Field.InputWrapper, Field.Input, and Field.Error child components.
|
|
566
533
|
* Based on the default-field-layout functionality.
|
|
567
534
|
*
|
|
568
535
|
* @component
|
|
@@ -576,9 +543,14 @@ function useFieldContext() {
|
|
|
576
543
|
* <Form.Field.Label>
|
|
577
544
|
* <label className="text-foreground font-paragraph">Username</label>
|
|
578
545
|
* </Form.Field.Label>
|
|
579
|
-
* <Form.Field.
|
|
580
|
-
* <
|
|
581
|
-
*
|
|
546
|
+
* <Form.Field.InputWrapper>
|
|
547
|
+
* <Form.Field.Input description={<span className="text-secondary-foreground">Required</span>}>
|
|
548
|
+
* <input className="bg-background border-foreground text-foreground" />
|
|
549
|
+
* </Form.Field.Input>
|
|
550
|
+
* <Form.Field.Error>
|
|
551
|
+
* <span className="text-destructive text-sm font-paragraph">Username is required</span>
|
|
552
|
+
* </Form.Field.Error>
|
|
553
|
+
* </Form.Field.InputWrapper>
|
|
582
554
|
* </Form.Field>
|
|
583
555
|
* );
|
|
584
556
|
* }
|
|
@@ -614,9 +586,11 @@ FieldRoot.displayName = 'Form.Field';
|
|
|
614
586
|
* <Form.Field.Label>
|
|
615
587
|
* <label className="text-foreground font-paragraph">Email Address</label>
|
|
616
588
|
* </Form.Field.Label>
|
|
617
|
-
* <Form.Field.
|
|
618
|
-
* <
|
|
619
|
-
*
|
|
589
|
+
* <Form.Field.InputWrapper>
|
|
590
|
+
* <Form.Field.Input>
|
|
591
|
+
* <input type="email" className="bg-background border-foreground text-foreground" />
|
|
592
|
+
* </Form.Field.Input>
|
|
593
|
+
* </Form.Field.InputWrapper>
|
|
620
594
|
* </Form.Field>
|
|
621
595
|
* ```
|
|
622
596
|
*/
|
|
@@ -627,9 +601,40 @@ export const FieldLabel = React.forwardRef((props, ref) => {
|
|
|
627
601
|
});
|
|
628
602
|
FieldLabel.displayName = 'Form.Field.Label';
|
|
629
603
|
/**
|
|
630
|
-
*
|
|
604
|
+
* InputWrapper component that wraps input and error elements with grid positioning.
|
|
631
605
|
* Must be used within a Form.Field component.
|
|
632
|
-
*
|
|
606
|
+
* This wrapper applies the grid positioning styles to contain both the input and error.
|
|
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.InputWrapper>
|
|
618
|
+
* <Form.Field.Input>
|
|
619
|
+
* <input type="email" className="bg-background border-foreground text-foreground" />
|
|
620
|
+
* </Form.Field.Input>
|
|
621
|
+
* <Form.Field.Error>
|
|
622
|
+
* <span className="text-destructive text-sm font-paragraph">Please enter a valid email</span>
|
|
623
|
+
* </Form.Field.Error>
|
|
624
|
+
* </Form.Field.InputWrapper>
|
|
625
|
+
* </Form.Field>
|
|
626
|
+
* ```
|
|
627
|
+
*/
|
|
628
|
+
export const FieldInputWrapper = React.forwardRef((props, ref) => {
|
|
629
|
+
const { children, asChild, className, ...otherProps } = props;
|
|
630
|
+
const { gridStyles } = useFieldContext();
|
|
631
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, style: gridStyles.input, "data-testid": TestIds.fieldInputWrapper, customElement: children, customElementProps: {}, ...otherProps, children: _jsx("div", { children: children }) }));
|
|
632
|
+
});
|
|
633
|
+
FieldInputWrapper.displayName = 'Form.Field.InputWrapper';
|
|
634
|
+
/**
|
|
635
|
+
* Input component for a form field.
|
|
636
|
+
* Must be used within a Form.Field.InputWrapper component.
|
|
637
|
+
* Renders the actual input element without grid positioning.
|
|
633
638
|
*
|
|
634
639
|
* @component
|
|
635
640
|
* @example
|
|
@@ -640,18 +645,53 @@ FieldLabel.displayName = 'Form.Field.Label';
|
|
|
640
645
|
* <Form.Field.Label>
|
|
641
646
|
* <label className="text-foreground font-paragraph">Password</label>
|
|
642
647
|
* </Form.Field.Label>
|
|
643
|
-
* <Form.Field.
|
|
644
|
-
* <
|
|
645
|
-
*
|
|
648
|
+
* <Form.Field.InputWrapper>
|
|
649
|
+
* <Form.Field.Input description={<span className="text-secondary-foreground">Min 8 characters</span>}>
|
|
650
|
+
* <input type="password" className="bg-background border-foreground text-foreground" />
|
|
651
|
+
* </Form.Field.Input>
|
|
652
|
+
* </Form.Field.InputWrapper>
|
|
646
653
|
* </Form.Field>
|
|
647
654
|
* ```
|
|
648
655
|
*/
|
|
649
656
|
export const FieldInput = React.forwardRef((props, ref) => {
|
|
650
657
|
const { children, description, asChild, className, ...otherProps } = props;
|
|
651
|
-
|
|
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 }) }));
|
|
658
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.fieldInput, customElement: children, customElementProps: {}, ...otherProps, children: _jsx("div", { children: children }) }));
|
|
653
659
|
});
|
|
654
660
|
FieldInput.displayName = 'Form.Field.Input';
|
|
661
|
+
/**
|
|
662
|
+
* Error component for displaying field-level validation errors.
|
|
663
|
+
* Must be used within a Form.Field.InputWrapper component.
|
|
664
|
+
* Only renders when there is an error for the current field.
|
|
665
|
+
*
|
|
666
|
+
* @component
|
|
667
|
+
* @example
|
|
668
|
+
* ```tsx
|
|
669
|
+
* import { Form } from '@wix/headless-forms/react';
|
|
670
|
+
*
|
|
671
|
+
* <Form.Field id="email">
|
|
672
|
+
* <Form.Field.Label>
|
|
673
|
+
* <label className="text-foreground font-paragraph">Email Address</label>
|
|
674
|
+
* </Form.Field.Label>
|
|
675
|
+
* <Form.Field.InputWrapper>
|
|
676
|
+
* <Form.Field.Input>
|
|
677
|
+
* <input type="email" className="bg-background border-foreground text-foreground" />
|
|
678
|
+
* </Form.Field.Input>
|
|
679
|
+
* <Form.Field.Error path="email">
|
|
680
|
+
* <span className="text-destructive text-sm font-paragraph">Please enter a valid email address</span>
|
|
681
|
+
* </Form.Field.Error>
|
|
682
|
+
* </Form.Field.InputWrapper>
|
|
683
|
+
* </Form.Field>
|
|
684
|
+
* ```
|
|
685
|
+
*/
|
|
686
|
+
export const FieldError = React.forwardRef((props, ref) => {
|
|
687
|
+
const { errorMessage, asChild, className, children, ...otherProps } = props;
|
|
688
|
+
if (!errorMessage && !children)
|
|
689
|
+
return null;
|
|
690
|
+
return (_jsx(AsChildSlot, { "data-testid": TestIds.fieldError, ref: ref, asChild: asChild, className: className, ...otherProps, children: children || errorMessage }));
|
|
691
|
+
});
|
|
692
|
+
FieldError.displayName = 'Form.Field.Error';
|
|
655
693
|
export const Field = FieldRoot;
|
|
656
694
|
Field.Label = FieldLabel;
|
|
695
|
+
Field.InputWrapper = FieldInputWrapper;
|
|
657
696
|
Field.Input = FieldInput;
|
|
697
|
+
Field.Error = 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;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export const FieldContext = React.createContext(null);
|
|
3
|
+
export function useFieldContext() {
|
|
4
|
+
const context = React.useContext(FieldContext);
|
|
5
|
+
if (!context) {
|
|
6
|
+
throw new Error('Field components must be used within a Form.Field component');
|
|
7
|
+
}
|
|
8
|
+
return context;
|
|
9
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type Layout } from '../core/Form.js';
|
|
3
|
+
export interface FieldLayoutMap {
|
|
4
|
+
[fieldId: string]: Layout;
|
|
5
|
+
}
|
|
6
|
+
export declare const FieldLayoutContext: React.Context<FieldLayoutMap | null>;
|
|
7
|
+
export interface FieldLayoutProviderProps {
|
|
8
|
+
value: FieldLayoutMap;
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
}
|
|
11
|
+
export declare const FieldLayoutProvider: React.FC<FieldLayoutProviderProps>;
|
|
12
|
+
export declare function useFieldLayout(fieldId: string): Layout | null;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export const FieldLayoutContext = React.createContext(null);
|
|
4
|
+
export const FieldLayoutProvider = ({ value, children, }) => {
|
|
5
|
+
return (_jsx(FieldLayoutContext.Provider, { value: value, children: children }));
|
|
6
|
+
};
|
|
7
|
+
export function useFieldLayout(fieldId) {
|
|
8
|
+
const layoutMap = React.useContext(FieldLayoutContext);
|
|
9
|
+
if (!layoutMap) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
return layoutMap[fieldId] || null;
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/headless-forms",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "npm run build:esm && npm run build:cjs",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"vitest": "^3.1.4"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@wix/form-public": "^0.
|
|
44
|
+
"@wix/form-public": "^0.53.0",
|
|
45
45
|
"@wix/forms": "^1.0.331",
|
|
46
46
|
"@wix/headless-utils": "0.0.4",
|
|
47
47
|
"@wix/services-definitions": "^0.1.4",
|