@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.
Files changed (50) hide show
  1. package/README.md +126 -0
  2. package/link.sh +2 -0
  3. package/package.json +30 -0
  4. package/src/api/index.ts +25 -0
  5. package/src/form/Form.tsx +157 -0
  6. package/src/form/FormField.tsx +80 -0
  7. package/src/form/FormFieldUtils.ts +241 -0
  8. package/src/form/FormFields.tsx +41 -0
  9. package/src/form/context/FormContext.tsx +66 -0
  10. package/src/form/formfields/ArrayField.tsx +169 -0
  11. package/src/form/formfields/BusinessHoursField.tsx +204 -0
  12. package/src/form/formfields/CheckboxButtonsField.tsx +97 -0
  13. package/src/form/formfields/CheckboxField.tsx +118 -0
  14. package/src/form/formfields/ColorPickerField.tsx +59 -0
  15. package/src/form/formfields/ComboMultiSelect.tsx +290 -0
  16. package/src/form/formfields/ComboSelect.tsx +278 -0
  17. package/src/form/formfields/DatePickerField.tsx +89 -0
  18. package/src/form/formfields/DateRangePickerField.tsx +104 -0
  19. package/src/form/formfields/DynamicMultiSelect.tsx +189 -0
  20. package/src/form/formfields/DynamicSelect.tsx +187 -0
  21. package/src/form/formfields/Error.tsx +15 -0
  22. package/src/form/formfields/ErrorContextHandler.tsx +77 -0
  23. package/src/form/formfields/FileUploadField.tsx +196 -0
  24. package/src/form/formfields/IframeField.tsx +65 -0
  25. package/src/form/formfields/InputField.tsx +67 -0
  26. package/src/form/formfields/InputGroupField.tsx +44 -0
  27. package/src/form/formfields/MultipleSelectField.tsx +98 -0
  28. package/src/form/formfields/NumberField.tsx +61 -0
  29. package/src/form/formfields/PasswordField.tsx +93 -0
  30. package/src/form/formfields/PhoneNumberField.tsx +163 -0
  31. package/src/form/formfields/RadioField.tsx +104 -0
  32. package/src/form/formfields/RadioGroupComponent.tsx +94 -0
  33. package/src/form/formfields/RangeField.tsx +53 -0
  34. package/src/form/formfields/SelectField.tsx +82 -0
  35. package/src/form/formfields/SwitchField.tsx +131 -0
  36. package/src/form/formfields/TextAreaField.tsx +48 -0
  37. package/src/form/formfields/TimeField.tsx +53 -0
  38. package/src/form/formfields/Typeahead.tsx +211 -0
  39. package/src/form/formfields/TypeaheadMultiSelect.tsx +203 -0
  40. package/src/form/formfields/UrlField.tsx +53 -0
  41. package/src/form/hooks/useDynamicReducer.tsx +42 -0
  42. package/src/form/schema/CustomValidators.ts +63 -0
  43. package/src/form/schema/FormFieldSchema.ts +342 -0
  44. package/src/form/util/RenderFormField.tsx +149 -0
  45. package/src/form/util/RenderListOptions.tsx +424 -0
  46. package/src/form/util/index.ts +185 -0
  47. package/src/util/LoaderWithText.tsx +28 -0
  48. package/src/util/svg/HELPER_ICONS.ts +16 -0
  49. package/src/util/svg/SVGIcon.tsx +23 -0
  50. package/tsconfig.json +25 -0
