@engagebay/engagebay-form-module 1.0.3 → 1.0.6
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 +1 -1
- package/src/form/FormFieldUtils.ts +4 -0
- package/src/form/formfields/DatePickerField.tsx +21 -13
- package/src/form/formfields/NumberField.tsx +55 -50
- package/src/form/formfields/Typeahead2.tsx +284 -0
- package/src/form/schema/FormFieldSchema.ts +4 -0
- package/src/form/util/RenderListOptions.tsx +9 -2
package/package.json
CHANGED
|
@@ -28,6 +28,7 @@ import TimeField from "./formfields/TimeField";
|
|
|
28
28
|
import Typeahead from "./formfields/Typeahead";
|
|
29
29
|
import TypeaheadMultiSelect from "./formfields/TypeaheadMultiSelect";
|
|
30
30
|
import UrlField from "./formfields/UrlField";
|
|
31
|
+
import Typeahead2 from "./formfields/Typeahead2";
|
|
31
32
|
import {
|
|
32
33
|
FieldOptionsSchema,
|
|
33
34
|
FormFieldComponentPropSchema,
|
|
@@ -136,6 +137,9 @@ const formFieldComponents: FormComponentSchema = {
|
|
|
136
137
|
[FormFieldType[FormFieldType.TYPEAHEAD]]: {
|
|
137
138
|
component: Typeahead,
|
|
138
139
|
},
|
|
140
|
+
[FormFieldType[FormFieldType.TYPEAHEAD_2]]: {
|
|
141
|
+
component: Typeahead2,
|
|
142
|
+
},
|
|
139
143
|
[FormFieldType[FormFieldType.TYPEAHEAD_MULTI_SELECT]]: {
|
|
140
144
|
component: TypeaheadMultiSelect,
|
|
141
145
|
},
|
|
@@ -35,14 +35,28 @@ const DatePicker: React.FC<FormFieldComponentPropSchema> = (
|
|
|
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
|
-
|
|
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
|
-
|
|
9
|
+
props: FormFieldComponentPropSchema,
|
|
10
10
|
) => {
|
|
11
|
-
|
|
11
|
+
const formContext = useContext(FormContext);
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
|
|
14
14
|
|
|
15
|
-
|
|
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
|
-
|
|
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 (
|
|
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;
|
|
@@ -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;
|
|
@@ -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",
|
|
@@ -105,6 +106,7 @@ export type FormFieldSchema = {
|
|
|
105
106
|
max?: number;
|
|
106
107
|
min?: number;
|
|
107
108
|
rows?: number;
|
|
109
|
+
allowedMinQueryLength?: number;
|
|
108
110
|
defaultValue?: string | string[] | {} | boolean;
|
|
109
111
|
options?: FieldOptionsSchema[];
|
|
110
112
|
minDate?: Date | null | undefined;
|
|
@@ -127,9 +129,11 @@ export type FormFieldSchema = {
|
|
|
127
129
|
decimalAllowed?: boolean;
|
|
128
130
|
errorMessage?: string;
|
|
129
131
|
submitOnChange?: boolean;
|
|
132
|
+
isMultiple?: boolean;
|
|
130
133
|
formFieldPattern?: FormFieldPatternsImpl[];
|
|
131
134
|
fetchUrl?: string;
|
|
132
135
|
postUrl?: string;
|
|
136
|
+
fetchSavedDataUrl?: string;
|
|
133
137
|
fileAccept?: string;
|
|
134
138
|
icon?: ReactNode;
|
|
135
139
|
outputFormat?: OutputFormatType;
|
|
@@ -11,6 +11,7 @@ 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";
|
|
14
15
|
import {
|
|
15
16
|
FieldOptionsSchema,
|
|
16
17
|
FormFieldSchema,
|
|
@@ -132,7 +133,9 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
|
|
|
132
133
|
}, [filteredList]);
|
|
133
134
|
|
|
134
135
|
useEffect(() => {
|
|
135
|
-
|
|
136
|
+
if (formField == FormFieldType.TYPEAHEAD_2)
|
|
137
|
+
queryCallback && queryCallback(query);
|
|
138
|
+
else handleQueryCallback();
|
|
136
139
|
}, [query]);
|
|
137
140
|
|
|
138
141
|
let enableCreateFlag =
|
|
@@ -142,6 +145,7 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
|
|
|
142
145
|
|
|
143
146
|
let validTypeaheadFields = [
|
|
144
147
|
FormFieldType.TYPEAHEAD,
|
|
148
|
+
FormFieldType.TYPEAHEAD_2,
|
|
145
149
|
FormFieldType.TYPEAHEAD_MULTI_SELECT,
|
|
146
150
|
];
|
|
147
151
|
let isTypeahead: boolean = validTypeaheadFields.indexOf(formField) > -1;
|
|
@@ -216,7 +220,9 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
|
|
|
216
220
|
};
|
|
217
221
|
|
|
218
222
|
const renderList = useMemo(() => {
|
|
219
|
-
if (filteredList.length === 0 && (!enableCreateFlag || query === ""))
|
|
223
|
+
if (filteredList.length === 0 && (!enableCreateFlag || query === "")) {
|
|
224
|
+
if (formField == FormFieldType.TYPEAHEAD_2 && query === "")
|
|
225
|
+
return <></>;
|
|
220
226
|
return (
|
|
221
227
|
<div className="form-listbox-option text-center">
|
|
222
228
|
<span className="empty-content text-gray-600 font-normal">
|
|
@@ -224,6 +230,7 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
|
|
|
224
230
|
</span>
|
|
225
231
|
</div>
|
|
226
232
|
);
|
|
233
|
+
}
|
|
227
234
|
|
|
228
235
|
return (
|
|
229
236
|
<>
|