@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,185 @@
1
+ import {RegisterOptions} from "react-hook-form";
2
+ import {FormFieldPatternsImpl, FormFieldSchema, OutputFormatType,} from "../schema/FormFieldSchema";
3
+ import {FormContextType} from "../context/FormContext";
4
+
5
+ /**
6
+ * Registers form field options based on the provided configuration.
7
+ *
8
+ * This function takes a form field configuration object (`FormFieldSchema`)
9
+ * and returns `RegisterOptions` to be used with form validation libraries
10
+ * (such as React Hook Form).
11
+ *
12
+ * @param {FormFieldSchema} fieldConfig - The configuration object for the form field.
13
+ *
14
+ * @returns {RegisterOptions} An object containing validation options for the form field.
15
+ *
16
+ * @property {boolean|string} required - Indicates if the field is required.
17
+ * The value is a string with a validation message if required, otherwise `false`.
18
+ *
19
+ * @property {RegExp} [pattern] - A regular expression pattern to validate the field value.
20
+ *
21
+ * @property {Function} [validate] - A custom validation function.
22
+ *
23
+ * @property {number} [maxLength] - Specifies the maximum length of the field value.
24
+ *
25
+ * @property {number} [minLength] - Specifies the minimum length of the field value.
26
+ *
27
+ * @property {number} [min] - Specifies the minimum value for numeric fields.
28
+ *
29
+ * @property {number} [max] - Specifies the maximum value for numeric fields.
30
+ *
31
+ * @example
32
+ * const fieldConfig = {
33
+ * required: true,
34
+ * formFieldPattern: [
35
+ * FormFieldPatternImpl.URL
36
+ * ],
37
+ * maxLength: 10,
38
+ * minLength: 3,
39
+ * min: 1,
40
+ * max: 100,
41
+ * };
42
+ *
43
+ * USE CASE:
44
+ * const registerOptions = registerFormField(fieldConfig);
45
+ *
46
+ */
47
+ export const registerFormField = (
48
+ fieldConfig: FormFieldSchema,
49
+ ignoreAsArray?: boolean
50
+ ): RegisterOptions => {
51
+ let registerOptions: RegisterOptions = {
52
+ required: fieldConfig.required
53
+ ? FormFieldPatternsImpl.REQUIRED.getMessage()
54
+ : false,
55
+ };
56
+
57
+ if (fieldConfig?.validateSpaces) {
58
+ registerOptions.validate = {
59
+ notEmpty: (value: string) => value.trim() !== '' || 'This field cannot be empty or contain only spaces',
60
+ notSpaces: (value: string) => value.trim() !== '' || 'This field cannot be empty or contain only spaces',
61
+ };
62
+ }
63
+ if (fieldConfig?.formFieldPattern) {
64
+ if (fieldConfig.formFieldPattern[0].getValidate()) {
65
+ registerOptions.validate =
66
+ fieldConfig.formFieldPattern[0].getValidate();
67
+ } else {
68
+ registerOptions.pattern =
69
+ fieldConfig?.formFieldPattern[0].getPattern();
70
+ }
71
+ }
72
+
73
+ if (fieldConfig?.maxLength) {
74
+ registerOptions.maxLength = fieldConfig?.maxLength;
75
+ }
76
+
77
+ if (fieldConfig?.minLength) {
78
+ registerOptions.minLength = fieldConfig?.minLength;
79
+ }
80
+
81
+ if (fieldConfig?.min) {
82
+ registerOptions.min = fieldConfig?.min;
83
+ }
84
+
85
+ if (fieldConfig?.max) {
86
+ registerOptions.max = fieldConfig?.max;
87
+ }
88
+
89
+ if (
90
+ fieldConfig?.outputFormat === OutputFormatType.ARRAY &&
91
+ !ignoreAsArray
92
+ ) {
93
+ registerOptions.setValueAs = (value) =>
94
+ Array.isArray(value) ? [...value] : [value];
95
+ }
96
+
97
+ return registerOptions;
98
+ };
99
+
100
+ /**
101
+ * Handles changes to a form field, updating the form context and triggering
102
+ * any additional callbacks specified in the field configuration.
103
+ *
104
+ * @param {string} value - The new value of the form field.
105
+ * @param {FormContextType} formContext - The form context object, which includes methods
106
+ * and properties for managing form state (e.g., `setValue`, `getFieldState`).
107
+ * @param {FormFieldSchema} fieldConfig - The configuration object for the form field, which
108
+ * may include properties such as `name`, `onChange`, and `submitOnChange`.
109
+ * @param {Function} onChange - An optional callback function to be called after the form field value
110
+ * has been updated.
111
+ *
112
+ * @returns {void}
113
+ *
114
+ * @example
115
+ * const value = "example";
116
+ * const formContext = {
117
+ * setValue: (name, value, options) => { ... },
118
+ * getFieldState: (name) => ({ isDirty: false, isTouched: true }),
119
+ * onSubmit: () => { ... }
120
+ * };
121
+ *
122
+ * const fieldConfig = {
123
+ * name: "exampleField",
124
+ * onChange: (value) => { console.log("Field changed:", value); },
125
+ * submitOnChange: true,
126
+ * };
127
+ *
128
+ * const onChange = (value) => { console.log("External onChange:", value); };
129
+ *
130
+ * handleChange(value, formContext, fieldConfig, onChange);
131
+ *
132
+ * // This will:
133
+ * // - Update the form context with the new value.
134
+ * // - Call `fieldConfig.onChange` if it exists.
135
+ * // - Call `onChange` if it exists.
136
+ * // - Submit the form if `submitOnChange` is true.
137
+ */
138
+ export const handleChange = (
139
+ value: any,
140
+ formContext: FormContextType,
141
+ fieldConfig: FormFieldSchema,
142
+ onChange?: (value: any) => void
143
+ ): void => {
144
+ // if (fieldConfig.outputFormat === OutputFormatType.ARRAY) {
145
+ // value = [value];
146
+ // }
147
+ // Update the form context with the new value
148
+ formContext.setValue(fieldConfig.name, value, {
149
+ shouldValidate: true,
150
+ shouldDirty: formContext.getFieldState(fieldConfig.name).isDirty,
151
+ shouldTouch: formContext.getFieldState(fieldConfig.name).isTouched,
152
+ });
153
+
154
+ // Call the onChange method from fieldConfig if it exists
155
+ if (fieldConfig.onChange) {
156
+ fieldConfig.onChange(value);
157
+ }
158
+
159
+ // Call the external onChange callback if provided
160
+ if (onChange) onChange(value);
161
+
162
+ // Submit the form if submitOnChange is true and onSubmit exists in formContext
163
+ if (fieldConfig.submitOnChange && formContext.onSubmit) {
164
+ formContext.onSubmit();
165
+ }
166
+ };
167
+
168
+ export const resetFormField = (formContext: FormContextType, name: string) => {
169
+ formContext.resetField(name);
170
+ };
171
+
172
+
173
+ export const convertToTitleCase = (str: string): string => {
174
+ if (!str) return '-';
175
+ // Split the string by underscores
176
+ let words: string[] = str.split('_');
177
+
178
+ // Capitalize the first letter of each word
179
+ let capitalizedWords: string[] = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase());
180
+
181
+ // Join the words with a space
182
+ let result: string = capitalizedWords.join(' ');
183
+
184
+ return result;
185
+ };
@@ -0,0 +1,28 @@
1
+ import React from "react";
2
+
3
+ export function LoaderWithText() {
4
+ return (
5
+ <div
6
+ id="loader-with-text"
7
+ className="flex items-center justify-center space-x-2"
8
+ >
9
+ <div aria-label="Loading..." role="status">
10
+ <svg
11
+ width="24"
12
+ height="24"
13
+ fill="none"
14
+ stroke="currentColor"
15
+ strokeWidth="1.5"
16
+ viewBox="0 0 24 24"
17
+ strokeLinecap="round"
18
+ strokeLinejoin="round"
19
+ xmlns="http://www.w3.org/2000/svg"
20
+ className="animate-spin w-4 h-4 stroke-slate-500"
21
+ >
22
+ <path d="M12 3v3m6.366-.366-2.12 2.12M21 12h-3m.366 6.366-2.12-2.12M12 21v-3m-6.366.366 2.12-2.12M3 12h3m-.366-6.366 2.12 2.12"></path>
23
+ </svg>
24
+ </div>
25
+ <span className="text-xs font-medium text-slate-500">Loading...</span>
26
+ </div>
27
+ );
28
+ }
@@ -0,0 +1,16 @@
1
+ export const HELPER_ICONS: {
2
+ [key in string]: string;
3
+ } = {
4
+ starIcon: `<path
5
+ fill-rule="evenodd"
6
+ d="M8 1.75a.75.75 0 0 1 .692.462l1.41 3.393 3.664.293a.75.75 0 0 1 .428 1.317l-2.791 2.39.853 3.575a.75.75 0 0 1-1.12.814L7.998 12.08l-3.135 1.915a.75.75 0 0 1-1.12-.814l.852-3.574-2.79-2.39a.75.75 0 0 1 .427-1.318l3.663-.293 1.41-3.393A.75.75 0 0 1 8 1.75Z"
7
+ clip-rule="evenodd"
8
+ ></path>`,
9
+ chevronDown: `<path fill-rule="evenodd"
10
+ d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z"
11
+ clip-rule="evenodd"
12
+ ></path>`,
13
+ checkIcon: `<path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0"/>`,
14
+ // <path fill-rule="evenodd" d="M16.704 4.153a.75.75 0 0 1 .143 1.052l-8 10.5a.75.75 0 0 1-1.127.075l-4.5-4.5a.75.75 0 0 1 1.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 0 1 1.05-.143Z" clip-rule="evenodd"></path>`,
15
+ search: `<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"></path>`,
16
+ };
@@ -0,0 +1,23 @@
1
+ import React, { SVGAttributes } from "react";
2
+ import { HELPER_ICONS } from "./HELPER_ICONS";
3
+
4
+ const SVGIcon = (props: SVGAttributes<{}>) => {
5
+ if (!props.name) {
6
+ return <p>No name is specified</p>;
7
+ }
8
+ const icon = HELPER_ICONS[props.name];
9
+ return (
10
+ <svg
11
+ xmlns="http://www.w3.org/2000/svg"
12
+ viewBox="0 0 16 16"
13
+ fill="currentColor"
14
+ aria-hidden="true"
15
+ data-slot="icon"
16
+ {...props}
17
+ dangerouslySetInnerHTML={{ __html: icon }}
18
+ ></svg>
19
+
20
+ );
21
+ };
22
+
23
+ export default SVGIcon;
package/tsconfig.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES6", // Target ECMAScript 6
4
+ "module": "ESNext", // Use the latest module system
5
+ "lib": ["DOM", "DOM.Iterable", "ESNext"], // Include DOM and ESNext libraries
6
+ "jsx": "react-jsx", // JSX support for React 17+
7
+ "strict": true, // Enable all strict type-checking options
8
+ "esModuleInterop": true, // Enable interoperability between CommonJS and ES Modules
9
+ "allowSyntheticDefaultImports": true, // Allow default imports from modules with no default export
10
+ "skipLibCheck": true, // Skip type checking of declaration files
11
+ "forceConsistentCasingInFileNames": true, // Ensure consistent casing in file names
12
+ "moduleResolution": "node", // Use Node.js module resolution strategy
13
+ "isolatedModules": true, // Treat each file as a module
14
+ "resolveJsonModule": true, // Allow importing JSON files
15
+ "noEmit": true, // Prevent TypeScript from emitting compiled output
16
+ "baseUrl": ".", // Set the base URL for module resolution
17
+ "paths": {
18
+ // Define path aliases
19
+ "@components/*": ["src/components/*"],
20
+ "@utils/*": ["src/utils/*"]
21
+ }
22
+ },
23
+ "include": ["src"], // Include files in the 'src' directory
24
+ "exclude": ["node_modules", "build", "dist"] // Exclude files and directories from the compilation
25
+ }