@@ -0,0 +1,187 @@
1
+ import React, {useCallback, useContext, useEffect, useMemo, useRef, useState} from "react";
2
+ import {
3
+ FieldOptionsSchema,
4
+ FormFieldComponentPropSchema,
5
+ FormFieldType,
6
+ OutputFormatType,
7
+ StoreStateSchema,
8
+ } from "../schema/FormFieldSchema";
9
+ import {RegisterOptions} from "react-hook-form";
10
+ import {FormContext} from "../context/FormContext";
11
+ import {Listbox, ListboxButton} from "@headlessui/react";
12
+ import {useDispatch} from "react-redux";
13
+ import {getListOptions} from "../FormFieldUtils";
14
+ import useDymanicReducer from "../hooks/useDynamicReducer";
15
+ import RenderFormField from "../util/RenderFormField";
16
+ import RenderListOptions, {renderListBoxValue,} from "../util/RenderListOptions";
17
+ import {handleChange, registerFormField} from "../util";
18
+ import {reachoAPI} from "../../api/index";
19
+ import axios from "axios";
20
+
21
+ const DynamicSelect: React.FC<FormFieldComponentPropSchema> = (
22
+ props: FormFieldComponentPropSchema
23
+ ) => {
24
+ const dynamicSelectRef = useRef<HTMLUListElement>(null);
25
+
26
+ const formContext = useContext(FormContext);
27
+
28
+ const [listOptions, setListOptions] = useState<FieldOptionsSchema[]>([]);
29
+ const [loading, setLoading] = useState<boolean>(false);
30
+
31
+ useMemo(() => {
32
+ const {fieldConfig} = props;
33
+
34
+ // Initialize dropdownFieldConfig with defaults if not already set
35
+ fieldConfig.dropdownFieldConfig = {
36
+ showCreateOption:
37
+ fieldConfig.dropdownFieldConfig?.showCreateOption ?? false,
38
+ showDeleteOption:
39
+ fieldConfig.dropdownFieldConfig?.showDeleteOption ?? true,
40
+ showSearchBox:
41
+ fieldConfig.dropdownFieldConfig?.showSearchBox ?? true,
42
+ showSelectedCount:
43
+ fieldConfig.dropdownFieldConfig?.showSelectedCount ?? true,
44
+ };
45
+ if (
46
+ !formContext.getValues(props.fieldConfig.name) &&
47
+ props.fieldConfig.defaultValue
48
+ ) {
49
+ formContext.setValue(
50
+ props.fieldConfig.name,
51
+ props.fieldConfig.defaultValue
52
+ );
53
+ }
54
+ }, [props.fieldConfig]);
55
+
56
+ if (props.fieldConfig.store) {
57
+ // Get the state from the reducer
58
+ const {state} = useDymanicReducer<StoreStateSchema<any>>({
59
+ reducerConfig: props.fieldConfig.store,
60
+ });
61
+
62
+ const dispatch = useDispatch();
63
+
64
+ // Memoize the listOptions and loading state derived from state.data
65
+ const {listOptions, isLoading} = useMemo(() => {
66
+ const options = state && state.data && Array.isArray(state.data)
67
+ ? getListOptions(state.data, props.fieldConfig.optionsConfig)
68
+ : [];
69
+ const loading = state && state.loading;
70
+ return {listOptions: options, isLoading: loading};
71
+ }, [state, props.fieldConfig.optionsConfig]);
72
+
73
+ useEffect(() => {
74
+ // Dispatch the initial fetch action if needed
75
+ if (props.fieldConfig.store?.initialFetchReducerAction && (!state || !state.data)) {
76
+ dispatch(props.fieldConfig.store.initialFetchReducerAction(props.fieldConfig.customUrlForRedux ? props.fieldConfig.customUrlForRedux : ''));
77
+ }
78
+
79
+ // Set the listOptions and loading state when the state changes
80
+ setListOptions(listOptions);
81
+ setLoading(isLoading);
82
+ }, [isLoading]);
83
+ }
84
+ useEffect(() => {
85
+ // Fetch data when fetchUrl is present
86
+ if (props.fieldConfig.fetchUrl) {
87
+ fetchData();
88
+ }
89
+
90
+ // Set options only when they are available and valid
91
+ if (Array.isArray(props.fieldConfig.options) && props.fieldConfig.options.length > 0) {
92
+ let options = props.fieldConfig.options;
93
+ setListOptions(prevState => [...options, ...prevState]);
94
+ }
95
+ }, []);
96
+
97
+ const fetchData = useCallback(async () => {
98
+ if (!props.fieldConfig.fetchUrl) return;
99
+
100
+ try {
101
+ let response = await (props.fieldConfig.disableHeaderInFetch
102
+ ? axios.get(props.fieldConfig.fetchUrl)
103
+ : reachoAPI.get(props.fieldConfig.fetchUrl));
104
+
105
+ if (response.data) {
106
+ const data: FieldOptionsSchema[] = getListOptions(response.data, props.fieldConfig.optionsConfig);
107
+ setListOptions(prev => [...prev, ...data]);
108
+
109
+ if (props.fieldConfig.fetchCallback) {
110
+ props.fieldConfig.fetchCallback(response);
111
+ }
112
+ } else {
113
+ console.error(response.statusText);
114
+ setLoading(false);
115
+ }
116
+ } catch (error) {
117
+ console.error('Fetch error:', error);
118
+ setLoading(false);
119
+ }
120
+ }, [props.fieldConfig.fetchUrl, props.fieldConfig.optionsConfig, props.fieldConfig.fetchCallback, props.fieldConfig.disableHeaderInFetch]);
121
+
122
+ let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
123
+ const hookProps = useMemo(() => formContext.register(props.fieldConfig.name, registerOptions), [formContext, props.fieldConfig.name, registerOptions]);
124
+
125
+ function getInput() {
126
+ return (
127
+ <Listbox
128
+ as={"div"}
129
+ {...hookProps}
130
+ className={`relative form-listbox flex-1`}
131
+ value={
132
+ formContext.getValues(props.fieldConfig.name)
133
+ ? props.fieldConfig.outputFormat ===
134
+ OutputFormatType.ARRAY
135
+ ? formContext.getValues(props.fieldConfig.name)[0]
136
+ : formContext.getValues(props.fieldConfig.name)
137
+ : undefined
138
+ }
139
+ onChange={(val) => {
140
+ const currentValue = formContext.getValues(props.fieldConfig.name);
141
+
142
+ // If the value matches, set it to null, otherwise set it to val
143
+ const newValue = currentValue === val ? null : val;
144
+
145
+ handleChange(newValue, formContext, props.fieldConfig, props.onChange);
146
+ }}
147
+ defaultValue={props.fieldConfig.defaultValue}
148
+ name={props.fieldConfig.name}
149
+ disabled={props.fieldConfig.disabled}>
150
+ <ListboxButton
151
+ className={
152
+ props.fieldConfig.customClassNames?.fieldClassName
153
+ ? "form-listbox-select " +
154
+ props.fieldConfig.customClassNames.fieldClassName
155
+ : "form-listbox-select"
156
+ }>
157
+ {renderListBoxValue(
158
+ formContext,
159
+ props.fieldConfig,
160
+ listOptions,
161
+ props.onChange
162
+ )}
163
+ </ListboxButton>
164
+ <RenderListOptions
165
+ formContext={formContext}
166
+ onChange={props.onChange}
167
+ formField={FormFieldType.DYNAMIC_SELECT}
168
+ ref={dynamicSelectRef}
169
+ fieldConfig={props.fieldConfig}
170
+ listOptions={listOptions}
171
+ setListOptions={setListOptions}
172
+ loading={loading}
173
+ setLoading={setLoading}
174
+ />
175
+ </Listbox>
176
+ );
177
+ }
178
+ if(props.fieldConfig.hideWhenNoResults && listOptions.length==0 )
179
+ {
180
+ return <></>
181
+ }
182
+
183
+ return (
184
+ <RenderFormField fieldConfig={props.fieldConfig} getInput={getInput}/>
185
+ );
186
+ };
187
+ export default DynamicSelect;
@@ -0,0 +1,15 @@
1
+ import React, { useContext, useEffect } from 'react';
2
+
3
+ const Error = ({ type, patternMessage }: { type: any; patternMessage?: string | undefined }) => {
4
+ return (
5
+ <>
6
+ {type === 'required' && <span className="field-error">This field is required</span>}
7
+ {type === 'minLength' && <span className="field-error">This is invalid field</span>}
8
+ {type === 'maxLength' && <span className="field-error">This field cannot exceed characters</span>}
9
+ {type === 'min' && <span className="field-error">This is invalid field</span>}
10
+ {type === 'pattern' && <span className="field-error">{patternMessage}</span>}
11
+ {type === 'validate' && <span className="field-error">{patternMessage}</span>}
12
+ </>
13
+ );
14
+ };
15
+ export default Error;
@@ -0,0 +1,77 @@
1
+ import React, { ContextType, useContext } from "react";
2
+ import Error from "./Error";
3
+ import { FormFieldSchema } from "../schema/FormFieldSchema";
4
+ import { FormContext } from "../context/FormContext";
5
+
6
+ export interface ErrorContextProps {
7
+ fieldConfig: FormFieldSchema;
8
+ }
9
+
10
+ export default function ErrorContextHandler({
11
+ fieldConfig,
12
+ }: ErrorContextProps) {
13
+ const formContext = useContext(FormContext);
14
+
15
+ const getPatternMessage = () => {
16
+ try {
17
+ if (
18
+ formContext.getFieldState &&
19
+ formContext.getFieldState(fieldConfig.name) &&
20
+ formContext.getFieldState(fieldConfig.name).error?.type === "validate"
21
+ ) {
22
+ return formContext.getFieldState(fieldConfig.name).error?.message;
23
+ }
24
+ return (
25
+ fieldConfig &&
26
+ fieldConfig.formFieldPattern &&
27
+ (fieldConfig.errorMessage ||
28
+ fieldConfig.formFieldPattern[0].getMessage())
29
+ );
30
+ } catch (error) {}
31
+ return;
32
+ };
33
+
34
+ // for object fields
35
+ function getError() {
36
+ const errorMessage: JSX.Element[] = [];
37
+ fieldConfig.children?.map((field) => {
38
+ if (formContext.errors && formContext.errors[field.name]) {
39
+ errorMessage.push(<ErrorContextHandler fieldConfig={field} />);
40
+ }
41
+ });
42
+
43
+ return <>{errorMessage.length > 0 && errorMessage[0]}</>;
44
+ }
45
+
46
+ const checkError = () => {
47
+ let error: JSX.Element | null = null;
48
+
49
+ formContext.errors &&
50
+ formContext.errors[fieldConfig.name] &&
51
+ formContext.errors[fieldConfig.name]?.type &&
52
+ (error = (
53
+ <Error
54
+ type={formContext.errors[fieldConfig.name]?.type}
55
+ patternMessage={getPatternMessage()}
56
+ ></Error>
57
+ ));
58
+
59
+ /**
60
+ * Checks for Nested Oject Array
61
+ */
62
+ if (error === null) {
63
+ formContext.getFieldState &&
64
+ formContext.getFieldState(fieldConfig.name).error &&
65
+ (error = (
66
+ <Error
67
+ type={formContext.getFieldState(fieldConfig.name).error?.type}
68
+ patternMessage={getPatternMessage()}
69
+ ></Error>
70
+ ));
71
+ }
72
+
73
+ return error;
74
+ };
75
+
76
+ return <>{checkError()}</>;
77
+ }
@@ -0,0 +1,196 @@
1
+ import { useContext, useState } from "react";
2
+ import { RegisterOptions } from "react-hook-form";
3
+ import { FormContext } from "../context/FormContext";
4
+ import {
5
+ FormFieldComponentPropSchema,
6
+ FormFieldPatternsImpl,
7
+ } from "../schema/FormFieldSchema";
8
+ import { Description } from "@headlessui/react";
9
+ import React from "react";
10
+ import RenderFormField from "../util/RenderFormField";
11
+ import clsx from "clsx";
12
+ const FileUploadField: React.FC<FormFieldComponentPropSchema> = (
13
+ props: FormFieldComponentPropSchema
14
+ ) => {
15
+ const formContext = useContext(FormContext);
16
+ const [file, setFile] = useState<File | null>(
17
+ formContext.getValues(props.fieldConfig.name)
18
+ ? formContext.getValues(props.fieldConfig.name)
19
+ : null
20
+ );
21
+ const [loading, setLoading] = useState<boolean>(false);
22
+ const [isDragging, setIsDragging] = useState<boolean>(false);
23
+
24
+ let registerOptions: RegisterOptions = {
25
+ required: props.fieldConfig.required
26
+ ? FormFieldPatternsImpl.REQUIRED.getMessage()
27
+ : false,
28
+ };
29
+
30
+ if (props.fieldConfig?.formFieldPattern) {
31
+ registerOptions.pattern =
32
+ props.fieldConfig?.formFieldPattern[0].getPattern();
33
+ }
34
+
35
+ let hookprops = formContext.register(
36
+ props.fieldConfig.name,
37
+ registerOptions
38
+ );
39
+
40
+ const handleDragOver = (e: React.DragEvent<HTMLLabelElement>) => {
41
+ e.preventDefault();
42
+ setIsDragging(true);
43
+ };
44
+
45
+ const handleDragLeave = () => {
46
+ setIsDragging(false);
47
+ };
48
+
49
+ const handleDrop = (e: React.DragEvent<HTMLLabelElement>) => {
50
+ e.preventDefault();
51
+ setIsDragging(false);
52
+ const files = e.dataTransfer.files;
53
+ if (files && files.length > 0) {
54
+ handleChange(files);
55
+ }
56
+ };
57
+
58
+ const handleChange = async (_fileList: FileList | null) => {
59
+ setLoading(true);
60
+ if (_fileList && _fileList.length) {
61
+ const _file: File = _fileList[0];
62
+ setFile(_file);
63
+ formContext.setValue(props.fieldConfig.name, _file, {
64
+ shouldValidate: true,
65
+ shouldDirty: formContext.getFieldState(props.fieldConfig.name)
66
+ .isDirty,
67
+ shouldTouch: formContext.getFieldState(props.fieldConfig.name)
68
+ .isTouched,
69
+ });
70
+ setTimeout(() => {
71
+ setLoading(false);
72
+ }, 1000);
73
+ if (props.fieldConfig.submitOnChange && formContext.onSubmit)
74
+ formContext.onSubmit();
75
+ if (props.onChange) props.onChange(_file);
76
+ if (props.fieldConfig.onChange) props.fieldConfig.onChange(_file);
77
+ }
78
+ };
79
+
80
+ const isImageFile = (file: any) => {
81
+ if (!file) return false;
82
+ const validImageTypes = ["image/jpeg", "image/jpg", "image/png"];
83
+ return validImageTypes.includes(file.type);
84
+ };
85
+
86
+ const getInput = () => {
87
+ return (
88
+ <label
89
+ htmlFor="dropzone-file"
90
+ className={clsx(
91
+ props.fieldConfig.customClassNames?.wrapperClassName ||
92
+ "flex flex-col items-center justify-center w-full h-64 border-2 border-dashed rounded-lg cursor-pointer",
93
+ isDragging
94
+ ? "border-blue-500 bg-blue-100"
95
+ : "border-gray-300 bg-gray-50 hover:bg-gray-100"
96
+ )}
97
+ onDragOver={handleDragOver}
98
+ onDragLeave={handleDragLeave}
99
+ onDrop={handleDrop}
100
+ >
101
+
102
+ {loading ? (
103
+ <Description>Loading...</Description>
104
+ ) : (
105
+ props.fieldConfig.fieldContainer ? <props.fieldConfig.fieldContainer /> :
106
+ <div className={"flex flex-col items-center justify-center pt-5 pb-6"}>
107
+ {file ? (
108
+ <>
109
+ {isImageFile(file) ? (
110
+ <div className="flex">
111
+ <img
112
+ src={URL.createObjectURL(
113
+ file
114
+ )}
115
+ alt="Preview"
116
+ className="m-2"
117
+ style={{
118
+ maxWidth: "50px",
119
+ maxHeight: "50px",
120
+ }}
121
+ />
122
+ <span className="m-2 ml-2">
123
+ {file?.name}
124
+ </span>
125
+ </div>
126
+ ) : (
127
+ <div className="flex">
128
+ <span className="m-2">
129
+ {file?.name}
130
+ </span>{" "}
131
+ <button
132
+ onClick={(e) => {
133
+ e.preventDefault();
134
+ formContext.setValue(
135
+ props.fieldConfig.name,cnull
136
+ );
137
+ setFile(null);
138
+ }}
139
+ className="btn btn-outline-dark btn-sm ">
140
+ X
141
+ </button>
142
+ </div>
143
+ )}
144
+ </>
145
+ ) : (
146
+ <>
147
+ <svg
148
+ className="w-8 h-8 mb-4 text-gray-500 dark:text-gray-400"
149
+ aria-hidden="true"
150
+ xmlns="http://www.w3.org/2000/svg"
151
+ fill="none"
152
+ viewBox="0 0 20 16">
153
+ <path
154
+ stroke="currentColor"
155
+ strokeLinecap="round"
156
+ strokeLinejoin="round"
157
+ strokeWidth="2"
158
+ d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"
159
+ />
160
+ </svg>
161
+ <p className="mb-2 text-sm text-gray-500 dark:text-gray-400">
162
+ <span className="font-semibold">
163
+ Click to upload or drag and drop
164
+ </span>
165
+ </p>
166
+ <p className="text-xs text-gray-500 dark:text-gray-400">
167
+ {props.fieldConfig.placeholder
168
+ ? props.fieldConfig.placeholder
169
+ : "-- placeholder --"}
170
+ </p>
171
+ </>
172
+ )}
173
+ </div>
174
+ )}
175
+ <input
176
+ id="dropzone-file"
177
+ type="file"
178
+ className="hidden"
179
+ accept={
180
+ props.fieldConfig.fileAccept
181
+ ? props.fieldConfig.fileAccept
182
+ : ""
183
+ }
184
+ onChange={(e) => handleChange(e.target.files)}
185
+ disabled={props.fieldConfig.readOnly}
186
+ />
187
+ </label>
188
+ );
189
+ };
190
+
191
+ return (
192
+ <RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
193
+ );
194
+ };
195
+
196
+ export default FileUploadField;
@@ -0,0 +1,65 @@
1
+ import { RegisterOptions } from "react-hook-form";
2
+ import { FormFieldComponentPropSchema } from "../schema/FormFieldSchema";
3
+ import { useContext, useEffect, useRef } from "react";
4
+ import { FormContext } from "../context/FormContext";
5
+ import RenderFormField from "../util/RenderFormField";
6
+ import { handleChange, registerFormField } from "../util";
7
+
8
+ const IframeField: 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
+ const iframe = useRef<HTMLIFrameElement>(null);
18
+
19
+ useEffect(() => {
20
+ if (iframe.current) {
21
+ const iframeDocument =
22
+ iframe.current.contentDocument ||
23
+ iframe.current.contentWindow?.document;
24
+ if (iframeDocument) {
25
+ iframeDocument.open();
26
+ iframeDocument.write(
27
+ "<!DOCTYPE html><html><head></head><body></body></html>"
28
+ );
29
+ iframeDocument.close();
30
+
31
+ const div = iframeDocument.createElement("div");
32
+ div.className = "iframe-content";
33
+ div.innerHTML = formContext.getValues(props.fieldConfig.name) || "";
34
+
35
+ div.setAttribute(
36
+ "contenteditable",
37
+ props.fieldConfig.readOnly ? "false" : "true"
38
+ );
39
+
40
+ div.oninput = (e: Event) => {
41
+ const value = (e.target as HTMLDivElement).innerHTML;
42
+ handleChange(value, formContext, props.fieldConfig, props.onChange);
43
+ };
44
+
45
+ iframeDocument.body.appendChild(div);
46
+ }
47
+ }
48
+ }, [props.fieldConfig.name, props.fieldConfig.readOnly, props.onChange]);
49
+
50
+ const getInput = () => {
51
+ return (
52
+ <iframe
53
+ ref={iframe}
54
+ className={`form-input ${
55
+ props.fieldConfig.customClassNames?.fieldClassName || "flex-1"
56
+ }`}
57
+ ></iframe>
58
+ );
59
+ };
60
+
61
+ return (
62
+ <RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
63
+ );
64
+ };
65
+ export default IframeField;
@@ -0,0 +1,67 @@
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 } from "@headlessui/react";
8
+
9
+ const InputField: 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(
17
+ props.fieldConfig.name,
18
+ registerOptions
19
+ );
20
+
21
+ /**
22
+ * Raw Form Field
23
+ */
24
+ const getInput = () => {
25
+ return (
26
+ <Input
27
+ {...hookProps}
28
+ // ref={props.fieldConfig.ref}
29
+ type="text"
30
+ maxLength={props.fieldConfig?.maxLength}
31
+ minLength={props.fieldConfig?.minLength}
32
+ placeholder={props.fieldConfig?.placeholder}
33
+ readOnly={props.fieldConfig?.readOnly}
34
+ disabled={props.fieldConfig?.disabled}
35
+ autoComplete={props.fieldConfig?.autoComplete}
36
+ defaultValue={props.fieldConfig.defaultValue as string}
37
+ className={`form-input ${
38
+ props.fieldConfig.customClassNames?.fieldClassName ||
39
+ "flex-1"
40
+ }`}
41
+ onKeyDown={(e) => {
42
+ const inputElement = e.target as HTMLInputElement;
43
+ const { selectionStart, value } = inputElement;
44
+
45
+ if (e.key === " " && selectionStart === 0) {
46
+ e.preventDefault();
47
+ }
48
+ props.fieldConfig.onKeyDown &&
49
+ props.fieldConfig.onKeyDown(e);
50
+ }}
51
+ onChange={(e) => {
52
+ handleChange(
53
+ e.target.value,
54
+ formContext,
55
+ props.fieldConfig,
56
+ props.onChange
57
+ );
58
+ }}
59
+ />
60
+ );
61
+ };
62
+
63
+ return (
64
+ <RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
65
+ );
66
+ };
67
+ export default InputField;
@@ -0,0 +1,44 @@
1
+ import React, { useContext } from "react";
2
+ import FormField from "../FormField";
3
+ import { FormContext } from "../context/FormContext";
4
+ import { FormFieldComponentPropSchema } from "../schema/FormFieldSchema";
5
+ import RenderFormField from "../util/RenderFormField";
6
+
7
+ const InputGroupField: React.FC<FormFieldComponentPropSchema> = (
8
+ props: FormFieldComponentPropSchema
9
+ ) => {
10
+ function getInput() {
11
+ return (
12
+ <>
13
+ {props.fieldConfig.children?.map((child, index: number) => {
14
+ child.disableDefaultWrapper = true;
15
+ if (
16
+ !child.customClassNames ||
17
+ child.customClassNames?.fieldClassName === undefined ||
18
+ child.customClassNames?.fieldClassName === null ||
19
+ child.customClassNames?.fieldClassName === ""
20
+ ) {
21
+ child.customClassNames = {};
22
+ if (props.fieldConfig.children) {
23
+ if (index === 0) {
24
+ child.customClassNames.fieldClassName =
25
+ "input-group-first-field";
26
+ } else if (index === props.fieldConfig.children?.length - 1) {
27
+ child.customClassNames.fieldClassName = "input-group-end-field";
28
+ } else {
29
+ child.customClassNames.fieldClassName =
30
+ "input-group-middle-field";
31
+ }
32
+ }
33
+ }
34
+ return <FormField fieldConfig={child}></FormField>;
35
+ })}
36
+ </>
37
+ );
38
+ }
39
+
40
+ return (
41
+ <RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
42
+ );
43
+ };
44
+ export default InputGroupField;