@wix/headless-forms 0.0.11 → 0.0.13
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 +121 -12
- package/cjs/dist/react/Form.js +144 -61
- package/cjs/dist/react/Phone.d.ts +47 -0
- package/cjs/dist/react/Phone.js +56 -0
- 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/index.d.ts +1 -0
- package/cjs/dist/react/index.js +1 -0
- package/dist/react/Form.d.ts +121 -12
- package/dist/react/Form.js +139 -56
- package/dist/react/Phone.d.ts +47 -0
- package/dist/react/Phone.js +50 -0
- 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/index.d.ts +1 -0
- package/dist/react/index.js +1 -0
- package/package.json +16 -4
package/cjs/dist/react/index.js
CHANGED
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,14 @@ export interface FieldLabelProps {
|
|
|
634
637
|
/** CSS classes to apply to the label element */
|
|
635
638
|
className?: string;
|
|
636
639
|
}
|
|
640
|
+
export interface FieldInputWrapperProps {
|
|
641
|
+
/** Child components (typically Field.Input and Field.Error) */
|
|
642
|
+
children: React.ReactNode;
|
|
643
|
+
/** Whether to render as a child component */
|
|
644
|
+
asChild?: boolean;
|
|
645
|
+
/** CSS classes to apply to the wrapper element */
|
|
646
|
+
className?: string;
|
|
647
|
+
}
|
|
637
648
|
/**
|
|
638
649
|
* Props for Field.Input component
|
|
639
650
|
*/
|
|
@@ -648,9 +659,74 @@ export interface FieldInputProps {
|
|
|
648
659
|
description?: React.ReactNode;
|
|
649
660
|
}
|
|
650
661
|
/**
|
|
651
|
-
*
|
|
662
|
+
* Render props for Field.Error component
|
|
663
|
+
*/
|
|
664
|
+
export interface FieldErrorRenderProps {
|
|
665
|
+
/** The error type */
|
|
666
|
+
type: FormError['errorType'];
|
|
667
|
+
/** The error message */
|
|
668
|
+
message: string;
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Props for Field.Error component
|
|
672
|
+
*/
|
|
673
|
+
export interface FieldErrorProps {
|
|
674
|
+
/** Whether to render as a child component */
|
|
675
|
+
asChild?: boolean;
|
|
676
|
+
/** CSS classes to apply to the error element */
|
|
677
|
+
className?: string;
|
|
678
|
+
/** The error message */
|
|
679
|
+
errorMessage?: string;
|
|
680
|
+
/** Child components to render */
|
|
681
|
+
children?: React.ReactNode;
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Props for Field.Label.Required component
|
|
685
|
+
*/
|
|
686
|
+
export interface FieldLabelRequiredProps {
|
|
687
|
+
/** Whether to show the required indicator */
|
|
688
|
+
required?: boolean;
|
|
689
|
+
/** Custom content to display (defaults to red asterisk) */
|
|
690
|
+
children?: React.ReactNode;
|
|
691
|
+
/** Whether to render as a child component */
|
|
692
|
+
asChild?: boolean;
|
|
693
|
+
/** CSS classes to apply to the required indicator */
|
|
694
|
+
className?: string;
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Required indicator component for form field labels.
|
|
698
|
+
* Must be used within a Form.Field.Label component.
|
|
699
|
+
*
|
|
700
|
+
* @component
|
|
701
|
+
* @example
|
|
702
|
+
* ```tsx
|
|
703
|
+
* import { Form } from '@wix/headless-forms/react';
|
|
704
|
+
*
|
|
705
|
+
* // Basic usage with required prop
|
|
706
|
+
* <Form.Field.Label>
|
|
707
|
+
* <label className="text-foreground font-paragraph">
|
|
708
|
+
* Email Address
|
|
709
|
+
* <Form.Field.Label.Required />
|
|
710
|
+
* </label>
|
|
711
|
+
* </Form.Field.Label>
|
|
712
|
+
*
|
|
713
|
+
* // Custom styling
|
|
714
|
+
* <Form.Field.Label>
|
|
715
|
+
* <label className="text-foreground font-paragraph">
|
|
716
|
+
* Username
|
|
717
|
+
* <Form.Field.Label.Required required={true} className="text-destructive ml-2" />
|
|
718
|
+
* </label>
|
|
719
|
+
* </Form.Field.Label>
|
|
720
|
+
*/
|
|
721
|
+
export declare const FieldLabelRequired: React.ForwardRefExoticComponent<FieldLabelRequiredProps & React.RefAttributes<HTMLSpanElement>>;
|
|
722
|
+
interface FieldLabelComponent extends React.ForwardRefExoticComponent<FieldLabelProps & React.RefAttributes<HTMLDivElement>> {
|
|
723
|
+
Required: typeof FieldLabelRequired;
|
|
724
|
+
}
|
|
725
|
+
export declare const FieldLabel: FieldLabelComponent;
|
|
726
|
+
/**
|
|
727
|
+
* InputWrapper component that wraps input and error elements with grid positioning.
|
|
652
728
|
* Must be used within a Form.Field component.
|
|
653
|
-
*
|
|
729
|
+
* This wrapper applies the grid positioning styles to contain both the input and error.
|
|
654
730
|
*
|
|
655
731
|
* @component
|
|
656
732
|
* @example
|
|
@@ -661,17 +737,22 @@ export interface FieldInputProps {
|
|
|
661
737
|
* <Form.Field.Label>
|
|
662
738
|
* <label className="text-foreground font-paragraph">Email Address</label>
|
|
663
739
|
* </Form.Field.Label>
|
|
664
|
-
* <Form.Field.
|
|
665
|
-
* <
|
|
666
|
-
*
|
|
740
|
+
* <Form.Field.InputWrapper>
|
|
741
|
+
* <Form.Field.Input>
|
|
742
|
+
* <input type="email" className="bg-background border-foreground text-foreground" />
|
|
743
|
+
* </Form.Field.Input>
|
|
744
|
+
* <Form.Field.Error>
|
|
745
|
+
* <span className="text-destructive text-sm font-paragraph">Please enter a valid email</span>
|
|
746
|
+
* </Form.Field.Error>
|
|
747
|
+
* </Form.Field.InputWrapper>
|
|
667
748
|
* </Form.Field>
|
|
668
749
|
* ```
|
|
669
750
|
*/
|
|
670
|
-
export declare const
|
|
751
|
+
export declare const FieldInputWrapper: React.ForwardRefExoticComponent<FieldInputWrapperProps & React.RefAttributes<HTMLDivElement>>;
|
|
671
752
|
/**
|
|
672
|
-
* Input component for a form field
|
|
673
|
-
* Must be used within a Form.Field component.
|
|
674
|
-
* Renders
|
|
753
|
+
* Input component for a form field.
|
|
754
|
+
* Must be used within a Form.Field.InputWrapper component.
|
|
755
|
+
* Renders the actual input element without grid positioning.
|
|
675
756
|
*
|
|
676
757
|
* @component
|
|
677
758
|
* @example
|
|
@@ -682,12 +763,40 @@ export declare const FieldLabel: React.ForwardRefExoticComponent<FieldLabelProps
|
|
|
682
763
|
* <Form.Field.Label>
|
|
683
764
|
* <label className="text-foreground font-paragraph">Password</label>
|
|
684
765
|
* </Form.Field.Label>
|
|
685
|
-
* <Form.Field.
|
|
686
|
-
* <
|
|
687
|
-
*
|
|
766
|
+
* <Form.Field.InputWrapper>
|
|
767
|
+
* <Form.Field.Input description={<span className="text-secondary-foreground">Min 8 characters</span>}>
|
|
768
|
+
* <input type="password" className="bg-background border-foreground text-foreground" />
|
|
769
|
+
* </Form.Field.Input>
|
|
770
|
+
* </Form.Field.InputWrapper>
|
|
688
771
|
* </Form.Field>
|
|
689
772
|
* ```
|
|
690
773
|
*/
|
|
691
774
|
export declare const FieldInput: React.ForwardRefExoticComponent<FieldInputProps & React.RefAttributes<HTMLDivElement>>;
|
|
775
|
+
/**
|
|
776
|
+
* Error component for displaying field-level validation errors.
|
|
777
|
+
* Must be used within a Form.Field.InputWrapper component.
|
|
778
|
+
* Only renders when there is an error for the current field.
|
|
779
|
+
*
|
|
780
|
+
* @component
|
|
781
|
+
* @example
|
|
782
|
+
* ```tsx
|
|
783
|
+
* import { Form } from '@wix/headless-forms/react';
|
|
784
|
+
*
|
|
785
|
+
* <Form.Field id="email">
|
|
786
|
+
* <Form.Field.Label>
|
|
787
|
+
* <label className="text-foreground font-paragraph">Email Address</label>
|
|
788
|
+
* </Form.Field.Label>
|
|
789
|
+
* <Form.Field.InputWrapper>
|
|
790
|
+
* <Form.Field.Input>
|
|
791
|
+
* <input type="email" className="bg-background border-foreground text-foreground" />
|
|
792
|
+
* </Form.Field.Input>
|
|
793
|
+
* <Form.Field.Error path="email">
|
|
794
|
+
* <span className="text-destructive text-sm font-paragraph">Please enter a valid email address</span>
|
|
795
|
+
* </Form.Field.Error>
|
|
796
|
+
* </Form.Field.InputWrapper>
|
|
797
|
+
* </Form.Field>
|
|
798
|
+
* ```
|
|
799
|
+
*/
|
|
800
|
+
export declare const FieldError: React.ForwardRefExoticComponent<FieldErrorProps & React.RefAttributes<HTMLDivElement>>;
|
|
692
801
|
export declare const Field: FieldComponent;
|
|
693
802
|
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
|
* }
|
|
@@ -612,24 +584,100 @@ FieldRoot.displayName = 'Form.Field';
|
|
|
612
584
|
*
|
|
613
585
|
* <Form.Field id="email">
|
|
614
586
|
* <Form.Field.Label>
|
|
615
|
-
* <label className="text-foreground font-paragraph">
|
|
587
|
+
* <label className="text-foreground font-paragraph">
|
|
588
|
+
* Email Address
|
|
589
|
+
* <Form.Field.Label.Required required={true} />
|
|
590
|
+
* </label>
|
|
616
591
|
* </Form.Field.Label>
|
|
617
|
-
* <Form.Field.
|
|
618
|
-
* <
|
|
619
|
-
*
|
|
592
|
+
* <Form.Field.InputWrapper>
|
|
593
|
+
* <Form.Field.Input>
|
|
594
|
+
* <input type="email" className="bg-background border-foreground text-foreground" />
|
|
595
|
+
* </Form.Field.Input>
|
|
596
|
+
* </Form.Field.InputWrapper>
|
|
620
597
|
* </Form.Field>
|
|
621
598
|
* ```
|
|
622
599
|
*/
|
|
623
|
-
|
|
600
|
+
const FieldLabelRoot = React.forwardRef((props, ref) => {
|
|
624
601
|
const { children, asChild, className, ...otherProps } = props;
|
|
625
602
|
const { gridStyles } = useFieldContext();
|
|
626
603
|
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
604
|
});
|
|
628
|
-
|
|
605
|
+
FieldLabelRoot.displayName = 'Form.Field.Label';
|
|
606
|
+
/**
|
|
607
|
+
* Required indicator component for form field labels.
|
|
608
|
+
* Must be used within a Form.Field.Label component.
|
|
609
|
+
*
|
|
610
|
+
* @component
|
|
611
|
+
* @example
|
|
612
|
+
* ```tsx
|
|
613
|
+
* import { Form } from '@wix/headless-forms/react';
|
|
614
|
+
*
|
|
615
|
+
* // Basic usage with required prop
|
|
616
|
+
* <Form.Field.Label>
|
|
617
|
+
* <label className="text-foreground font-paragraph">
|
|
618
|
+
* Email Address
|
|
619
|
+
* <Form.Field.Label.Required />
|
|
620
|
+
* </label>
|
|
621
|
+
* </Form.Field.Label>
|
|
622
|
+
*
|
|
623
|
+
* // Custom styling
|
|
624
|
+
* <Form.Field.Label>
|
|
625
|
+
* <label className="text-foreground font-paragraph">
|
|
626
|
+
* Username
|
|
627
|
+
* <Form.Field.Label.Required required={true} className="text-destructive ml-2" />
|
|
628
|
+
* </label>
|
|
629
|
+
* </Form.Field.Label>
|
|
630
|
+
*/
|
|
631
|
+
export const FieldLabelRequired = React.forwardRef((props, ref) => {
|
|
632
|
+
const { required = false, children, asChild, className, ...otherProps } = props;
|
|
633
|
+
const requiredIndicator = 'asterisk';
|
|
634
|
+
// @ts-expect-error
|
|
635
|
+
if (!required || requiredIndicator === 'none')
|
|
636
|
+
return null;
|
|
637
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, customElement: children, ...otherProps, children: _jsx("span", { children: requiredIndicator === 'asterisk'
|
|
638
|
+
? '*'
|
|
639
|
+
: requiredIndicator === 'text'
|
|
640
|
+
? '(Required)'
|
|
641
|
+
: null }) }));
|
|
642
|
+
});
|
|
643
|
+
FieldLabelRequired.displayName = 'Form.Field.Label.Required';
|
|
644
|
+
export const FieldLabel = FieldLabelRoot;
|
|
645
|
+
FieldLabel.Required = FieldLabelRequired;
|
|
629
646
|
/**
|
|
630
|
-
*
|
|
647
|
+
* InputWrapper component that wraps input and error elements with grid positioning.
|
|
631
648
|
* Must be used within a Form.Field component.
|
|
632
|
-
*
|
|
649
|
+
* This wrapper applies the grid positioning styles to contain both the input and error.
|
|
650
|
+
*
|
|
651
|
+
* @component
|
|
652
|
+
* @example
|
|
653
|
+
* ```tsx
|
|
654
|
+
* import { Form } from '@wix/headless-forms/react';
|
|
655
|
+
*
|
|
656
|
+
* <Form.Field id="email">
|
|
657
|
+
* <Form.Field.Label>
|
|
658
|
+
* <label className="text-foreground font-paragraph">Email Address</label>
|
|
659
|
+
* </Form.Field.Label>
|
|
660
|
+
* <Form.Field.InputWrapper>
|
|
661
|
+
* <Form.Field.Input>
|
|
662
|
+
* <input type="email" className="bg-background border-foreground text-foreground" />
|
|
663
|
+
* </Form.Field.Input>
|
|
664
|
+
* <Form.Field.Error>
|
|
665
|
+
* <span className="text-destructive text-sm font-paragraph">Please enter a valid email</span>
|
|
666
|
+
* </Form.Field.Error>
|
|
667
|
+
* </Form.Field.InputWrapper>
|
|
668
|
+
* </Form.Field>
|
|
669
|
+
* ```
|
|
670
|
+
*/
|
|
671
|
+
export const FieldInputWrapper = React.forwardRef((props, ref) => {
|
|
672
|
+
const { children, asChild, className, ...otherProps } = props;
|
|
673
|
+
const { gridStyles } = useFieldContext();
|
|
674
|
+
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 }) }));
|
|
675
|
+
});
|
|
676
|
+
FieldInputWrapper.displayName = 'Form.Field.InputWrapper';
|
|
677
|
+
/**
|
|
678
|
+
* Input component for a form field.
|
|
679
|
+
* Must be used within a Form.Field.InputWrapper component.
|
|
680
|
+
* Renders the actual input element without grid positioning.
|
|
633
681
|
*
|
|
634
682
|
* @component
|
|
635
683
|
* @example
|
|
@@ -640,18 +688,53 @@ FieldLabel.displayName = 'Form.Field.Label';
|
|
|
640
688
|
* <Form.Field.Label>
|
|
641
689
|
* <label className="text-foreground font-paragraph">Password</label>
|
|
642
690
|
* </Form.Field.Label>
|
|
643
|
-
* <Form.Field.
|
|
644
|
-
* <
|
|
645
|
-
*
|
|
691
|
+
* <Form.Field.InputWrapper>
|
|
692
|
+
* <Form.Field.Input description={<span className="text-secondary-foreground">Min 8 characters</span>}>
|
|
693
|
+
* <input type="password" className="bg-background border-foreground text-foreground" />
|
|
694
|
+
* </Form.Field.Input>
|
|
695
|
+
* </Form.Field.InputWrapper>
|
|
646
696
|
* </Form.Field>
|
|
647
697
|
* ```
|
|
648
698
|
*/
|
|
649
699
|
export const FieldInput = React.forwardRef((props, ref) => {
|
|
650
700
|
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 }) }));
|
|
701
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.fieldInput, customElement: children, customElementProps: {}, ...otherProps, children: _jsx("div", { children: children }) }));
|
|
653
702
|
});
|
|
654
703
|
FieldInput.displayName = 'Form.Field.Input';
|
|
704
|
+
/**
|
|
705
|
+
* Error component for displaying field-level validation errors.
|
|
706
|
+
* Must be used within a Form.Field.InputWrapper component.
|
|
707
|
+
* Only renders when there is an error for the current field.
|
|
708
|
+
*
|
|
709
|
+
* @component
|
|
710
|
+
* @example
|
|
711
|
+
* ```tsx
|
|
712
|
+
* import { Form } from '@wix/headless-forms/react';
|
|
713
|
+
*
|
|
714
|
+
* <Form.Field id="email">
|
|
715
|
+
* <Form.Field.Label>
|
|
716
|
+
* <label className="text-foreground font-paragraph">Email Address</label>
|
|
717
|
+
* </Form.Field.Label>
|
|
718
|
+
* <Form.Field.InputWrapper>
|
|
719
|
+
* <Form.Field.Input>
|
|
720
|
+
* <input type="email" className="bg-background border-foreground text-foreground" />
|
|
721
|
+
* </Form.Field.Input>
|
|
722
|
+
* <Form.Field.Error path="email">
|
|
723
|
+
* <span className="text-destructive text-sm font-paragraph">Please enter a valid email address</span>
|
|
724
|
+
* </Form.Field.Error>
|
|
725
|
+
* </Form.Field.InputWrapper>
|
|
726
|
+
* </Form.Field>
|
|
727
|
+
* ```
|
|
728
|
+
*/
|
|
729
|
+
export const FieldError = React.forwardRef((props, ref) => {
|
|
730
|
+
const { errorMessage, asChild, className, children, ...otherProps } = props;
|
|
731
|
+
if (!errorMessage && !children)
|
|
732
|
+
return null;
|
|
733
|
+
return (_jsx(AsChildSlot, { "data-testid": TestIds.fieldError, ref: ref, asChild: asChild, className: className, ...otherProps, children: children || errorMessage }));
|
|
734
|
+
});
|
|
735
|
+
FieldError.displayName = 'Form.Field.Error';
|
|
655
736
|
export const Field = FieldRoot;
|
|
656
737
|
Field.Label = FieldLabel;
|
|
738
|
+
Field.InputWrapper = FieldInputWrapper;
|
|
657
739
|
Field.Input = FieldInput;
|
|
740
|
+
Field.Error = FieldError;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface PhoneFieldProps {
|
|
3
|
+
id: string;
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
asChild?: boolean;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface PhoneLabelProps {
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
asChild?: boolean;
|
|
11
|
+
className?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface PhoneLabelRequiredProps {
|
|
14
|
+
required?: boolean;
|
|
15
|
+
children?: React.ReactNode;
|
|
16
|
+
asChild?: boolean;
|
|
17
|
+
className?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare const Required: React.ForwardRefExoticComponent<PhoneLabelRequiredProps & React.RefAttributes<HTMLSpanElement>>;
|
|
20
|
+
interface PhoneLabelComponent extends React.ForwardRefExoticComponent<PhoneLabelProps & React.RefAttributes<HTMLDivElement>> {
|
|
21
|
+
Required: typeof Required;
|
|
22
|
+
}
|
|
23
|
+
export declare const Label: PhoneLabelComponent;
|
|
24
|
+
export interface PhoneErrorProps {
|
|
25
|
+
children?: React.ReactNode;
|
|
26
|
+
asChild?: boolean;
|
|
27
|
+
className?: string;
|
|
28
|
+
}
|
|
29
|
+
declare const Error: React.ForwardRefExoticComponent<PhoneErrorProps & React.RefAttributes<HTMLDivElement>>;
|
|
30
|
+
export interface CountryCodeProps {
|
|
31
|
+
asChild?: boolean;
|
|
32
|
+
className?: string;
|
|
33
|
+
}
|
|
34
|
+
declare const CountryCode: React.ForwardRefExoticComponent<CountryCodeProps & React.RefAttributes<HTMLDivElement>>;
|
|
35
|
+
export interface PhoneFieldInputProps {
|
|
36
|
+
asChild?: boolean;
|
|
37
|
+
className?: string;
|
|
38
|
+
}
|
|
39
|
+
declare const Input: React.ForwardRefExoticComponent<PhoneFieldInputProps & React.RefAttributes<HTMLDivElement>>;
|
|
40
|
+
interface PhoneFieldComponent extends React.ForwardRefExoticComponent<PhoneFieldProps & React.RefAttributes<HTMLDivElement>> {
|
|
41
|
+
Label: typeof Label;
|
|
42
|
+
Error: typeof Error;
|
|
43
|
+
Input: typeof Input;
|
|
44
|
+
CountryCode: typeof CountryCode;
|
|
45
|
+
}
|
|
46
|
+
export declare const PhoneField: PhoneFieldComponent;
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Form } from '@wix/headless-forms/react';
|
|
4
|
+
import { useFieldProps } from '@wix/form-public';
|
|
5
|
+
const Root = React.forwardRef((props, ref) => {
|
|
6
|
+
const { id, children, asChild, className } = props;
|
|
7
|
+
const { description } = useFieldProps();
|
|
8
|
+
return (_jsx(Form.Field, { ref: ref, id: id, asChild: asChild, className: className, children: _jsx(Form.Field.InputWrapper, { children: _jsx(Form.Field.Input, { description: description, children: children }) }) }));
|
|
9
|
+
});
|
|
10
|
+
Root.displayName = 'PhoneField';
|
|
11
|
+
const LabelRoot = React.forwardRef((props, ref) => {
|
|
12
|
+
const { asChild, className, children } = props;
|
|
13
|
+
const { id, label, showLabel } = useFieldProps();
|
|
14
|
+
if (!showLabel) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
return (_jsx(Form.Field.Label, { ref: ref, asChild: asChild, className: className, children: _jsxs("label", { htmlFor: id, children: [label, children] }) }));
|
|
18
|
+
});
|
|
19
|
+
LabelRoot.displayName = 'PhoneField.Label';
|
|
20
|
+
export const Required = React.forwardRef((props, ref) => {
|
|
21
|
+
const { required } = useFieldProps();
|
|
22
|
+
return _jsx(Form.Field.Label.Required, { ...props, ref: ref, required: required });
|
|
23
|
+
});
|
|
24
|
+
Required.displayName = 'PhoneField.Label.Required';
|
|
25
|
+
export const Label = LabelRoot;
|
|
26
|
+
Label.Required = Required;
|
|
27
|
+
const Error = React.forwardRef((props, ref) => {
|
|
28
|
+
const { children, ...rest } = props;
|
|
29
|
+
const { errorMessage } = useFieldProps();
|
|
30
|
+
return (_jsx(Form.Field.Error, { ref: ref, ...rest, children: errorMessage }));
|
|
31
|
+
});
|
|
32
|
+
Error.displayName = 'PhoneField.Error';
|
|
33
|
+
const CountryCode = React.forwardRef((props, ref) => {
|
|
34
|
+
const { asChild, className, ...rest } = props;
|
|
35
|
+
const { id, countryCodes, defaultCountryCode, readOnly, onChange, onBlur, onFocus, } = useFieldProps();
|
|
36
|
+
return (_jsx(Form.Field.Input, { ref: ref, asChild: asChild, className: className, ...rest, children: _jsx("select", { id: `${id}-country`, defaultValue: defaultCountryCode || '', disabled: readOnly, onChange: (e) => onChange?.(e.target.value), onBlur: () => onBlur?.(), onFocus: () => onFocus?.(), children: countryCodes?.map((code) => (_jsx("option", { value: code, children: code }, code))) }) }));
|
|
37
|
+
});
|
|
38
|
+
CountryCode.displayName = 'PhoneField.CountryCode';
|
|
39
|
+
const Input = React.forwardRef((props, ref) => {
|
|
40
|
+
const { asChild, className, ...rest } = props;
|
|
41
|
+
const { id, value, required, readOnly, placeholder, onChange, onBlur, onFocus, description, } = useFieldProps();
|
|
42
|
+
const descriptionId = description ? `${id}-description` : undefined;
|
|
43
|
+
return (_jsx(Form.Field.Input, { ref: ref, asChild: asChild, className: className, ...rest, children: _jsx("input", { id: id, type: "tel", value: value || '', required: required, readOnly: readOnly, placeholder: placeholder, "aria-describedby": descriptionId, "aria-invalid": !!(required && !value), "aria-required": required, onChange: (e) => onChange?.(e.target.value), onBlur: () => onBlur?.(), onFocus: () => onFocus?.() }) }));
|
|
44
|
+
});
|
|
45
|
+
Input.displayName = 'PhoneField.Input';
|
|
46
|
+
export const PhoneField = Root;
|
|
47
|
+
PhoneField.Label = Label;
|
|
48
|
+
PhoneField.Error = Error;
|
|
49
|
+
PhoneField.Input = Input;
|
|
50
|
+
PhoneField.CountryCode = CountryCode;
|
|
@@ -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/dist/react/index.d.ts
CHANGED
package/dist/react/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/headless-forms",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
4
|
+
"license": "MIT",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"scripts": {
|
|
6
7
|
"build": "npm run build:esm && npm run build:cjs",
|
|
@@ -41,10 +42,21 @@
|
|
|
41
42
|
"vitest": "^3.1.4"
|
|
42
43
|
},
|
|
43
44
|
"dependencies": {
|
|
44
|
-
"@wix/form-public": "^0.
|
|
45
|
+
"@wix/form-public": "^0.55.0",
|
|
45
46
|
"@wix/forms": "^1.0.331",
|
|
46
|
-
"@wix/headless-utils": "0.0.
|
|
47
|
+
"@wix/headless-utils": "0.0.5",
|
|
47
48
|
"@wix/services-definitions": "^0.1.4",
|
|
48
49
|
"@wix/services-manager-react": "^0.1.26"
|
|
49
|
-
}
|
|
50
|
+
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"registry": "https://registry.npmjs.org/",
|
|
53
|
+
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"wix": {
|
|
56
|
+
"artifact": {
|
|
57
|
+
"artifactId": "headless-forms",
|
|
58
|
+
"groupId": "com.wixpress.headless-components"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"falconPackageHash": "168b61be66455144dea6e8f0990a615314a7fbd283fa1cd8e4c18b6c"
|
|
50
62
|
}
|