@engagebay/engagebay-form-module 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +126 -0
- package/link.sh +2 -0
- package/package.json +30 -0
- package/src/api/index.ts +25 -0
- package/src/form/Form.tsx +157 -0
- package/src/form/FormField.tsx +80 -0
- package/src/form/FormFieldUtils.ts +241 -0
- package/src/form/FormFields.tsx +41 -0
- package/src/form/context/FormContext.tsx +66 -0
- package/src/form/formfields/ArrayField.tsx +169 -0
- package/src/form/formfields/BusinessHoursField.tsx +204 -0
- package/src/form/formfields/CheckboxButtonsField.tsx +97 -0
- package/src/form/formfields/CheckboxField.tsx +118 -0
- package/src/form/formfields/ColorPickerField.tsx +59 -0
- package/src/form/formfields/ComboMultiSelect.tsx +290 -0
- package/src/form/formfields/ComboSelect.tsx +278 -0
- package/src/form/formfields/DatePickerField.tsx +89 -0
- package/src/form/formfields/DateRangePickerField.tsx +104 -0
- package/src/form/formfields/DynamicMultiSelect.tsx +189 -0
- package/src/form/formfields/DynamicSelect.tsx +187 -0
- package/src/form/formfields/Error.tsx +15 -0
- package/src/form/formfields/ErrorContextHandler.tsx +77 -0
- package/src/form/formfields/FileUploadField.tsx +196 -0
- package/src/form/formfields/IframeField.tsx +65 -0
- package/src/form/formfields/InputField.tsx +67 -0
- package/src/form/formfields/InputGroupField.tsx +44 -0
- package/src/form/formfields/MultipleSelectField.tsx +98 -0
- package/src/form/formfields/NumberField.tsx +61 -0
- package/src/form/formfields/PasswordField.tsx +93 -0
- package/src/form/formfields/PhoneNumberField.tsx +163 -0
- package/src/form/formfields/RadioField.tsx +104 -0
- package/src/form/formfields/RadioGroupComponent.tsx +94 -0
- package/src/form/formfields/RangeField.tsx +53 -0
- package/src/form/formfields/SelectField.tsx +82 -0
- package/src/form/formfields/SwitchField.tsx +131 -0
- package/src/form/formfields/TextAreaField.tsx +48 -0
- package/src/form/formfields/TimeField.tsx +53 -0
- package/src/form/formfields/Typeahead.tsx +211 -0
- package/src/form/formfields/TypeaheadMultiSelect.tsx +203 -0
- package/src/form/formfields/UrlField.tsx +53 -0
- package/src/form/hooks/useDynamicReducer.tsx +42 -0
- package/src/form/schema/CustomValidators.ts +63 -0
- package/src/form/schema/FormFieldSchema.ts +342 -0
- package/src/form/util/RenderFormField.tsx +149 -0
- package/src/form/util/RenderListOptions.tsx +424 -0
- package/src/form/util/index.ts +185 -0
- package/src/util/LoaderWithText.tsx +28 -0
- package/src/util/svg/HELPER_ICONS.ts +16 -0
- package/src/util/svg/SVGIcon.tsx +23 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
Fragment,
|
|
3
|
+
useContext,
|
|
4
|
+
useEffect,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from "react";
|
|
8
|
+
|
|
9
|
+
import { RegisterOptions } from "react-hook-form";
|
|
10
|
+
import { Listbox, ListboxButton, Transition } from "@headlessui/react";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
FieldOptionsSchema,
|
|
14
|
+
FormFieldComponentPropSchema,
|
|
15
|
+
FormFieldType,
|
|
16
|
+
} from "../schema/FormFieldSchema";
|
|
17
|
+
import { FormContext } from "../context/FormContext";
|
|
18
|
+
import RenderFormField from "../util/RenderFormField";
|
|
19
|
+
import RenderListOptions, {
|
|
20
|
+
renderListBoxValue,
|
|
21
|
+
} from "../util/RenderListOptions";
|
|
22
|
+
import { handleChange, registerFormField } from "../util";
|
|
23
|
+
|
|
24
|
+
const MultipleSelectField: React.FC<FormFieldComponentPropSchema> = ({
|
|
25
|
+
fieldConfig,
|
|
26
|
+
onChange,
|
|
27
|
+
}: FormFieldComponentPropSchema) => {
|
|
28
|
+
const multiSelectRef = useRef<HTMLUListElement>(null);
|
|
29
|
+
const formContext = useContext(FormContext);
|
|
30
|
+
let registerOptions: RegisterOptions = registerFormField(fieldConfig, true);
|
|
31
|
+
let hookProps = formContext.register(fieldConfig.name, registerOptions);
|
|
32
|
+
|
|
33
|
+
const [listOptions, setListOptions] = useState<FieldOptionsSchema[]>(
|
|
34
|
+
fieldConfig.options ? fieldConfig.options : []
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const validSelectedValues = (formContext.getValues(fieldConfig.name) ||[])
|
|
38
|
+
.filter((selectedValue:any) =>
|
|
39
|
+
fieldConfig.options && fieldConfig.options.some(option => option.value === selectedValue)
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (
|
|
44
|
+
!formContext.getValues(fieldConfig.name) &&
|
|
45
|
+
fieldConfig.defaultValue
|
|
46
|
+
)
|
|
47
|
+
formContext.setValue(fieldConfig.name, fieldConfig.defaultValue);
|
|
48
|
+
|
|
49
|
+
if (fieldConfig.options && fieldConfig.options.length > 0) {
|
|
50
|
+
setListOptions(fieldConfig.options);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}, [fieldConfig.options]);
|
|
54
|
+
|
|
55
|
+
function getInput() {
|
|
56
|
+
return (
|
|
57
|
+
<Listbox
|
|
58
|
+
as={"div"}
|
|
59
|
+
{...hookProps}
|
|
60
|
+
value={validSelectedValues}
|
|
61
|
+
defaultValue={fieldConfig.defaultValue}
|
|
62
|
+
onChange={(val) =>
|
|
63
|
+
handleChange(val, formContext, fieldConfig, onChange)
|
|
64
|
+
}
|
|
65
|
+
disabled={fieldConfig.disabled}
|
|
66
|
+
multiple
|
|
67
|
+
className={"relative form-listbox flex-1"}>
|
|
68
|
+
<ListboxButton
|
|
69
|
+
className={
|
|
70
|
+
fieldConfig.customClassNames?.fieldClassName
|
|
71
|
+
? "form-listbox-select " +
|
|
72
|
+
fieldConfig.customClassNames?.fieldClassName
|
|
73
|
+
: "form-listbox-select"
|
|
74
|
+
}>
|
|
75
|
+
{renderListBoxValue(
|
|
76
|
+
formContext,
|
|
77
|
+
fieldConfig,
|
|
78
|
+
listOptions,
|
|
79
|
+
onChange
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
</ListboxButton>
|
|
83
|
+
<RenderListOptions
|
|
84
|
+
formContext={formContext}
|
|
85
|
+
onChange={onChange}
|
|
86
|
+
formField={FormFieldType.MULTI_SELECT}
|
|
87
|
+
ref={multiSelectRef}
|
|
88
|
+
fieldConfig={fieldConfig}
|
|
89
|
+
listOptions={listOptions}
|
|
90
|
+
setListOptions={setListOptions}
|
|
91
|
+
/>
|
|
92
|
+
</Listbox>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return <RenderFormField fieldConfig={fieldConfig} getInput={getInput} />;
|
|
97
|
+
};
|
|
98
|
+
export default MultipleSelectField;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React, {useContext} from "react";
|
|
2
|
+
import {RegisterOptions} from "react-hook-form";
|
|
3
|
+
import {FormContext} from "../context/FormContext";
|
|
4
|
+
import {FormFieldComponentPropSchema} from "../schema/FormFieldSchema";
|
|
5
|
+
import {handleChange, registerFormField} from "../util";
|
|
6
|
+
import RenderFormField from "../util/RenderFormField";
|
|
7
|
+
|
|
8
|
+
const NumberField: React.FC<FormFieldComponentPropSchema> = (
|
|
9
|
+
props: FormFieldComponentPropSchema
|
|
10
|
+
) => {
|
|
11
|
+
const formContext = useContext(FormContext);
|
|
12
|
+
|
|
13
|
+
let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
|
|
14
|
+
|
|
15
|
+
let hookProps = formContext.register(props.fieldConfig.name, registerOptions);
|
|
16
|
+
|
|
17
|
+
function getInput() {
|
|
18
|
+
return (
|
|
19
|
+
<input
|
|
20
|
+
type="number"
|
|
21
|
+
{...hookProps}
|
|
22
|
+
placeholder={props.fieldConfig?.placeholder}
|
|
23
|
+
readOnly={props.fieldConfig?.readOnly}
|
|
24
|
+
disabled={props.fieldConfig?.disabled}
|
|
25
|
+
autoComplete={props.fieldConfig?.autoComplete}
|
|
26
|
+
min={props.fieldConfig?.min}
|
|
27
|
+
max={props.fieldConfig?.max}
|
|
28
|
+
step={props.fieldConfig.decimalAllowed ? 0.01 : 1}
|
|
29
|
+
defaultValue={props.fieldConfig.defaultValue as string}
|
|
30
|
+
className={`form-input ${
|
|
31
|
+
props.fieldConfig.customClassNames?.fieldClassName || "flex-1 !w-60"
|
|
32
|
+
}`}
|
|
33
|
+
onKeyDown={(e) => {
|
|
34
|
+
props.fieldConfig.onKeyDown && props.fieldConfig.onKeyDown(e);
|
|
35
|
+
}}
|
|
36
|
+
onChange={(e) => {
|
|
37
|
+
let value = e.target.value;
|
|
38
|
+
let numericValue = parseFloat(value);
|
|
39
|
+
if (props.fieldConfig.max !== undefined && numericValue > props.fieldConfig.max) {
|
|
40
|
+
numericValue = props.fieldConfig.max;
|
|
41
|
+
}
|
|
42
|
+
if (props.fieldConfig.min !== undefined && numericValue < props.fieldConfig.min) {
|
|
43
|
+
numericValue = props.fieldConfig.min;
|
|
44
|
+
}
|
|
45
|
+
value = numericValue.toString();
|
|
46
|
+
handleChange(
|
|
47
|
+
value,
|
|
48
|
+
formContext,
|
|
49
|
+
props.fieldConfig,
|
|
50
|
+
props.onChange
|
|
51
|
+
);
|
|
52
|
+
}}
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<RenderFormField fieldConfig={props.fieldConfig} getInput={getInput}/>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
export default NumberField;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import React, { useContext, useState } from "react";
|
|
2
|
+
import { RegisterOptions } from "react-hook-form";
|
|
3
|
+
import { FormContext } from "../context/FormContext";
|
|
4
|
+
import { FormFieldComponentPropSchema } from "../schema/FormFieldSchema";
|
|
5
|
+
import RenderFormField from "../util/RenderFormField";
|
|
6
|
+
import { handleChange, registerFormField } from "../util";
|
|
7
|
+
import Tippy from '@tippyjs/react';
|
|
8
|
+
|
|
9
|
+
const PasswordField: React.FC<FormFieldComponentPropSchema> = (
|
|
10
|
+
props: FormFieldComponentPropSchema
|
|
11
|
+
) => {
|
|
12
|
+
const formContext = useContext(FormContext);
|
|
13
|
+
|
|
14
|
+
let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
|
|
15
|
+
|
|
16
|
+
let hookProps = formContext.register(props.fieldConfig.name, registerOptions);
|
|
17
|
+
|
|
18
|
+
const [isPasswordVisible, setIsPasswordVisible] = useState<boolean>(false);
|
|
19
|
+
|
|
20
|
+
function getInput() {
|
|
21
|
+
return (
|
|
22
|
+
<div className="relative">
|
|
23
|
+
<input
|
|
24
|
+
type={`${isPasswordVisible ? "text" : "password"}`}
|
|
25
|
+
{...hookProps}
|
|
26
|
+
placeholder={props.fieldConfig?.placeholder}
|
|
27
|
+
readOnly={props.fieldConfig?.readOnly}
|
|
28
|
+
disabled={props.fieldConfig?.disabled}
|
|
29
|
+
autoComplete={props.fieldConfig?.autoComplete}
|
|
30
|
+
defaultValue={props.fieldConfig.defaultValue as string}
|
|
31
|
+
className={`form-input ${props.fieldConfig.customClassNames?.fieldClassName || "flex-1"}`}
|
|
32
|
+
onKeyDown={(e) => {
|
|
33
|
+
props.fieldConfig.onKeyDown && props.fieldConfig.onKeyDown(e);
|
|
34
|
+
}}
|
|
35
|
+
onChange={(e) => {
|
|
36
|
+
handleChange(
|
|
37
|
+
e.target.value,
|
|
38
|
+
formContext,
|
|
39
|
+
props.fieldConfig,
|
|
40
|
+
props.onChange
|
|
41
|
+
);
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
44
|
+
<div
|
|
45
|
+
className="absolute inset-y-0 right-0 flex items-center pr-3 cursor-pointer"
|
|
46
|
+
onClick={() => setIsPasswordVisible(!isPasswordVisible)}
|
|
47
|
+
>
|
|
48
|
+
<div
|
|
49
|
+
className="absolute inset-y-0 right-0 flex items-center pr-3 cursor-pointer"
|
|
50
|
+
onClick={() => setIsPasswordVisible(!isPasswordVisible)}
|
|
51
|
+
>
|
|
52
|
+
{isPasswordVisible ? (
|
|
53
|
+
<Tippy
|
|
54
|
+
content={
|
|
55
|
+
<>
|
|
56
|
+
<h4 className="text-sm capitalize text-center truncate">Hide Password</h4>
|
|
57
|
+
</>
|
|
58
|
+
}
|
|
59
|
+
>
|
|
60
|
+
<span className="mark-as-read-btn">
|
|
61
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#121212a1" className="bi bi-eye-slash-fill" viewBox="0 0 16 16">
|
|
62
|
+
<path d="m10.79 12.912-1.614-1.615a3.5 3.5 0 0 1-4.474-4.474l-2.06-2.06C.938 6.278 0 8 0 8s3 5.5 8 5.5a7 7 0 0 0 2.79-.588M5.21 3.088A7 7 0 0 1 8 2.5c5 0 8 5.5 8 5.5s-.939 1.721-2.641 3.238l-2.062-2.062a3.5 3.5 0 0 0-4.474-4.474z" />
|
|
63
|
+
<path d="M5.525 7.646a2.5 2.5 0 0 0 2.829 2.829zm4.95.708-2.829-2.83a2.5 2.5 0 0 1 2.829 2.829zm3.171 6-12-12 .708-.708 12 12z" />
|
|
64
|
+
</svg>
|
|
65
|
+
</span>
|
|
66
|
+
</Tippy>
|
|
67
|
+
) : (
|
|
68
|
+
<Tippy
|
|
69
|
+
content={
|
|
70
|
+
<>
|
|
71
|
+
<h4 className="text-sm capitalize text-center truncate">Show Password</h4>
|
|
72
|
+
</>
|
|
73
|
+
}
|
|
74
|
+
>
|
|
75
|
+
<span className="mark-as-read-btn">
|
|
76
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#121212a1" className="bi bi-eye-fill" viewBox="0 0 16 16">
|
|
77
|
+
<path d="M10.5 8a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0" />
|
|
78
|
+
<path d="M0 8s3-5.5 8-5.5S16 8 16 8s-3 5.5-8 5.5S0 8 0 8m8 3.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7" />
|
|
79
|
+
</svg>
|
|
80
|
+
</span>
|
|
81
|
+
</Tippy>
|
|
82
|
+
)}
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
export default PasswordField;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import React, { useContext, useCallback, useState } from "react";
|
|
2
|
+
import { RegisterOptions } from "react-hook-form";
|
|
3
|
+
import 'react-phone-number-input/style.css';
|
|
4
|
+
import PhoneInput, { isValidPhoneNumber, Country } from 'react-phone-number-input';
|
|
5
|
+
import { Input, InputProps, Popover, PopoverButton, PopoverPanel } from "@headlessui/react";
|
|
6
|
+
|
|
7
|
+
import flags from "react-phone-number-input/flags";
|
|
8
|
+
import * as RPNInput from "react-phone-number-input";
|
|
9
|
+
import { FormContext } from "../context/FormContext";
|
|
10
|
+
import { FormFieldComponentPropSchema, FormFieldType } from "../schema/FormFieldSchema";
|
|
11
|
+
import RenderFormField from "../util/RenderFormField";
|
|
12
|
+
import { handleChange, registerFormField } from "../util";
|
|
13
|
+
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/react/24/outline";
|
|
14
|
+
import InputField from "./InputField";
|
|
15
|
+
import clsx from "clsx";
|
|
16
|
+
// Phone Number Field Component
|
|
17
|
+
const PhoneNumberField: React.FC<FormFieldComponentPropSchema> = ({
|
|
18
|
+
fieldConfig,
|
|
19
|
+
onChange
|
|
20
|
+
}: FormFieldComponentPropSchema) => {
|
|
21
|
+
const formContext = useContext(FormContext);
|
|
22
|
+
const registerOptions: RegisterOptions = {
|
|
23
|
+
...registerFormField(fieldConfig),
|
|
24
|
+
validate: (value) => isValidPhoneNumber(value || '') || "Please enter a valid mobile number."
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const hookprops = formContext.register(fieldConfig.name, registerOptions);
|
|
28
|
+
|
|
29
|
+
const getInput = () => (
|
|
30
|
+
<>
|
|
31
|
+
{/* Phone Input */}
|
|
32
|
+
<RPNInput.default
|
|
33
|
+
{...hookprops}
|
|
34
|
+
className={clsx("flex")}
|
|
35
|
+
flagComponent={FlagComponent}
|
|
36
|
+
countrySelectComponent={CountrySelect}
|
|
37
|
+
inputComponent={InputComponent}
|
|
38
|
+
onChange={(value: any) => {
|
|
39
|
+
if (!value) return;
|
|
40
|
+
|
|
41
|
+
handleChange(value, formContext, fieldConfig, onChange);
|
|
42
|
+
}}
|
|
43
|
+
defaultCountry={fieldConfig.defaultValue as Country}
|
|
44
|
+
international
|
|
45
|
+
value={formContext.getValues(fieldConfig.name)}
|
|
46
|
+
disabled={fieldConfig.disabled}
|
|
47
|
+
// limitMaxLength={true}
|
|
48
|
+
placeholder={fieldConfig.placeholder}
|
|
49
|
+
withCountryCallingCode
|
|
50
|
+
addInternationalOption={false}
|
|
51
|
+
readOnly={fieldConfig.readOnly}
|
|
52
|
+
/>
|
|
53
|
+
</>
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
return <RenderFormField fieldConfig={fieldConfig} getInput={getInput} />;
|
|
57
|
+
};
|
|
58
|
+
export default PhoneNumberField;
|
|
59
|
+
|
|
60
|
+
// Country Select Component
|
|
61
|
+
const CountrySelect: React.FC<{
|
|
62
|
+
disabled: boolean;
|
|
63
|
+
value: Country;
|
|
64
|
+
onChange: (value: Country) => void;
|
|
65
|
+
options: { value: Country; label: string }[];
|
|
66
|
+
}> = ({ disabled, value, onChange, options }) => {
|
|
67
|
+
const [searchTerm, setSearchTerm] = useState<string>("");
|
|
68
|
+
|
|
69
|
+
const handleSelect = useCallback(
|
|
70
|
+
(country: Country) => {
|
|
71
|
+
onChange(country);
|
|
72
|
+
},
|
|
73
|
+
[onChange]
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Filter options based on the search term
|
|
77
|
+
const filteredOptions = options.filter((option) =>
|
|
78
|
+
option.label.toLowerCase().includes(searchTerm.toLowerCase())
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<Popover className={''}>
|
|
83
|
+
<PopoverButton
|
|
84
|
+
type="button"
|
|
85
|
+
className={`form-listbox-select rounded-e-none flex ${disabled ? 'opacity-50' : ''}`}
|
|
86
|
+
disabled={disabled}
|
|
87
|
+
>
|
|
88
|
+
<FlagComponent
|
|
89
|
+
country={value as Country}
|
|
90
|
+
countryName={value}
|
|
91
|
+
/>
|
|
92
|
+
{/* <ChevronUpDownIcon className="h-5 w-5 text-gray-400" /> */}
|
|
93
|
+
</PopoverButton>
|
|
94
|
+
<PopoverPanel className="w-[300px] absolute z-50 p-0 bg-white shadow-lg rounded-md">
|
|
95
|
+
<div className="p-2">
|
|
96
|
+
<input
|
|
97
|
+
className="form-input pr-9"
|
|
98
|
+
placeholder="Search country..."
|
|
99
|
+
value={searchTerm}
|
|
100
|
+
onChange={(e) => setSearchTerm(e.target.value)}
|
|
101
|
+
/>
|
|
102
|
+
</div>
|
|
103
|
+
<div className="phone-num-dropdown">
|
|
104
|
+
{filteredOptions.length > 0 ? (
|
|
105
|
+
filteredOptions.map((option) => (
|
|
106
|
+
<div
|
|
107
|
+
key={option.value}
|
|
108
|
+
className="flex items-center justify-between px-3 py-2 hover:bg-gray-100 cursor-pointer"
|
|
109
|
+
onClick={() => handleSelect(option.value)}
|
|
110
|
+
>
|
|
111
|
+
{/* Country flag */}
|
|
112
|
+
<div className="flex items-center">
|
|
113
|
+
<FlagComponent
|
|
114
|
+
country={option.value}
|
|
115
|
+
countryName={option.label}
|
|
116
|
+
/>
|
|
117
|
+
<span className="ml-3">{option.label}</span>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
{/* Country code and check icon */}
|
|
121
|
+
<div className="flex items-center space-x-3">
|
|
122
|
+
{option.value && (
|
|
123
|
+
<span className="text-foreground/50 text-sm">
|
|
124
|
+
{`+${RPNInput.getCountryCallingCode(option.value)}`}
|
|
125
|
+
</span>
|
|
126
|
+
)}
|
|
127
|
+
<CheckIcon
|
|
128
|
+
className={`h-4 w-4 ${option.value === value ? 'opacity-100' : 'opacity-0'}`}
|
|
129
|
+
/>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
))
|
|
133
|
+
) : (
|
|
134
|
+
<div className="px-3 py-2 text-gray-500 text-center">No country found</div>
|
|
135
|
+
)}
|
|
136
|
+
</div>
|
|
137
|
+
</PopoverPanel>
|
|
138
|
+
</Popover>
|
|
139
|
+
);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Flag Component
|
|
143
|
+
const FlagComponent = ({ country, countryName }: RPNInput.FlagProps) => {
|
|
144
|
+
const Flag = flags[country];
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<span className="bg-foreground/20 flex h-5 w-6 overflow-hidden rounded-sm">
|
|
148
|
+
{Flag && <Flag title={countryName} />}
|
|
149
|
+
</span>
|
|
150
|
+
);
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// Input Component for the phone input
|
|
154
|
+
const InputComponent = React.forwardRef<HTMLInputElement, InputProps>(
|
|
155
|
+
({ className, ...props }, ref) => (
|
|
156
|
+
<Input
|
|
157
|
+
className={clsx("rounded-s-none form-input", className)}
|
|
158
|
+
{...props}
|
|
159
|
+
ref={ref}
|
|
160
|
+
/>
|
|
161
|
+
),
|
|
162
|
+
);
|
|
163
|
+
InputComponent.displayName = "InputComponent";
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React, { useContext } from "react";
|
|
2
|
+
import { RegisterOptions } from "react-hook-form";
|
|
3
|
+
import { FormContext } from "../context/FormContext";
|
|
4
|
+
import { FormFieldComponentPropSchema } from "../schema/FormFieldSchema";
|
|
5
|
+
import RenderFormField from "../util/RenderFormField";
|
|
6
|
+
import { handleChange, registerFormField } from "../util";
|
|
7
|
+
import { Input, Radio, RadioGroup } from "@headlessui/react";
|
|
8
|
+
import clsx from "clsx";
|
|
9
|
+
|
|
10
|
+
const RadioField: React.FC<FormFieldComponentPropSchema> = (
|
|
11
|
+
props: FormFieldComponentPropSchema
|
|
12
|
+
) => {
|
|
13
|
+
const formContext = useContext(FormContext);
|
|
14
|
+
|
|
15
|
+
let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
|
|
16
|
+
|
|
17
|
+
let hookProps = formContext.register(
|
|
18
|
+
props.fieldConfig.name,
|
|
19
|
+
registerOptions
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
function getInput() {
|
|
23
|
+
return (
|
|
24
|
+
props.fieldConfig.options && (
|
|
25
|
+
<RadioGroup
|
|
26
|
+
{...hookProps}
|
|
27
|
+
value={formContext.getValues(props.fieldConfig.name) ?? props.fieldConfig.defaultValue}
|
|
28
|
+
className={
|
|
29
|
+
props.fieldConfig.customClassNames
|
|
30
|
+
?.fieldClassName ||
|
|
31
|
+
"form-check-container"
|
|
32
|
+
}
|
|
33
|
+
onChange={(value) => {
|
|
34
|
+
handleChange(
|
|
35
|
+
value,
|
|
36
|
+
formContext,
|
|
37
|
+
props.fieldConfig,
|
|
38
|
+
props.onChange
|
|
39
|
+
);
|
|
40
|
+
}}>
|
|
41
|
+
{props.fieldConfig.options.map((option) => {
|
|
42
|
+
return (
|
|
43
|
+
<Radio
|
|
44
|
+
value={option.value}
|
|
45
|
+
key={option.value}
|
|
46
|
+
disabled={option.isDisabled}
|
|
47
|
+
className={
|
|
48
|
+
props.fieldConfig.customClassNames
|
|
49
|
+
?.optionClassName ||
|
|
50
|
+
"form-check-container"
|
|
51
|
+
}
|
|
52
|
+
>
|
|
53
|
+
{({ checked }) => {
|
|
54
|
+
return props.fieldConfig
|
|
55
|
+
.fieldOptionWrapper ? (
|
|
56
|
+
<props.fieldConfig.fieldOptionWrapper
|
|
57
|
+
data={option}
|
|
58
|
+
selected={
|
|
59
|
+
checked
|
|
60
|
+
}></props.fieldConfig.fieldOptionWrapper>
|
|
61
|
+
) : (
|
|
62
|
+
<>
|
|
63
|
+
<span
|
|
64
|
+
className={"form-check-radio flex items-center"}>
|
|
65
|
+
<span
|
|
66
|
+
className={clsx("form-radio ")}
|
|
67
|
+
>
|
|
68
|
+
{checked &&
|
|
69
|
+
<span className="checked"></span>
|
|
70
|
+
}
|
|
71
|
+
</span>
|
|
72
|
+
<label htmlFor={option.value} className="cursor-pointer">
|
|
73
|
+
{option.label}
|
|
74
|
+
{option.icon && (
|
|
75
|
+
<span
|
|
76
|
+
className={
|
|
77
|
+
"listbox-svg ml-2 cursor-pointer"
|
|
78
|
+
}>
|
|
79
|
+
{option.icon}
|
|
80
|
+
</span>
|
|
81
|
+
)}
|
|
82
|
+
</label>
|
|
83
|
+
</span>
|
|
84
|
+
{option.helpText && (
|
|
85
|
+
<p className="ms-6 text-tiny text-gray-500">
|
|
86
|
+
{option.helpText}
|
|
87
|
+
</p>
|
|
88
|
+
)}
|
|
89
|
+
</>
|
|
90
|
+
);
|
|
91
|
+
}}
|
|
92
|
+
</Radio>
|
|
93
|
+
);
|
|
94
|
+
})}
|
|
95
|
+
</RadioGroup>
|
|
96
|
+
)
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
export default RadioField;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import React, { useContext } from "react";
|
|
2
|
+
import { RegisterOptions } from "react-hook-form";
|
|
3
|
+
import { FormContext } from "../context/FormContext";
|
|
4
|
+
import { Label, Radio, RadioGroup } from "@headlessui/react";
|
|
5
|
+
import { FormFieldComponentPropSchema } from "../schema/FormFieldSchema";
|
|
6
|
+
import RenderFormField from "../util/RenderFormField";
|
|
7
|
+
import { handleChange, registerFormField } from "../util";
|
|
8
|
+
|
|
9
|
+
const RadioGroupComponent: React.FC<FormFieldComponentPropSchema> = (
|
|
10
|
+
props: FormFieldComponentPropSchema
|
|
11
|
+
) => {
|
|
12
|
+
const formContext = useContext(FormContext);
|
|
13
|
+
|
|
14
|
+
let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
|
|
15
|
+
|
|
16
|
+
let hookProps = formContext.register(props.fieldConfig.name, registerOptions);
|
|
17
|
+
|
|
18
|
+
function getInput() {
|
|
19
|
+
return (
|
|
20
|
+
<>
|
|
21
|
+
<div className="w-full">
|
|
22
|
+
{/*<div className="mx-auto w-full max-w-md">*/}
|
|
23
|
+
{props.fieldConfig.options && (
|
|
24
|
+
<RadioGroup
|
|
25
|
+
{...hookProps}
|
|
26
|
+
onChange={(value) => {
|
|
27
|
+
handleChange(
|
|
28
|
+
value,
|
|
29
|
+
formContext,
|
|
30
|
+
props.fieldConfig,
|
|
31
|
+
props.onChange
|
|
32
|
+
);
|
|
33
|
+
}}
|
|
34
|
+
>
|
|
35
|
+
<Label className="sr-only">Server size</Label>
|
|
36
|
+
<div className="space-x-2 flex items-stretch">
|
|
37
|
+
{props.fieldConfig.options.map((option) => (
|
|
38
|
+
<Radio
|
|
39
|
+
key={option.label}
|
|
40
|
+
value={option.value}
|
|
41
|
+
disabled={option.isDisabled}
|
|
42
|
+
className={({ checked }) =>
|
|
43
|
+
`${
|
|
44
|
+
checked ? "bg-blue-50" : "bg-white"
|
|
45
|
+
} flex w-full flex-col gap-2 rounded-lg border border-gray-200 p-4 shadow-sm cursor-pointer`
|
|
46
|
+
}
|
|
47
|
+
>
|
|
48
|
+
{({ checked }) => (
|
|
49
|
+
<>
|
|
50
|
+
<div className="flex w-full items-center justify-between">
|
|
51
|
+
<div className="flex items-center justify-center">
|
|
52
|
+
<span className={"listbox-svg"}>
|
|
53
|
+
{option.icon}
|
|
54
|
+
</span>
|
|
55
|
+
<div className="text-sm ">
|
|
56
|
+
<Label
|
|
57
|
+
as="p"
|
|
58
|
+
className={`font-medium ${
|
|
59
|
+
checked ? "text-gray-900" : "text-gray-900"
|
|
60
|
+
}`}
|
|
61
|
+
>
|
|
62
|
+
{option.label}
|
|
63
|
+
</Label>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
{option.helpText ? (
|
|
68
|
+
<p
|
|
69
|
+
className="mt-2 text-tiny text-gray-500"
|
|
70
|
+
id="email-description"
|
|
71
|
+
>
|
|
72
|
+
{option.helpText}
|
|
73
|
+
</p>
|
|
74
|
+
) : (
|
|
75
|
+
<></>
|
|
76
|
+
)}
|
|
77
|
+
</>
|
|
78
|
+
)}
|
|
79
|
+
</Radio>
|
|
80
|
+
))}
|
|
81
|
+
</div>
|
|
82
|
+
</RadioGroup>
|
|
83
|
+
)}
|
|
84
|
+
{/*</div>*/}
|
|
85
|
+
</div>
|
|
86
|
+
</>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
export default RadioGroupComponent;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { RegisterOptions } from "react-hook-form";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { FormContext } from "../context/FormContext";
|
|
5
|
+
import { FormFieldComponentPropSchema } from "../schema/FormFieldSchema";
|
|
6
|
+
import { Input } from "@headlessui/react";
|
|
7
|
+
import RenderFormField from "../util/RenderFormField";
|
|
8
|
+
import { handleChange, registerFormField } from "../util";
|
|
9
|
+
|
|
10
|
+
export const RangeField: React.FC<FormFieldComponentPropSchema> = (
|
|
11
|
+
props: FormFieldComponentPropSchema
|
|
12
|
+
) => {
|
|
13
|
+
const formContext = useContext(FormContext);
|
|
14
|
+
|
|
15
|
+
let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
|
|
16
|
+
|
|
17
|
+
let hookProps = formContext.register(props.fieldConfig.name, registerOptions);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Raw Form Field
|
|
21
|
+
*/
|
|
22
|
+
const getInput = () => {
|
|
23
|
+
return (
|
|
24
|
+
<Input
|
|
25
|
+
type="range"
|
|
26
|
+
{...hookProps}
|
|
27
|
+
placeholder={props.fieldConfig?.placeholder}
|
|
28
|
+
readOnly={props.fieldConfig?.readOnly}
|
|
29
|
+
disabled={props.fieldConfig?.disabled}
|
|
30
|
+
autoComplete={props.fieldConfig?.autoComplete}
|
|
31
|
+
defaultValue={props.fieldConfig.defaultValue as string}
|
|
32
|
+
className={`form-range ${
|
|
33
|
+
props.fieldConfig.customClassNames?.fieldClassName || ""
|
|
34
|
+
}`}
|
|
35
|
+
onChange={(e) => {
|
|
36
|
+
handleChange(
|
|
37
|
+
e.target.value,
|
|
38
|
+
formContext,
|
|
39
|
+
props.fieldConfig,
|
|
40
|
+
props.onChange
|
|
41
|
+
);
|
|
42
|
+
}}
|
|
43
|
+
onClick={(event) => {
|
|
44
|
+
event.currentTarget.showPicker();
|
|
45
|
+
}}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
|
|
52
|
+
);
|
|
53
|
+
};
|