@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,118 @@
1
+ import { RegisterOptions } from "react-hook-form";
2
+ import { useContext, useEffect, useState } from "react";
3
+ import { FormContext } from "../context/FormContext";
4
+ import { FormFieldComponentPropSchema } from "../schema/FormFieldSchema";
5
+ import { Checkbox, Field, Input, Label } from "@headlessui/react";
6
+ import React from "react";
7
+ import RenderFormField from "../util/RenderFormField";
8
+ import { handleChange, registerFormField } from "../util";
9
+ const CheckboxField: React.FC<FormFieldComponentPropSchema> = (
10
+ props: FormFieldComponentPropSchema
11
+ ) => {
12
+ const formContext = useContext(FormContext);
13
+ const [selectedOption, setSelectedOption] = useState<string[]>(
14
+ formContext.getValues(props.fieldConfig.name)
15
+ ? formContext.getValues(props.fieldConfig.name)
16
+ : []
17
+ );
18
+ const [isChecked, setIsChecked] = useState<boolean>(
19
+ formContext.getValues(props.fieldConfig.name)
20
+ ? formContext.getValues(props.fieldConfig.name)
21
+ : props.fieldConfig.defaultValue
22
+ );
23
+ useEffect(() => {
24
+ if (formContext.getValues(props.fieldConfig.name)) {
25
+ setSelectedOption(formContext.getValues(props.fieldConfig.name));
26
+ }
27
+ }, []);
28
+ let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
29
+ let hookProps = formContext.register(props.fieldConfig.name, registerOptions);
30
+ const handleInput = (option: string) => {
31
+ let tempData: string[] = selectedOption;
32
+ if (selectedOption.includes(option)) {
33
+ tempData = tempData.filter((opt) => opt !== option);
34
+ setSelectedOption([...tempData]);
35
+ } else {
36
+ tempData.push(option);
37
+ setSelectedOption([...tempData]);
38
+ }
39
+ handleChange(tempData, formContext, props.fieldConfig, props.onChange);
40
+ };
41
+ function getInput() {
42
+ return props.fieldConfig.options ? (
43
+ props.fieldConfig.options.map((option, index) => {
44
+ return props.fieldConfig.fieldOptionWrapper ? (
45
+ <props.fieldConfig.fieldOptionWrapper data={option} />
46
+ ) : (
47
+ <Field
48
+ key={option.value + index}
49
+ className={
50
+ props.fieldConfig.customClassNames?.fieldClassName
51
+ ? `form-check ${props.fieldConfig.customClassNames.fieldClassName}`
52
+ : "form-check"
53
+ }
54
+ disabled={option.isDisabled}>
55
+ <Checkbox
56
+ disabled={option.isDisabled}
57
+ value={option.value}
58
+ id={option.value}
59
+ checked={selectedOption.includes(option.value)}
60
+ onChange={() => handleInput(option.value)}
61
+ >
62
+ {({ checked, disabled }) => (
63
+ <Input
64
+ disabled={disabled}
65
+ className="form-checkbox cursor-pointer"
66
+ id={option.value}
67
+ type="checkbox"
68
+ checked={checked}
69
+ />
70
+ )}
71
+ </Checkbox>
72
+ <Label className='form-check-label' htmlFor={option.value}>{option.label}</Label>
73
+ </Field>
74
+ )
75
+ })
76
+ ) : (
77
+ <Field
78
+ className={
79
+ props.fieldConfig.customClassNames?.fieldClassName
80
+ ? `form-check ${props.fieldConfig.customClassNames.fieldClassName}`
81
+ : "form-check"
82
+ }
83
+ >
84
+ <Checkbox
85
+ disabled={props.fieldConfig.readOnly}
86
+ {...hookProps}
87
+ checked={isChecked}
88
+ onChange={(checked) => {
89
+ handleChange(
90
+ checked,
91
+ formContext,
92
+ props.fieldConfig,
93
+ props.onChange
94
+ );
95
+ setIsChecked(checked);
96
+ }}
97
+ >
98
+ {({ checked, disabled }) => (
99
+ <Input
100
+ disabled={disabled}
101
+ className="form-checkbox cursor-pointer"
102
+ id={props.fieldConfig.name}
103
+ type="checkbox"
104
+ checked={checked}
105
+ />
106
+ )}
107
+ </Checkbox>
108
+ <Label className="form-check-label inline-label" htmlFor={props.fieldConfig.name}>
109
+ {props.fieldConfig.label}
110
+ </Label>
111
+ </Field>
112
+ );
113
+ }
114
+ return (
115
+ <RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
116
+ );
117
+ };
118
+ export default CheckboxField;
@@ -0,0 +1,59 @@
1
+ import { useContext, useState } 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 { Field } from "@headlessui/react";
7
+ import RenderFormField from "../util/RenderFormField";
8
+ import { handleChange, registerFormField } from "../util";
9
+
10
+ export const ColorPickerField: React.FC<FormFieldComponentPropSchema> = (
11
+ props: FormFieldComponentPropSchema
12
+ ) => {
13
+ const formContext = useContext(FormContext);
14
+
15
+ let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
16
+
17
+ formContext.register(props.fieldConfig.name, registerOptions);
18
+
19
+ const getInput = () => {
20
+ return (
21
+ <Field className="flex" disabled={props.fieldConfig.disabled}>
22
+ <input
23
+ type="color"
24
+ onChange={(e) =>
25
+ handleChange(
26
+ e.target.value,
27
+ formContext,
28
+ props.fieldConfig,
29
+ props.onChange
30
+ )
31
+ }
32
+ defaultValue={props.fieldConfig.defaultValue as string}
33
+ disabled={props.fieldConfig.readOnly}
34
+ value={formContext.getValues(props.fieldConfig.name) ?? ""}
35
+ className="inline-flex w-[30%] h-auto rounded-l-md border border-r-0 border-gray-300 px-2 cursor-pointer"></input>
36
+ <input
37
+ type="text"
38
+ value={formContext.getValues(props.fieldConfig.name) ?? ""}
39
+ disabled={props.fieldConfig.readOnly}
40
+ defaultValue={props.fieldConfig.defaultValue as string}
41
+ maxLength={7}
42
+ onChange={(e) =>
43
+ handleChange(
44
+ e.target.value,
45
+ formContext,
46
+ props.fieldConfig,
47
+ props.onChange
48
+ )
49
+ }
50
+ className="flex-1 form-input rounded-s-none"
51
+ />
52
+ </Field>
53
+ );
54
+ };
55
+
56
+ return (
57
+ <RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
58
+ );
59
+ };
@@ -0,0 +1,290 @@
1
+ // import { Combobox, Listbox, Transition } from '@headlessui/react';
2
+ // import { ChevronDownIcon, CheckIcon } from '@heroicons/react/20/solid';
3
+ // import React, { Fragment, useContext, useEffect, useState } from 'react';
4
+ // import { RegisterOptions } from 'react-hook-form';
5
+ // import { useDispatch } from 'react-redux';
6
+ // import { reachoAPI } from '../../../../api';
7
+ // import useDymanicReducer from '../../../hooks/useDymamicReducer';
8
+ // import { StoreStateSchema } from '../../../schema/StorageSchemas';
9
+ // import { LoaderWithText } from '../../Loader';
10
+ // import { getListOptions } from '../FormFieldUtils';
11
+ // import { FormContext } from '../context/FormContext';
12
+ // import { FormFieldComponentPropSchema, FieldOptionsSchema, FormFieldPatternsImpl, FieldAlignType } from '../schema/FormFieldSchema';
13
+ // import ErrorContextHandler from './ErrorContextHandler';
14
+ // import { ChevronUpDownIcon, PlusIcon } from '@heroicons/react/16/solid';
15
+ // import { CancelTokenSource } from 'axios';
16
+ // import { usePopper } from 'react-popper';
17
+
18
+ import React from "react";
19
+ import { FormFieldComponentPropSchema } from "../schema/FormFieldSchema";
20
+
21
+ const ComboMultiSelect: React.FC<FormFieldComponentPropSchema> = (
22
+ props: FormFieldComponentPropSchema
23
+ ) => {
24
+ // const formContext = useContext(FormContext);
25
+
26
+ // const [selectedOption, setSelectedOption] = useState<string[]>(formContext.getValues(props.fieldConfig.name) ? formContext.getValues(props.fieldConfig.name) : []);
27
+ // const [listOptions, setListOptions] = useState<FieldOptionsSchema[]>([]);
28
+ // const [loading, setLoading] = useState<boolean>(false);
29
+ // const [query, setQuery] = useState<string>('');
30
+ // const [cancelToken, setCancelToken] = useState<CancelTokenSource | undefined>(undefined);
31
+
32
+ // let [referenceElement, setReferenceElement] = useState<any>();
33
+ // let [popperElement, setPopperElement] = useState<any>();
34
+ // let { styles, attributes } = usePopper(referenceElement, popperElement, {
35
+ // placement: 'auto-start',
36
+ // });
37
+
38
+ // if (props.fieldConfig.fetchUrl) {
39
+ // useEffect(() => {
40
+ // fetchData();
41
+ // }, []);
42
+ // }
43
+
44
+ // const fetchData = async () => {
45
+ // try {
46
+ // if (!props.fieldConfig.fetchUrl) return;
47
+
48
+ // let response = await reachoAPI.get(props.fieldConfig.fetchUrl);
49
+ // if (response.data) {
50
+ // const data: FieldOptionsSchema[] = getListOptions(response.data.products, props.fieldConfig.optionsConfig);
51
+ // setListOptions([...data]);
52
+ // }
53
+ // } catch (err) {
54
+ // } finally {
55
+ // setLoading(false);
56
+ // }
57
+ // };
58
+
59
+ // const createItem = async () => {
60
+ // setLoading(true);
61
+ // try {
62
+ // if (!props.fieldConfig.postUrl) {
63
+ // const data: FieldOptionsSchema = {
64
+ // label: query,
65
+ // value: query,
66
+ // };
67
+ // setListOptions([...listOptions, data]);
68
+ // handleSelect(query);
69
+ // return;
70
+ // }
71
+ // } catch (e: any) {
72
+ // console.error('Error in Creating the object ', e);
73
+ // } finally {
74
+ // setQuery('');
75
+ // setLoading(false);
76
+ // }
77
+ // };
78
+
79
+ // const getSelectedOption = () => {
80
+ // let value: string | undefined = formContext.getValues(props.fieldConfig.name) + '';
81
+ // if (value == '' || value === undefined) {
82
+ // value = props.fieldConfig.defaultValue ? props.fieldConfig.defaultValue : undefined;
83
+ // }
84
+ // return listOptions.find((option) => option.value == value)?.label as string;
85
+ // };
86
+ // const handleSelect = (option: string) => {
87
+ // let tempData: string[] = selectedOption;
88
+ // if (selectedOption.includes(option)) {
89
+ // tempData = tempData.filter((opt) => opt != option);
90
+ // setSelectedOption([...tempData]);
91
+ // } else {
92
+ // tempData.push(option);
93
+ // setSelectedOption([...tempData]);
94
+ // }
95
+ // formContext.setValue(props.fieldConfig.name, tempData, {
96
+ // shouldValidate: true,
97
+ // shouldDirty: formContext.getFieldState(props.fieldConfig.name).isDirty,
98
+ // shouldTouch: formContext.getFieldState(props.fieldConfig.name).isTouched,
99
+ // });
100
+
101
+ // if (props.onChange) props.onChange(tempData);
102
+ // };
103
+
104
+ // let registerOptions: RegisterOptions = { required: props.fieldConfig.required ? FormFieldPatternsImpl.REQUIRED.getMessage() : false };
105
+ // if (props.fieldConfig?.formFieldPattern) {
106
+ // registerOptions.pattern = props.fieldConfig?.formFieldPattern[0].getPattern();
107
+ // }
108
+ // formContext.register(props.fieldConfig.name, registerOptions);
109
+
110
+ // const filteredPeople = query && query != '' ? listOptions.filter((item) => item.label.toLowerCase().replace(/\s+/g, '').includes(query.toLowerCase().replace(/\s+/g, ''))) : listOptions;
111
+
112
+ // const allTheOptions = () => {
113
+ // return selectedOption.length > 0 ? (
114
+ // selectedOption.length === 1 ? (
115
+ // <span className="form-selected-badge">
116
+ // <span className="form-selected-badge-name">{listOptions.find((option) => option.value === selectedOption[0])?.label}</span>
117
+ // <button
118
+ // type="button"
119
+ // className="group relative -mr-1 h-3.5 w-3.5 rounded-sm hover:bg-gray-500/20"
120
+ // onClick={(e) => {
121
+ // e.preventDefault();
122
+ // setSelectedOption(selectedOption.filter((option) => option !== selectedOption[0]));
123
+ // }}
124
+ // >
125
+ // <span className="sr-only">Remove</span>
126
+ // <svg viewBox="0 0 14 14" className="h-3.5 w-3.5 stroke-gray-600/50 group-hover:stroke-gray-600/75">
127
+ // <path d="M4 4l6 6m0-6l-6 6" />
128
+ // </svg>
129
+ // <span className="absolute -inset-1" />
130
+ // </button>
131
+ // </span>
132
+ // ) : (
133
+ // <span className="form-selected-badge">
134
+ // <span className="form-selected-badge-name">{`${selectedOption.length} selected`}</span>
135
+ // <button
136
+ // type="button"
137
+ // className="group relative -mr-1 h-3.5 w-3.5 rounded-sm hover:bg-gray-500/20"
138
+ // onClick={(e) => {
139
+ // e.preventDefault();
140
+ // setSelectedOption([]);
141
+ // }}
142
+ // >
143
+ // <span className="sr-only">Remove all</span>
144
+ // <svg viewBox="0 0 14 14" className="h-3.5 w-3.5 stroke-gray-600/50 group-hover:stroke-gray-600/75">
145
+ // <path d="M4 4l6 6m0-6l-6 6" />
146
+ // </svg>
147
+ // <span className="absolute -inset-1" />
148
+ // </button>
149
+ // </span>
150
+ // )
151
+ // ) : null;
152
+ // };
153
+
154
+ // function getInput() {
155
+ // return (
156
+ // <Combobox
157
+ // value={formContext.getValues(props.fieldConfig.name) == '' ? undefined : formContext.getValues(props.fieldConfig.name)}
158
+ // name={props.fieldConfig.name}
159
+ // defaultValue={props.fieldConfig.defaultValue}
160
+ // multiple
161
+ // >
162
+ // <div className="relative form-combobox flex-1" ref={setReferenceElement}>
163
+ // <Combobox.Button className={props.fieldConfig.customClassName ? 'form-combobox-select ' + props.fieldConfig.customClassName : 'form-combobox-select w-full justify-between'}>
164
+ // <Combobox.Input
165
+ // className="form-input"
166
+ // placeholder={selectedOption.length <= 0 ? props.fieldConfig.placeholder || 'Select an option' : undefined}
167
+ // onChange={(event) => setQuery(event.target.value)}
168
+ // />
169
+ // {allTheOptions() && <div className="absolute ml-3">{allTheOptions()}</div>}
170
+ // <ChevronDownIcon className="h-5 w-5 text-gray-400 form-combobox-svg" aria-hidden="true" />
171
+ // </Combobox.Button>
172
+ // <Transition as={Fragment} leave="transition ease-in duration-100" leaveFrom="opacity-100" leaveTo="opacity-0" afterLeave={() => setQuery('')}>
173
+ // <Combobox.Options ref={setPopperElement} style={styles.popper} {...attributes.popper} className="form-combobox-options">
174
+ // {filteredPeople.length === 0 && query !== ''
175
+ // ? props.fieldConfig.enableCreateOption && (
176
+ // <Combobox.Option
177
+ // key={'create_new'}
178
+ // className={({ active }) => `form-combobox-option ${active ? 'bg-[#f6f6f6] text-primary' : 'text-gray-900'}`}
179
+ // value={'create'}
180
+ // onClick={() => createItem()}
181
+ // >
182
+ // {({ selected }) => (
183
+ // <>
184
+ // <div className="flex items-center justify-between">
185
+ // <span>{query}</span>
186
+ // <span className="text-success flex items-center gap-x-1">
187
+ // <PlusIcon className="h-3 w-3" />
188
+ // Create & select
189
+ // </span>
190
+ // </div>
191
+ // </>
192
+ // )}
193
+ // </Combobox.Option>
194
+ // )
195
+ // : filteredPeople.map((option) => (
196
+ // <Combobox.Option
197
+ // key={option.value}
198
+ // onClick={() => handleSelect(option.value)}
199
+ // disabled={option.isDisabled}
200
+ // className={({ active }) => `form-combobox-option ${active ? 'bg-[#f6f6f6] text-primary' : 'text-gray-900'}`}
201
+ // value={option.value}
202
+ // >
203
+ // {({ selected, active }) => (
204
+ // <>
205
+ // <span className={`block truncate ${selected ? 'font-medium' : 'font-normal'}`}>{option.label}</span>
206
+ // {selected ? (
207
+ // <span className="icon">
208
+ // <CheckIcon className="h-5 w-5" aria-hidden="true" />
209
+ // </span>
210
+ // ) : null}
211
+ // <p className="form-listbox-helptext">{option.helpText}</p>
212
+ // </>
213
+ // )}
214
+ // </Combobox.Option>
215
+ // ))}
216
+ // </Combobox.Options>
217
+ // </Transition>
218
+ // </div>
219
+ // </Combobox>
220
+ // );
221
+ // }
222
+
223
+ // function formTypeHorizontal() {
224
+ // return (
225
+ // <div className="form-group flex sm:flex-row flex-col">
226
+ // {props.fieldConfig.label ? (
227
+ // <label htmlFor="email" className="form-label sm:w-1/4 sm:ltr:mr-2 rtl:ml-2">
228
+ // {props.fieldConfig.label}
229
+ // </label>
230
+ // ) : (
231
+ // <></>
232
+ // )}
233
+ // <div className="flex-1">
234
+ // {getInput()}
235
+
236
+ // <ErrorContextHandler fieldConfig={props.fieldConfig} />
237
+
238
+ // {props.fieldConfig && props.fieldConfig.helpText ? (
239
+ // <p className="mt-2 text-sm text-gray-500" id="email-description">
240
+ // {props.fieldConfig.helpText}
241
+ // </p>
242
+ // ) : (
243
+ // <></>
244
+ // )}
245
+ // </div>
246
+ // </div>
247
+ // );
248
+ // }
249
+
250
+ // function formTypeVertical() {
251
+ // return (
252
+ // <div className="form-group">
253
+ // {props.fieldConfig.label ? (
254
+ // <label htmlFor="email" className="form-label">
255
+ // {props.fieldConfig.label}
256
+ // </label>
257
+ // ) : (
258
+ // <></>
259
+ // )}
260
+
261
+ // <div className="flex-1">
262
+ // {getInput()}
263
+
264
+ // <ErrorContextHandler fieldConfig={props.fieldConfig} />
265
+
266
+ // {props.fieldConfig && props.fieldConfig.helpText ? (
267
+ // <p className="mt-2 text-sm text-gray-500" id="email-description">
268
+ // {props.fieldConfig.helpText}
269
+ // </p>
270
+ // ) : (
271
+ // <></>
272
+ // )}
273
+ // </div>
274
+ // </div>
275
+ // );
276
+ // }
277
+
278
+ // return (
279
+ // <>
280
+ // {props.fieldConfig && props.fieldConfig.disableDefaultWrapper ? (
281
+ // <>{getInput()}</>
282
+ // ) : (
283
+ // <>{props.fieldConfig.align === FieldAlignType.HORIZONTAL ? <>{formTypeHorizontal()}</> : <>{formTypeVertical()}</>}</>
284
+ // )}
285
+ // </>
286
+ // );
287
+ return <></>;
288
+ };
289
+
290
+ export default ComboMultiSelect;