@engagebay/engagebay-form-module 1.0.5 → 1.0.7-beta.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@engagebay/engagebay-form-module",
3
- "version": "1.0.5",
3
+ "version": "1.0.7-beta.1",
4
4
  "description": "Provide base form components to reacho",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -16,7 +16,6 @@
16
16
  "@heroicons/react": ">=2.1.5",
17
17
  "@reduxjs/toolkit": ">=2.2.7",
18
18
  "@tippyjs/react": ">=4.2.6",
19
- "@types/lodash": ">=4.17.7",
20
19
  "@types/react-redux": ">=7.1.33",
21
20
  "axios": ">=1.7.2",
22
21
  "clsx": ">=2.1.1",
@@ -32,7 +31,6 @@
32
31
  "@heroicons/react": ">=2.1.5",
33
32
  "@reduxjs/toolkit": ">=2.2.7",
34
33
  "@tippyjs/react": ">=4.2.6",
35
- "@types/lodash": ">=4.17.7",
36
34
  "@types/react-redux": ">=7.1.33",
37
35
  "axios": ">=1.7.2",
38
36
  "clsx": ">=2.1.1",
package/src/api/index.ts CHANGED
@@ -1,10 +1,16 @@
1
1
  import axios, {AxiosInstance, AxiosRequestConfig} from "axios";
2
2
  import {FormFieldSchema} from "../form/schema/FormFieldSchema";
3
3
 
4
+ let baseURL;
5
+ try {
6
+ baseURL = (window as any).DEFAULT_HOST ||
7
+ (window as any).parent.DEFAULT_HOST
8
+ } catch (error) {
9
+
10
+ }
11
+
4
12
  const BASE_API: AxiosRequestConfig = {
5
- baseURL:
6
- (window as any).REACHO_BASE_URL ||
7
- (window as any).parent.REACHO_BASE_URL,
13
+ baseURL: baseURL ?? "",
8
14
  timeout: 30000,
9
15
  headers: {
10
16
  "Content-Type": "application/json",
@@ -34,6 +34,7 @@ import {
34
34
  FormFieldType,
35
35
  OptionMappingConfig,
36
36
  } from "./schema/FormFieldSchema";
37
+ import Typeahead2 from "./formfields/Typeahead2";
37
38
 
38
39
  /**
39
40
  * @property {React.FC<FormFieldComponentPropSchema>} component - React component for a form field.
@@ -139,6 +140,9 @@ const formFieldComponents: FormComponentSchema = {
139
140
  [FormFieldType[FormFieldType.TYPEAHEAD_MULTI_SELECT]]: {
140
141
  component: TypeaheadMultiSelect,
141
142
  },
143
+ [FormFieldType[FormFieldType.TYPEAHEAD_2]]: {
144
+ component: Typeahead2,
145
+ },
142
146
  [FormFieldType[FormFieldType.COMBO_SELECT]]: {
143
147
  component: ComboSelect,
144
148
  },
@@ -21,7 +21,6 @@ import FormField from "../FormField";
21
21
  import RenderFormField from "../util/RenderFormField";
22
22
  import { TrashIcon, XMarkIcon } from "@heroicons/react/24/outline";
23
23
  import Tippy from "@tippyjs/react";
24
- import { set } from "lodash";
25
24
 
26
25
  const defaultBusinessHours = {
27
26
  MONDAY: {
@@ -124,9 +123,8 @@ export const BusinessHoursField: React.FC<FormFieldComponentPropSchema> = (
124
123
  <div className="w-full sm:w-full text-end mr-[2.3em]">
125
124
  <button
126
125
  type="button"
127
- className={`text-end text-primary cursor-pointer font-[13px] font-medium ${
128
- getValues(`${mappedName}.sessions`)?.length > 1 ? "mr-9" : ""
129
- }`}
126
+ className={`text-end text-primary cursor-pointer font-[13px] font-medium ${getValues(`${mappedName}.sessions`)?.length > 1 ? "mr-9" : ""
127
+ }`}
130
128
  onClick={() => {
131
129
  const lastEndTime =
132
130
  getValues(`${mappedName}.sessions`)?.[
@@ -8,7 +8,7 @@ import { handleChange, registerFormField } from "../util";
8
8
  import moment from "moment";
9
9
 
10
10
  const DatePicker: React.FC<FormFieldComponentPropSchema> = (
11
- props: FormFieldComponentPropSchema
11
+ props: FormFieldComponentPropSchema,
12
12
  ) => {
13
13
  const formContext = useContext(FormContext);
14
14
  const initialDate = formContext.getValues(props.fieldConfig.name)
@@ -25,24 +25,38 @@ const DatePicker: React.FC<FormFieldComponentPropSchema> = (
25
25
  ) {
26
26
  formContext.setValue(
27
27
  props.fieldConfig.name,
28
- props.fieldConfig.defaultValue
28
+ props.fieldConfig.defaultValue,
29
29
  );
30
30
  }
31
31
  }, [props.fieldConfig.forceUpdate]);
32
32
 
33
33
  let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
34
-
34
+ //registerOptions.valueAsDate = true;
35
35
  let hookProps = formContext.register(props.fieldConfig.name, registerOptions);
36
36
 
37
37
  function getInput() {
38
+ const rawStart =
39
+ initialDate?.startDate ||
40
+ props.fieldConfig.defaultValue ||
41
+ "";
42
+ const rawEnd =
43
+ initialDate?.endDate ||
44
+ props.fieldConfig.defaultValue ||
45
+ "";
46
+ const toDate = (v: string | Date | null | undefined): Date | null => {
47
+ if (v == null || v === "") return null;
48
+ return moment(v).isValid() ? moment(v).toDate() : null;
49
+ };
50
+ const value = {
51
+ startDate: toDate(rawStart),
52
+ endDate: toDate(rawEnd),
53
+ };
54
+ // Calendar opens on selected date's month/year (must be a Date instance)
55
+ const startFrom = value.startDate instanceof Date ? value.startDate : new Date();
38
56
  return (
39
57
  <Datepicker
40
- value={
41
- initialDate || {
42
- startDate: props.fieldConfig.defaultValue,
43
- endDate: props.fieldConfig.defaultValue,
44
- }
45
- }
58
+ value={value}
59
+ startFrom={startFrom}
46
60
  {...hookProps}
47
61
  placeholder={
48
62
  props.fieldConfig.placeholder
@@ -53,13 +67,7 @@ const DatePicker: React.FC<FormFieldComponentPropSchema> = (
53
67
  popoverDirection="down"
54
68
  useRange={false}
55
69
  inputName={props.fieldConfig.name}
56
- key={
57
- props.fieldConfig.name +
58
- "_" +
59
- initialDate.startDate +
60
- "_" +
61
- initialDate.endDate
62
- }
70
+ key={props.fieldConfig.name + "_" + rawStart + "_" + rawEnd}
63
71
  containerClassName={"relative"}
64
72
  minDate={props.fieldConfig.minDate}
65
73
  maxDate={props.fieldConfig.maxDate}
@@ -1,61 +1,66 @@
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";
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
6
  import RenderFormField from "../util/RenderFormField";
7
7
 
8
8
  const NumberField: React.FC<FormFieldComponentPropSchema> = (
9
- props: FormFieldComponentPropSchema
9
+ props: FormFieldComponentPropSchema,
10
10
  ) => {
11
- const formContext = useContext(FormContext);
11
+ const formContext = useContext(FormContext);
12
12
 
13
- let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
13
+ let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
14
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
- }
15
+ let hookProps = formContext.register(props.fieldConfig.name, registerOptions);
56
16
 
17
+ function getInput() {
57
18
  return (
58
- <RenderFormField fieldConfig={props.fieldConfig} getInput={getInput}/>
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"
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 (
40
+ props.fieldConfig.max !== undefined &&
41
+ numericValue > props.fieldConfig.max
42
+ ) {
43
+ numericValue = props.fieldConfig.max;
44
+ }
45
+ if (
46
+ props.fieldConfig.min !== undefined &&
47
+ numericValue < props.fieldConfig.min
48
+ ) {
49
+ numericValue = props.fieldConfig.min;
50
+ }
51
+ handleChange(
52
+ numericValue,
53
+ formContext,
54
+ props.fieldConfig,
55
+ props.onChange,
56
+ );
57
+ }}
58
+ />
59
59
  );
60
+ }
61
+
62
+ return (
63
+ <RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
64
+ );
60
65
  };
61
66
  export default NumberField;
@@ -1,9 +1,9 @@
1
1
  import React, {
2
- useCallback,
3
- useContext,
4
- useEffect,
5
- useRef,
6
- useState,
2
+ useCallback,
3
+ useContext,
4
+ useEffect,
5
+ useRef,
6
+ useState,
7
7
  } from "react";
8
8
 
9
9
  import { Listbox, ListboxButton } from "@headlessui/react";
@@ -12,199 +12,185 @@ import { RegisterOptions } from "react-hook-form";
12
12
  import { FormContext } from "../context/FormContext";
13
13
  import { getListOption, getListOptions } from "../FormFieldUtils";
14
14
  import {
15
- FieldOptionsSchema,
16
- FormFieldComponentPropSchema,
17
- FormFieldType,
18
- OutputFormatType,
15
+ FieldOptionsSchema,
16
+ FormFieldComponentPropSchema,
17
+ FormFieldType,
18
+ OutputFormatType,
19
19
  } from "../schema/FormFieldSchema";
20
20
  import { handleChange, registerFormField } from "../util";
21
21
  import RenderFormField from "../util/RenderFormField";
22
22
  import RenderListOptions, {
23
- renderListBoxValue,
23
+ renderListBoxValue,
24
24
  } from "../util/RenderListOptions";
25
25
 
26
26
  const Typeahead: React.FC<FormFieldComponentPropSchema> = (
27
- props: FormFieldComponentPropSchema
27
+ props: FormFieldComponentPropSchema,
28
28
  ) => {
29
- const dynamicSelectRef = useRef<HTMLUListElement>(null);
30
- const formContext = useContext(FormContext);
31
- let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
32
- let hookProps = formContext.register(
33
- props.fieldConfig.name,
34
- registerOptions
35
- );
36
- const [listOptions, setListOptions] = useState<FieldOptionsSchema[]>([]);
37
- const [selectedValues, setSelectedValues] = useState<FieldOptionsSchema[]>(
38
- []
39
- );
40
- const [loading, setLoading] = useState<boolean>(true);
41
-
42
- useEffect(() => {
43
- if (
44
- !formContext.getValues(props.fieldConfig.name) &&
45
- props.fieldConfig.defaultValue
46
- ) {
47
- formContext.setValue(
48
- props.fieldConfig.name,
49
- props.fieldConfig.defaultValue
50
- );
51
- }
52
- fetchData(undefined);
53
- }, []);
29
+ const dynamicSelectRef = useRef<HTMLUListElement>(null);
30
+ const formContext = useContext(FormContext);
31
+ let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
32
+ let hookProps = formContext.register(props.fieldConfig.name, registerOptions);
33
+ const [listOptions, setListOptions] = useState<FieldOptionsSchema[]>([]);
34
+ const [selectedValues, setSelectedValues] = useState<FieldOptionsSchema[]>(
35
+ [],
36
+ );
37
+ const [loading, setLoading] = useState<boolean>(true);
54
38
 
55
- const fetchData = useCallback(
56
- async (_query: string | undefined) => {
57
- setLoading(true);
58
- try {
59
- if (!props.fieldConfig.fetchUrl) return;
39
+ useEffect(() => {
40
+ if (
41
+ !formContext.getValues(props.fieldConfig.name) &&
42
+ props.fieldConfig.defaultValue
43
+ ) {
44
+ formContext.setValue(
45
+ props.fieldConfig.name,
46
+ props.fieldConfig.defaultValue,
47
+ );
48
+ }
49
+ fetchData(undefined);
50
+ }, []);
60
51
 
61
- let url = props.fieldConfig.fetchUrl;
62
- if (_query) {
63
- url = url.includes("?")
64
- ? url + "&q=" + _query
65
- : url + "?q=" + _query;
66
- }
52
+ const fetchData = useCallback(
53
+ async (_query: string | undefined) => {
54
+ setLoading(true);
55
+ try {
56
+ if (!props.fieldConfig.fetchUrl) return;
67
57
 
68
- let response = await (props.fieldConfig.disableHeaderInFetch
69
- ? axios.get(url)
70
- : formContext.axiosInstance?.get(url));
71
- if (response?.data) {
72
- const data: FieldOptionsSchema[] = getListOptions(
73
- response.data,
74
- props.fieldConfig.optionsConfig
75
- );
76
- setListOptions([...data]);
77
- let value = formContext.getValues(props.fieldConfig.name);
78
- if (
79
- value &&
80
- data.find((i) => i.value !== value)
81
- ) {
82
- if (selectedValues.find((i) => i.value !== value))
83
- fetchValue(value);
84
- }
85
- if (props.fieldConfig.fetchCallback) {
86
- props.fieldConfig.fetchCallback(response);
87
- }
88
- } else {
89
- console.error(response?.statusText);
90
- }
91
- } catch (err) {
92
- } finally {
93
- setLoading(false);
94
- }
95
- },
96
- [props.fieldConfig.fetchUrl]
97
- );
58
+ let url = props.fieldConfig.fetchUrl;
59
+ if (_query) {
60
+ url = url.includes("?") ? url + "&q=" + _query : url + "?q=" + _query;
61
+ }
98
62
 
99
- const fetchValue = async (value: string) => {
100
- try {
101
- if (
102
- props.fieldConfig.ignoreFetchValue ||
103
- !props.fieldConfig.fetchUrl
104
- ) {
105
- return;
106
- }
63
+ let response = await (props.fieldConfig.disableHeaderInFetch
64
+ ? axios.get(url)
65
+ : formContext.axiosInstance?.get(url));
66
+ if (response?.data) {
67
+ const data: FieldOptionsSchema[] = getListOptions(
68
+ response.data,
69
+ props.fieldConfig.optionsConfig,
70
+ );
71
+ setListOptions([...data]);
72
+ let value = formContext.getValues(props.fieldConfig.name);
73
+ if (value && data.find((i) => i.value !== value)) {
74
+ if (selectedValues.find((i) => i.value !== value))
75
+ fetchValue(value);
76
+ }
77
+ if (props.fieldConfig.fetchCallback) {
78
+ props.fieldConfig.fetchCallback(response);
79
+ }
80
+ } else {
81
+ console.error(response?.statusText);
82
+ }
83
+ } catch (err) {
84
+ } finally {
85
+ setLoading(false);
86
+ }
87
+ },
88
+ [props.fieldConfig.fetchUrl],
89
+ );
107
90
 
108
- let url = props.fieldConfig.fetchUrl;
91
+ const fetchValue = async (value: string) => {
92
+ try {
93
+ if (props.fieldConfig.ignoreFetchValue || !props.fieldConfig.fetchUrl) {
94
+ return;
95
+ }
109
96
 
110
- if (value)
111
- url = url.includes("?")
112
- ? url + "&values=" + value
113
- : url + "?values=" + value;
97
+ let url = props.fieldConfig.fetchUrl;
114
98
 
115
- let response = await (
116
- props.fieldConfig.disableHeaderInFetch
117
- ? axios.get(url)
118
- : formContext.axiosInstance?.get(url)
119
- );
120
- if (response?.data) {
121
- const data: FieldOptionsSchema[] = getListOptions(
122
- response?.data,
123
- props.fieldConfig.optionsConfig
124
- );
125
- setSelectedValues([...data]);
126
- }
127
- } catch (err) { }
128
- };
99
+ if (value)
100
+ url = url.includes("?")
101
+ ? url + "&values=" + value
102
+ : url + "?values=" + value;
129
103
 
130
- const updateListOptions = (data: any) => {
131
- const resData: FieldOptionsSchema = getListOption(
132
- data,
133
- props.fieldConfig.optionsConfig
104
+ let response = await (props.fieldConfig.disableHeaderInFetch
105
+ ? axios.get(url)
106
+ : formContext.axiosInstance?.get(url));
107
+ if (response?.data) {
108
+ const data: FieldOptionsSchema[] = getListOptions(
109
+ response?.data,
110
+ props.fieldConfig.optionsConfig,
134
111
  );
135
- setSelectedValues((perv) => [resData]);
136
- formContext.setValue(props.fieldConfig.name, resData.value);
137
- };
112
+ setSelectedValues([...data]);
113
+ }
114
+ } catch (err) {}
115
+ };
138
116
 
139
- function getInput() {
140
- return (
141
- <Listbox
142
- as={"div"}
143
- {...hookProps}
144
- className={`relative form-listbox flex-1`}
145
- value={
146
- formContext.getValues(props.fieldConfig.name)
147
- ? props.fieldConfig.outputFormat ===
148
- OutputFormatType.ARRAY
149
- ? formContext.getValues(props.fieldConfig.name)[0]
150
- : formContext.getValues(props.fieldConfig.name)
151
- : undefined
152
- }
153
- onChange={(val) => {
154
- const currentValue = formContext.getValues(
155
- props.fieldConfig.name
156
- );
117
+ const updateListOptions = (data: any) => {
118
+ const resData: FieldOptionsSchema = getListOption(
119
+ data,
120
+ props.fieldConfig.optionsConfig,
121
+ );
122
+ setSelectedValues((perv) => [resData]);
123
+ formContext.setValue(props.fieldConfig.name, resData.value);
124
+ };
157
125
 
158
- // If the value matches, set it to null, otherwise set it to val
159
- const newValue = currentValue === val ? null : val;
126
+ function getInput() {
127
+ return (
128
+ <Listbox
129
+ as={"div"}
130
+ {...hookProps}
131
+ className={`relative form-listbox flex-1`}
132
+ value={
133
+ formContext.getValues(props.fieldConfig.name)
134
+ ? props.fieldConfig.outputFormat === 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);
160
141
 
161
- const selected = listOptions.find(o => o.value == newValue);
162
- selected && setSelectedValues([selected]);
163
- handleChange(
164
- newValue,
165
- formContext,
166
- props.fieldConfig,
167
- props.onChange
168
- );
169
- }}
170
- name={props.fieldConfig.name}
171
- disabled={props.fieldConfig.disabled}>
172
- <ListboxButton
173
- className={
174
- props.fieldConfig.customClassNames?.fieldClassName
175
- ? "form-listbox-select " +
176
- props.fieldConfig.customClassNames?.fieldClassName
177
- : "form-listbox-select"
178
- }>
179
- {renderListBoxValue(
180
- formContext,
181
- props.fieldConfig,
182
- [...selectedValues, ...listOptions],
183
- props.onChange
184
- )}
185
- </ListboxButton>
186
- <RenderListOptions
187
- formContext={formContext}
188
- onChange={props.onChange}
189
- formField={FormFieldType.TYPEAHEAD}
190
- ref={dynamicSelectRef}
191
- fieldConfig={props.fieldConfig}
192
- listOptions={[...selectedValues, ...listOptions]}
193
- setListOptions={setListOptions}
194
- loading={loading}
195
- setLoading={setLoading}
196
- createCallback={(data) => updateListOptions(data)}
197
- queryCallback={(query) => fetchData(query)}
198
- />
199
- </Listbox>
200
- );
201
- }
202
- if (props.fieldConfig.hideWhenNoResults && listOptions.length == 0) {
203
- return <></>;
204
- }
142
+ // If the value matches, set it to null, otherwise set it to val
143
+ const newValue = currentValue === val ? null : val;
205
144
 
206
- return (
207
- <RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
145
+ const selected = listOptions.find((o) => o.value == newValue);
146
+ selected && setSelectedValues([selected]);
147
+ handleChange(
148
+ newValue,
149
+ formContext,
150
+ props.fieldConfig,
151
+ props.onChange,
152
+ );
153
+ }}
154
+ name={props.fieldConfig.name}
155
+ disabled={props.fieldConfig.disabled}
156
+ >
157
+ <ListboxButton
158
+ className={
159
+ props.fieldConfig.customClassNames?.fieldClassName
160
+ ? "form-listbox-select " +
161
+ props.fieldConfig.customClassNames?.fieldClassName
162
+ : "form-listbox-select"
163
+ }
164
+ >
165
+ {renderListBoxValue(
166
+ formContext,
167
+ props.fieldConfig,
168
+ [...selectedValues, ...listOptions],
169
+ props.onChange,
170
+ )}
171
+ </ListboxButton>
172
+ <RenderListOptions
173
+ formContext={formContext}
174
+ onChange={props.onChange}
175
+ formField={FormFieldType.TYPEAHEAD}
176
+ ref={dynamicSelectRef}
177
+ fieldConfig={props.fieldConfig}
178
+ listOptions={[...selectedValues, ...listOptions]}
179
+ setListOptions={setListOptions}
180
+ loading={loading}
181
+ setLoading={setLoading}
182
+ createCallback={(data) => updateListOptions(data)}
183
+ queryCallback={(query) => fetchData(query)}
184
+ />
185
+ </Listbox>
208
186
  );
187
+ }
188
+ if (props.fieldConfig.hideWhenNoResults && listOptions.length == 0) {
189
+ return <></>;
190
+ }
191
+
192
+ return (
193
+ <RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
194
+ );
209
195
  };
210
196
  export default Typeahead;
@@ -0,0 +1,284 @@
1
+ import React, {
2
+ useCallback,
3
+ useContext,
4
+ useEffect,
5
+ useMemo,
6
+ useRef,
7
+ useState,
8
+ } from "react";
9
+
10
+ import { Listbox, ListboxButton } from "@headlessui/react";
11
+ import { RegisterOptions } from "react-hook-form";
12
+
13
+ import { FormContext } from "../context/FormContext";
14
+ import { getListOption, getListOptions } from "../FormFieldUtils";
15
+ import {
16
+ FieldOptionsSchema,
17
+ FormFieldComponentPropSchema,
18
+ FormFieldType,
19
+ } from "../schema/FormFieldSchema";
20
+ import { handleChange, registerFormField } from "../util";
21
+ import RenderFormField from "../util/RenderFormField";
22
+ import RenderListOptions, {
23
+ renderListBoxValue,
24
+ } from "../util/RenderListOptions";
25
+ import axios from "axios";
26
+ import { getAxiosInstance } from "../../api";
27
+
28
+ const Typeahead2: React.FC<FormFieldComponentPropSchema> = (
29
+ props: FormFieldComponentPropSchema,
30
+ ) => {
31
+ const dynamicSelectRef = useRef<HTMLUListElement>(null);
32
+ const abortControllerRef = useRef<AbortController | null>(null);
33
+ const formContext = useContext(FormContext);
34
+ let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
35
+ let hookProps = formContext.register(props.fieldConfig.name, registerOptions);
36
+ const [listOptions, setListOptions] = useState<FieldOptionsSchema[]>([]);
37
+ const [selectedValues, setSelectedValues] = useState<FieldOptionsSchema[]>(
38
+ [],
39
+ );
40
+ const [loading, setLoading] = useState<boolean>(false);
41
+
42
+ useEffect(() => {
43
+ if (
44
+ !formContext.getValues(props.fieldConfig.name) &&
45
+ props.fieldConfig.defaultValue
46
+ ) {
47
+ formContext.setValue(
48
+ props.fieldConfig.name,
49
+ props.fieldConfig.defaultValue,
50
+ );
51
+ }
52
+ let values: any | any[] | undefined =
53
+ formContext.getValues(props.fieldConfig.name) ||
54
+ props.fieldConfig.defaultValue;
55
+
56
+ if (values && selectedValues.length < 1) {
57
+ if (props.fieldConfig.fetchSavedDataUrl) {
58
+ fetchValue(values);
59
+ } else {
60
+ setSelectedValues(
61
+ Array.isArray(values)
62
+ ? values.map((val: any) => ({ label: val, value: val }))
63
+ : [values],
64
+ );
65
+ }
66
+ }
67
+ }, []);
68
+
69
+
70
+
71
+ const fetchData = useMemo(() => {
72
+ let timeoutId: ReturnType<typeof setTimeout>;
73
+
74
+ return (_query: string | undefined) => {
75
+ // Clear the previous timer
76
+ if (timeoutId) clearTimeout(timeoutId);
77
+
78
+ // Set a new timer
79
+ timeoutId = setTimeout(async () => {
80
+ // 1. Cancel the previous request if it's still pending
81
+ if (abortControllerRef.current) {
82
+ abortControllerRef.current.abort();
83
+ }
84
+
85
+ // 2. Create a new controller for the current request
86
+ const controller = new AbortController();
87
+ abortControllerRef.current = controller;
88
+ const minLength = props.fieldConfig?.allowedMinQueryLength ?? 3;
89
+
90
+ if (!_query || _query.length < minLength) {
91
+ setListOptions([]);
92
+ setLoading(false);
93
+ return;
94
+ }
95
+ setLoading(true);
96
+ try {
97
+ if (!props.fieldConfig.fetchUrl) return;
98
+
99
+ let url = props.fieldConfig.fetchUrl;
100
+
101
+ url = url.includes("?") ? url + "&q=" + _query : url + "?q=" + _query;
102
+
103
+ const axiosInstance = getAxiosInstance(
104
+ formContext.axiosInstance,
105
+ props.fieldConfig,
106
+ );
107
+ const response = await axiosInstance.get(url);
108
+
109
+ if (controller === abortControllerRef.current) {
110
+ if (response?.data) {
111
+ const data: FieldOptionsSchema[] = getListOptions(
112
+ response?.data,
113
+ props.fieldConfig.optionsConfig,
114
+ );
115
+ setListOptions([...data]);
116
+
117
+ if (props.fieldConfig.fetchCallback) {
118
+ props.fieldConfig.fetchCallback(response);
119
+ }
120
+ }
121
+ }
122
+ } catch (err) {
123
+ console.error("Fetch error:", err);
124
+ } finally {
125
+ // Only stop loading if this was the "active" request
126
+ if (controller === abortControllerRef.current) {
127
+ setLoading(false);
128
+ }
129
+ }
130
+ }, 250);
131
+ };
132
+ }, []);
133
+
134
+ // Cleanup on unmount
135
+ useEffect(() => {
136
+ return () => {
137
+ abortControllerRef.current?.abort();
138
+ };
139
+ }, []);
140
+
141
+ const fetchValue = async (value: string | any | any[]) => {
142
+ try {
143
+ if (
144
+ props.fieldConfig.ignoreFetchValue ||
145
+ !props.fieldConfig.fetchSavedDataUrl
146
+ ) {
147
+ return;
148
+ }
149
+
150
+ let url = props.fieldConfig.fetchSavedDataUrl;
151
+
152
+ // url = url.includes("?")
153
+ // ? url + "&values=" + value
154
+ // : url + "?values=" + value;
155
+
156
+ let response = await (props.fieldConfig.disableHeaderInFetch
157
+ ? axios.post(url, Array.isArray(value) ? value : [value])
158
+ : formContext.axiosInstance?.post(
159
+ url,
160
+ Array.isArray(value) ? value : [value],
161
+ ));
162
+ if (response?.data) {
163
+ const data: FieldOptionsSchema[] = getListOptions(
164
+ response?.data,
165
+ props.fieldConfig.optionsConfig,
166
+ );
167
+ let values: any[] = formContext.getValues(props.fieldConfig.name) || [];
168
+ setSelectedValues(
169
+ [
170
+ ...data,
171
+ props.fieldConfig.dropdownFieldConfig?.isSuggestionBox &&
172
+ values &&
173
+ Array.isArray(values)
174
+ ? values
175
+ .filter(
176
+ (value) =>
177
+ data.some((d) => d.value !== value) || data.length == 0,
178
+ )
179
+ .map((val) => ({ label: val, value: val }))
180
+ : [],
181
+ ].flat(),
182
+ );
183
+ }
184
+ } catch (err) { }
185
+ };
186
+
187
+ const updateListOptions = (data: any) => {
188
+ const resData: FieldOptionsSchema = getListOption(
189
+ data,
190
+ props.fieldConfig.optionsConfig,
191
+ );
192
+ setListOptions([]);
193
+ setSelectedValues((prev) =>
194
+ props.fieldConfig.isMultiple ? [...prev, resData] : [resData],
195
+ );
196
+ let result = formContext.getValues(props.fieldConfig.name) || [];
197
+ result = !props.fieldConfig.isMultiple
198
+ ? resData.value
199
+ : result.includes(resData.value)
200
+ ? [...result.filter((v: string) => v != resData.value), resData.value]
201
+ : [...result, resData.value];
202
+ formContext.setValue(props.fieldConfig.name, result);
203
+ };
204
+
205
+ const getInput = () => {
206
+ return (
207
+ <Listbox
208
+ as={"div"}
209
+ {...hookProps}
210
+ value={
211
+ props.fieldConfig.isMultiple
212
+ ? formContext.getValues(props.fieldConfig.name)
213
+ ? formContext.getValues(props.fieldConfig.name)
214
+ : []
215
+ : formContext.getValues(props.fieldConfig.name)
216
+ }
217
+ name={props.fieldConfig.name}
218
+ defaultValue={props.fieldConfig.defaultValue}
219
+ key={props.fieldConfig.name}
220
+ className={"relative form-listbox flex-1 overflow-hidden"}
221
+ onChange={(selectedOptions) => {
222
+ let currentValue = formContext.getValues(props.fieldConfig.name);
223
+ const newValue =
224
+ currentValue === selectedOptions ? null : selectedOptions;
225
+
226
+ if (props.fieldConfig.isMultiple) {
227
+ currentValue = listOptions.filter((op) =>
228
+ selectedOptions.includes(op.value),
229
+ );
230
+ setSelectedValues((prev) => [...prev, ...currentValue]);
231
+ } else {
232
+ const selected = listOptions.find((o) => o.value == newValue);
233
+ selected && setSelectedValues([selected]);
234
+ }
235
+ setListOptions([]);
236
+ handleChange(
237
+ newValue,
238
+ formContext,
239
+ props.fieldConfig,
240
+ props.onChange,
241
+ );
242
+ }}
243
+ multiple={props.fieldConfig.isMultiple}
244
+ >
245
+ <ListboxButton
246
+ className={
247
+ props.fieldConfig.customClassNames?.fieldClassName
248
+ ? "form-listbox-select " +
249
+ props.fieldConfig.customClassNames?.fieldClassName
250
+ : "form-listbox-select"
251
+ }
252
+ >
253
+ {renderListBoxValue(
254
+ formContext,
255
+ props.fieldConfig,
256
+ [...selectedValues, ...listOptions],
257
+ props.onChange,
258
+ )}
259
+ </ListboxButton>
260
+ <RenderListOptions
261
+ formContext={formContext}
262
+ onChange={props.onChange}
263
+ formField={FormFieldType.TYPEAHEAD_2}
264
+ ref={dynamicSelectRef}
265
+ fieldConfig={props.fieldConfig}
266
+ listOptions={listOptions}
267
+ setListOptions={setListOptions}
268
+ loading={loading}
269
+ setLoading={setLoading}
270
+ createCallback={(data) => updateListOptions(data)}
271
+ queryCallback={(query) => fetchData(query)}
272
+ />
273
+ </Listbox>
274
+ );
275
+ };
276
+ if (props.fieldConfig.hideWhenNoResults && listOptions.length == 0) {
277
+ return <></>;
278
+ }
279
+
280
+ return (
281
+ <RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
282
+ );
283
+ };
284
+ export default Typeahead2;
@@ -23,9 +23,10 @@ import RenderListOptions, {
23
23
  } from "../util/RenderListOptions";
24
24
  // import _ from "lodash";
25
25
  import axios from "axios";
26
+ import { getAxiosInstance } from "../../api";
26
27
 
27
28
  const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
28
- props: FormFieldComponentPropSchema
29
+ props: FormFieldComponentPropSchema,
29
30
  ) => {
30
31
  const dynamicSelectRef = useRef<HTMLUListElement>(null);
31
32
  const formContext = useContext(FormContext);
@@ -33,7 +34,7 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
33
34
  let hookProps = formContext.register(props.fieldConfig.name, registerOptions);
34
35
  const [listOptions, setListOptions] = useState<FieldOptionsSchema[]>([]);
35
36
  const [selectedValues, setSelectedValues] = useState<FieldOptionsSchema[]>(
36
- []
37
+ [],
37
38
  );
38
39
  const [loading, setLoading] = useState<boolean>(true);
39
40
 
@@ -44,7 +45,7 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
44
45
  ) {
45
46
  formContext.setValue(
46
47
  props.fieldConfig.name,
47
- props.fieldConfig.defaultValue
48
+ props.fieldConfig.defaultValue,
48
49
  );
49
50
  }
50
51
  fetchData(undefined);
@@ -60,14 +61,16 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
60
61
  if (_query) {
61
62
  url = url.includes("?") ? url + "&q=" + _query : url + "?q=" + _query;
62
63
  }
64
+ const axiosInstance = getAxiosInstance(
65
+ formContext.axiosInstance,
66
+ props.fieldConfig,
67
+ );
63
68
 
64
- let response = await (props.fieldConfig.disableHeaderInFetch
65
- ? axios.get(url)
66
- : formContext.axiosInstance?.get(url));
69
+ let response = await axiosInstance.get(url);
67
70
  if (response?.data) {
68
71
  const data: FieldOptionsSchema[] = getListOptions(
69
72
  response?.data,
70
- props.fieldConfig.optionsConfig
73
+ props.fieldConfig.optionsConfig,
71
74
  );
72
75
  setListOptions([...data]);
73
76
  let values = formContext.getValues(props.fieldConfig.name) || [];
@@ -77,7 +80,7 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
77
80
  (data.length === 0 || // If 'data' is empty
78
81
  (!values.every((v: string) => data.some((i) => i.value === v)) && // Ensure none of 'values' match 'data'
79
82
  !values.every((v: string) =>
80
- selectedValues.some((i) => i.value === v)
83
+ selectedValues.some((i) => i.value === v),
81
84
  ))) // Ensure none of 'values' match 'selectedValues'
82
85
  ) {
83
86
  fetchValue(values); // Call 'fetchValue()' if all conditions are met
@@ -94,7 +97,7 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
94
97
  setLoading(false);
95
98
  }
96
99
  },
97
- [props.fieldConfig.fetchUrl]
100
+ [props.fieldConfig.fetchUrl],
98
101
  );
99
102
 
100
103
  const fetchValue = async (value: string) => {
@@ -105,7 +108,7 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
105
108
 
106
109
  let url = props.fieldConfig.fetchUrl;
107
110
 
108
- url = url = url.includes("?")
111
+ url = url.includes("?")
109
112
  ? url + "&values=" + value
110
113
  : url + "?values=" + value;
111
114
 
@@ -115,7 +118,7 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
115
118
  if (response?.data) {
116
119
  const data: FieldOptionsSchema[] = getListOptions(
117
120
  response?.data,
118
- props.fieldConfig.optionsConfig
121
+ props.fieldConfig.optionsConfig,
119
122
  );
120
123
  let values: any[] = formContext.getValues(props.fieldConfig.name) || [];
121
124
  setSelectedValues(
@@ -123,22 +126,22 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
123
126
  ...data,
124
127
  props.fieldConfig.dropdownFieldConfig?.isSuggestionBox && values
125
128
  ? values
126
- .filter(
127
- (value) =>
128
- data.some((d) => d.value !== value) || data.length == 0
129
- )
130
- .map((val) => ({ label: val, value: val }))
129
+ .filter(
130
+ (value) =>
131
+ data.some((d) => d.value !== value) || data.length == 0,
132
+ )
133
+ .map((val) => ({ label: val, value: val }))
131
134
  : [],
132
- ].flat()
135
+ ].flat(),
133
136
  );
134
137
  }
135
- } catch (err) {}
138
+ } catch (err) { }
136
139
  };
137
140
 
138
141
  const updateListOptions = (data: any) => {
139
142
  const resData: FieldOptionsSchema = getListOption(
140
143
  data,
141
- props.fieldConfig.optionsConfig
144
+ props.fieldConfig.optionsConfig,
142
145
  );
143
146
  setSelectedValues((prev) => [...prev, resData]);
144
147
  let result = formContext.getValues(props.fieldConfig.name) || [];
@@ -160,14 +163,14 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
160
163
  className={"relative form-listbox flex-1 overflow-hidden"}
161
164
  onChange={(selectedOptions) => {
162
165
  const chossenOptions = listOptions.filter((op) =>
163
- selectedOptions.includes(op.value)
166
+ selectedOptions.includes(op.value),
164
167
  );
165
168
  setSelectedValues((prev) => [...prev, ...chossenOptions]);
166
169
  handleChange(
167
170
  selectedOptions,
168
171
  formContext,
169
172
  props.fieldConfig,
170
- props.onChange
173
+ props.onChange,
171
174
  );
172
175
  }}
173
176
  multiple
@@ -176,7 +179,7 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
176
179
  className={
177
180
  props.fieldConfig.customClassNames?.fieldClassName
178
181
  ? "form-listbox-select " +
179
- props.fieldConfig.customClassNames?.fieldClassName
182
+ props.fieldConfig.customClassNames?.fieldClassName
180
183
  : "form-listbox-select"
181
184
  }
182
185
  >
@@ -184,7 +187,7 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
184
187
  formContext,
185
188
  props.fieldConfig,
186
189
  [...selectedValues, ...listOptions],
187
- props.onChange
190
+ props.onChange,
188
191
  )}
189
192
  </ListboxButton>
190
193
  <RenderListOptions
@@ -37,6 +37,7 @@ export enum FormFieldType {
37
37
  DYNAMIC_MULTI_SELECT = "DYNAMIC_MULTI_SELECT",
38
38
 
39
39
  TYPEAHEAD = "TYPEAHEAD",
40
+ TYPEAHEAD_2 = "TYPEAHEAD_2",
40
41
  TYPEAHEAD_MULTI_SELECT = "TYPEAHEAD_MULTI_SELECT",
41
42
  PHONE_NUMBER_INPUT = "PHONE_NUMBER_INPUT",
42
43
  SWITCH = "SWITCH",
@@ -129,10 +130,13 @@ export type FormFieldSchema = {
129
130
  submitOnChange?: boolean;
130
131
  formFieldPattern?: FormFieldPatternsImpl[];
131
132
  fetchUrl?: string;
133
+ fetchSavedDataUrl?: string;
132
134
  postUrl?: string;
133
135
  fileAccept?: string;
134
136
  icon?: ReactNode;
135
137
  outputFormat?: OutputFormatType;
138
+ isMultiple?: boolean;
139
+ allowedMinQueryLength?: number;
136
140
  children?: FormFieldSchema[];
137
141
  defaultOptions?: FieldOptionsSchema[];
138
142
  optionsConfig?: OptionMappingConfig;
@@ -201,6 +205,7 @@ export type FieldOptionsSchema = {
201
205
  isDisabled?: boolean;
202
206
  helpText?: string;
203
207
  groupName?: string;
208
+ tooltip?: string;
204
209
  icon?: React.ReactNode;
205
210
  };
206
211
 
@@ -11,13 +11,13 @@ import { handleChange } from ".";
11
11
  import { LoaderWithText } from "../../util/LoaderWithText";
12
12
  import SVGIcon from "../../util/svg/SVGIcon";
13
13
  import { FormContextType } from "../context/FormContext";
14
- import Tippy from "@tippyjs/react";
15
14
  import {
16
15
  FieldOptionsSchema,
17
16
  FormFieldSchema,
18
17
  FormFieldType,
19
18
  OutputFormatType,
20
19
  } from "../schema/FormFieldSchema";
20
+ import Tippy from "@tippyjs/react";
21
21
 
22
22
  type RenderListOptionsProps = {
23
23
  formContext: FormContextType;
@@ -102,15 +102,15 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
102
102
 
103
103
  const filteredList = query
104
104
  ? resultList.filter((item) => {
105
- const normalizedLabel = fieldConfig.dropdownFieldConfig
106
- ?.isCaseSensitive
107
- ? item.label
108
- : item.label.toLowerCase();
105
+ const normalizedLabel = fieldConfig.dropdownFieldConfig
106
+ ?.isCaseSensitive
107
+ ? item.label
108
+ : item.label.toLowerCase();
109
109
 
110
- return normalizedLabel
111
- .replace(/\s+/g, "")
112
- .includes(caseSensitive.replace(/\s+/g, ""));
113
- })
110
+ return normalizedLabel
111
+ .replace(/\s+/g, "")
112
+ .includes(caseSensitive.replace(/\s+/g, ""));
113
+ })
114
114
  : resultList;
115
115
 
116
116
  let nullGroupOptions: any[] = [];
@@ -127,13 +127,15 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
127
127
  }, {});
128
128
 
129
129
  const handleQueryCallback = useCallback(() => {
130
- if (filteredList.length == 0 && isTypeahead) {
130
+ if (filteredList.length < 5 && isTypeahead) {
131
131
  queryCallback && queryCallback(query);
132
132
  }
133
133
  }, [filteredList]);
134
134
 
135
135
  useEffect(() => {
136
- handleQueryCallback();
136
+ if (formField == FormFieldType.TYPEAHEAD_2)
137
+ queryCallback && queryCallback(query);
138
+ else handleQueryCallback();
137
139
  }, [query]);
138
140
 
139
141
  let enableCreateFlag =
@@ -143,6 +145,7 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
143
145
 
144
146
  let validTypeaheadFields = [
145
147
  FormFieldType.TYPEAHEAD,
148
+ FormFieldType.TYPEAHEAD_2,
146
149
  FormFieldType.TYPEAHEAD_MULTI_SELECT,
147
150
  ];
148
151
  let isTypeahead: boolean = validTypeaheadFields.indexOf(formField) > -1;
@@ -166,11 +169,10 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
166
169
  key={option.value}
167
170
  disabled={option.isDisabled}
168
171
  onClick={() => setTimeout(resetToDefault, 300)}
169
- className={`form-listbox-option ${
170
- selected
171
- ? " bg-gray-100 text-gray-900"
172
- : "hover:bg-gray-100 text-gray-700"
173
- }`}
172
+ className={`form-listbox-option ${selected
173
+ ? " bg-gray-100 text-gray-900"
174
+ : "hover:bg-gray-100 text-gray-700"
175
+ }`}
174
176
  value={option.value}
175
177
  >
176
178
  {fieldConfig.fieldOptionWrapper ? (
@@ -179,17 +181,30 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
179
181
  <>
180
182
  <div className="flex items-center justify-between gap-x-1.5">
181
183
  <span
182
- className={`block truncate w-full ${fieldConfig.customClassNames?.optionClassName} space-x-2 ${
183
- selected ? "font-medium" : "font-normal"
184
- }`}
184
+ className={`block truncate w-full ${fieldConfig.customClassNames?.optionClassName} space-x-2 ${selected ? "font-medium" : "font-normal"
185
+ }`}
185
186
  >
186
187
  {option.icon && (
187
188
  <span className="listbox-svg">{option.icon}</span>
188
189
  )}
189
- {option.label}
190
+ {option.tooltip ? (
191
+ <Tippy
192
+ content={
193
+ <>
194
+ {option.label}
195
+ <br />
196
+ {option.helpText}
197
+ </>
198
+ }
199
+ >
200
+ <div className="truncate">{option.label}</div>
201
+ </Tippy>
202
+ ) : (
203
+ option.label
204
+ )}
190
205
  </span>
191
206
  {isTypeahead &&
192
- !fieldConfig.dropdownFieldConfig?.isSuggestionBox ? (
207
+ !fieldConfig.dropdownFieldConfig?.isSuggestionBox ? (
193
208
  <input
194
209
  type="checkbox"
195
210
  className="form-checkbox"
@@ -205,11 +220,30 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
205
220
  )
206
221
  )}
207
222
  </div>
208
- {option.helpText && (
209
- <div className="mt-0 text-xs text-gray-500 font-normal truncate w-full !max-w-[150px]">
210
- {option.helpText}
211
- </div>
212
- )}
223
+ {option.helpText &&
224
+ (option.tooltip ? (
225
+ <Tippy
226
+ content={
227
+ <>
228
+ {option.label}
229
+ <br />
230
+ {option.helpText}
231
+ </>
232
+ }
233
+ >
234
+ <div
235
+ className={`mt-0 text-xs text-gray-500 font-normal truncate w-full ${fieldConfig.customClassNames?.optionClassName}`}
236
+ >
237
+ {option.helpText}
238
+ </div>
239
+ </Tippy>
240
+ ) : (
241
+ <div
242
+ className={`mt-0 text-xs text-gray-500 font-normal truncate w-full ${fieldConfig.customClassNames?.optionClassName}`}
243
+ >
244
+ {option.helpText}
245
+ </div>
246
+ ))}
213
247
  </>
214
248
  )}
215
249
  </ListboxOption>
@@ -217,7 +251,9 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
217
251
  };
218
252
 
219
253
  const renderList = useMemo(() => {
220
- if (filteredList.length === 0 && (!enableCreateFlag || query === ""))
254
+ if (filteredList.length === 0 && (!enableCreateFlag || query === "")) {
255
+ if (formField == FormFieldType.TYPEAHEAD_2 && query === "")
256
+ return <></>;
221
257
  return (
222
258
  <div className="form-listbox-option text-center">
223
259
  <span className="empty-content text-gray-600 font-normal">
@@ -225,6 +261,7 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
225
261
  </span>
226
262
  </div>
227
263
  );
264
+ }
228
265
 
229
266
  return (
230
267
  <>
@@ -366,28 +403,28 @@ export function renderListBoxValue(
366
403
  {values.length > 1
367
404
  ? `${values.length} selected`
368
405
  : listOptions.find((option) => option.value == value[0])?.label ||
369
- values[0]}
406
+ values[0]}
370
407
  </span>
371
408
  {getDeleteButton()}
372
409
  </span>
373
410
  ) : (
374
411
  Array.isArray(values) &&
375
- values.map((opt: any) => {
376
- const option = listOptions.find((option) => option.value == opt);
377
- if (!option && values.length == 0) return getPlaceholder();
412
+ values.map((opt: any) => {
413
+ const option = listOptions.find((option) => option.value == opt);
414
+ if (!option && values.length == 0) return getPlaceholder();
378
415
 
379
- return (
380
- <span key={option?.value} className="form-selected-badge">
381
- <Tippy content={option?.label} delay={500}>
382
- <span className="form-selected-badge-name">
383
- {option?.label}
384
- </span>
385
- </Tippy>
416
+ return (
417
+ <span key={option?.value} className="form-selected-badge">
418
+ <Tippy content={option?.label} delay={500}>
419
+ <span className="form-selected-badge-name">
420
+ {option?.label}
421
+ </span>
422
+ </Tippy>
386
423
 
387
- {getDeleteButton(opt)}
388
- </span>
389
- );
390
- })
424
+ {getDeleteButton(opt)}
425
+ </span>
426
+ );
427
+ })
391
428
  );
392
429
  };
393
430
  const getDeleteButton = (option?: string) => {
@@ -419,9 +456,10 @@ export function renderListBoxValue(
419
456
  )
420
457
  );
421
458
  };
422
- let outputFormat = fieldConfig.outputFormat
423
- ? fieldConfig.outputFormat === OutputFormatType.ARRAY
424
- : false;
459
+ let outputFormat =
460
+ fieldConfig.outputFormat != undefined
461
+ ? fieldConfig.outputFormat === OutputFormatType.STRING
462
+ : false;
425
463
  const getPlaceholder = () => (
426
464
  <span className="form-placeholder">
427
465
  {fieldConfig.placeholder || "Select any option"}