@engagebay/engagebay-form-module 1.0.7-beta.3 → 1.0.7
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 +5 -3
- package/src/api/index.ts +3 -9
- package/src/form/FormFieldUtils.ts +4 -4
- package/src/form/formfields/BusinessHoursField.tsx +4 -2
- package/src/form/formfields/DatePickerField.tsx +3 -3
- package/src/form/formfields/Typeahead.tsx +177 -163
- package/src/form/formfields/TypeaheadMultiSelect.tsx +24 -27
- package/src/form/formfields/UrlField.tsx +3 -8
- package/src/form/schema/FormFieldSchema.ts +3 -4
- package/src/form/util/RenderListOptions.tsx +12 -46
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@engagebay/engagebay-form-module",
|
|
3
|
-
"version": "1.0.7
|
|
4
|
-
"description": "Provide base form components to
|
|
3
|
+
"version": "1.0.7",
|
|
4
|
+
"description": "Provide base form components to engagebay",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
7
7
|
},
|
|
8
|
-
"author": "
|
|
8
|
+
"author": "Engagebay Developer",
|
|
9
9
|
"license": "ISC",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"react-phone-input-2": "^2.15.1",
|
|
@@ -16,6 +16,7 @@
|
|
|
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",
|
|
19
20
|
"@types/react-redux": ">=7.1.33",
|
|
20
21
|
"axios": ">=1.7.2",
|
|
21
22
|
"clsx": ">=2.1.1",
|
|
@@ -31,6 +32,7 @@
|
|
|
31
32
|
"@heroicons/react": ">=2.1.5",
|
|
32
33
|
"@reduxjs/toolkit": ">=2.2.7",
|
|
33
34
|
"@tippyjs/react": ">=4.2.6",
|
|
35
|
+
"@types/lodash": ">=4.17.7",
|
|
34
36
|
"@types/react-redux": ">=7.1.33",
|
|
35
37
|
"axios": ">=1.7.2",
|
|
36
38
|
"clsx": ">=2.1.1",
|
package/src/api/index.ts
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
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
|
-
|
|
12
4
|
const BASE_API: AxiosRequestConfig = {
|
|
13
|
-
baseURL:
|
|
5
|
+
baseURL:
|
|
6
|
+
(window as any).REACHO_BASE_URL ||
|
|
7
|
+
(window as any).parent.REACHO_BASE_URL,
|
|
14
8
|
timeout: 30000,
|
|
15
9
|
headers: {
|
|
16
10
|
"Content-Type": "application/json",
|
|
@@ -28,13 +28,13 @@ 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,
|
|
34
35
|
FormFieldType,
|
|
35
36
|
OptionMappingConfig,
|
|
36
37
|
} from "./schema/FormFieldSchema";
|
|
37
|
-
import Typeahead2 from "./formfields/Typeahead2";
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* @property {React.FC<FormFieldComponentPropSchema>} component - React component for a form field.
|
|
@@ -137,12 +137,12 @@ const formFieldComponents: FormComponentSchema = {
|
|
|
137
137
|
[FormFieldType[FormFieldType.TYPEAHEAD]]: {
|
|
138
138
|
component: Typeahead,
|
|
139
139
|
},
|
|
140
|
-
[FormFieldType[FormFieldType.TYPEAHEAD_MULTI_SELECT]]: {
|
|
141
|
-
component: TypeaheadMultiSelect,
|
|
142
|
-
},
|
|
143
140
|
[FormFieldType[FormFieldType.TYPEAHEAD_2]]: {
|
|
144
141
|
component: Typeahead2,
|
|
145
142
|
},
|
|
143
|
+
[FormFieldType[FormFieldType.TYPEAHEAD_MULTI_SELECT]]: {
|
|
144
|
+
component: TypeaheadMultiSelect,
|
|
145
|
+
},
|
|
146
146
|
[FormFieldType[FormFieldType.COMBO_SELECT]]: {
|
|
147
147
|
component: ComboSelect,
|
|
148
148
|
},
|
|
@@ -21,6 +21,7 @@ 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";
|
|
24
25
|
|
|
25
26
|
const defaultBusinessHours = {
|
|
26
27
|
MONDAY: {
|
|
@@ -123,8 +124,9 @@ export const BusinessHoursField: React.FC<FormFieldComponentPropSchema> = (
|
|
|
123
124
|
<div className="w-full sm:w-full text-end mr-[2.3em]">
|
|
124
125
|
<button
|
|
125
126
|
type="button"
|
|
126
|
-
className={`text-end text-primary cursor-pointer font-[13px] font-medium ${
|
|
127
|
-
}`
|
|
127
|
+
className={`text-end text-primary cursor-pointer font-[13px] font-medium ${
|
|
128
|
+
getValues(`${mappedName}.sessions`)?.length > 1 ? "mr-9" : ""
|
|
129
|
+
}`}
|
|
128
130
|
onClick={() => {
|
|
129
131
|
const lastEndTime =
|
|
130
132
|
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,13 +25,13 @@ 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
|
+
|
|
35
35
|
let hookProps = formContext.register(props.fieldConfig.name, registerOptions);
|
|
36
36
|
|
|
37
37
|
function getInput() {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React, {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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,185 +12,199 @@ import { RegisterOptions } from "react-hook-form";
|
|
|
12
12
|
import { FormContext } from "../context/FormContext";
|
|
13
13
|
import { getListOption, getListOptions } from "../FormFieldUtils";
|
|
14
14
|
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
23
|
+
renderListBoxValue,
|
|
24
24
|
} from "../util/RenderListOptions";
|
|
25
25
|
|
|
26
26
|
const Typeahead: React.FC<FormFieldComponentPropSchema> = (
|
|
27
|
-
|
|
27
|
+
props: FormFieldComponentPropSchema
|
|
28
28
|
) => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const [listOptions, setListOptions] = useState<FieldOptionsSchema[]>([]);
|
|
34
|
-
const [selectedValues, setSelectedValues] = useState<FieldOptionsSchema[]>(
|
|
35
|
-
[],
|
|
36
|
-
);
|
|
37
|
-
const [loading, setLoading] = useState<boolean>(true);
|
|
38
|
-
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
if (
|
|
41
|
-
!formContext.getValues(props.fieldConfig.name) &&
|
|
42
|
-
props.fieldConfig.defaultValue
|
|
43
|
-
) {
|
|
44
|
-
formContext.setValue(
|
|
29
|
+
const dynamicSelectRef = useRef<HTMLUListElement>(null);
|
|
30
|
+
const formContext = useContext(FormContext);
|
|
31
|
+
let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
|
|
32
|
+
let hookProps = formContext.register(
|
|
45
33
|
props.fieldConfig.name,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
async (_query: string | undefined) => {
|
|
54
|
-
setLoading(true);
|
|
55
|
-
try {
|
|
56
|
-
if (!props.fieldConfig.fetchUrl) return;
|
|
34
|
+
registerOptions
|
|
35
|
+
);
|
|
36
|
+
const [listOptions, setListOptions] = useState<FieldOptionsSchema[]>([]);
|
|
37
|
+
const [selectedValues, setSelectedValues] = useState<FieldOptionsSchema[]>(
|
|
38
|
+
[]
|
|
39
|
+
);
|
|
40
|
+
const [loading, setLoading] = useState<boolean>(true);
|
|
57
41
|
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
|
|
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
|
+
);
|
|
61
51
|
}
|
|
52
|
+
fetchData(undefined);
|
|
53
|
+
}, []);
|
|
62
54
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
55
|
+
const fetchData = useCallback(
|
|
56
|
+
async (_query: string | undefined) => {
|
|
57
|
+
setLoading(true);
|
|
58
|
+
try {
|
|
59
|
+
if (!props.fieldConfig.fetchUrl) return;
|
|
60
|
+
|
|
61
|
+
let url = props.fieldConfig.fetchUrl;
|
|
62
|
+
if (_query) {
|
|
63
|
+
url = url.includes("?")
|
|
64
|
+
? url + "&q=" + _query
|
|
65
|
+
: url + "?q=" + _query;
|
|
66
|
+
}
|
|
67
|
+
|
|
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
|
+
);
|
|
90
98
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
99
|
+
const fetchValue = async (value: string) => {
|
|
100
|
+
try {
|
|
101
|
+
if (
|
|
102
|
+
props.fieldConfig.ignoreFetchValue ||
|
|
103
|
+
!props.fieldConfig.fetchUrl
|
|
104
|
+
) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
96
107
|
|
|
97
|
-
|
|
108
|
+
let url = props.fieldConfig.fetchUrl;
|
|
98
109
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
110
|
+
if (value)
|
|
111
|
+
url = url.includes("?")
|
|
112
|
+
? url + "&values=" + value
|
|
113
|
+
: url + "?values=" + value;
|
|
103
114
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
+
};
|
|
129
|
+
|
|
130
|
+
const updateListOptions = (data: any) => {
|
|
131
|
+
const resData: FieldOptionsSchema = getListOption(
|
|
132
|
+
data,
|
|
133
|
+
props.fieldConfig.optionsConfig
|
|
111
134
|
);
|
|
112
|
-
setSelectedValues([
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
};
|
|
135
|
+
setSelectedValues((perv) => [resData]);
|
|
136
|
+
formContext.setValue(props.fieldConfig.name, resData.value);
|
|
137
|
+
};
|
|
116
138
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
+
);
|
|
125
157
|
|
|
126
|
-
|
|
127
|
-
|
|
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);
|
|
158
|
+
// If the value matches, set it to null, otherwise set it to val
|
|
159
|
+
const newValue = currentValue === val ? null : val;
|
|
141
160
|
|
|
142
|
-
|
|
143
|
-
|
|
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
|
+
}
|
|
144
205
|
|
|
145
|
-
|
|
146
|
-
|
|
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>
|
|
206
|
+
return (
|
|
207
|
+
<RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
|
|
186
208
|
);
|
|
187
|
-
}
|
|
188
|
-
if (props.fieldConfig.hideWhenNoResults && listOptions.length == 0) {
|
|
189
|
-
return <></>;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return (
|
|
193
|
-
<RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
|
|
194
|
-
);
|
|
195
209
|
};
|
|
196
210
|
export default Typeahead;
|
|
@@ -23,10 +23,9 @@ import RenderListOptions, {
|
|
|
23
23
|
} from "../util/RenderListOptions";
|
|
24
24
|
// import _ from "lodash";
|
|
25
25
|
import axios from "axios";
|
|
26
|
-
import { getAxiosInstance } from "../../api";
|
|
27
26
|
|
|
28
27
|
const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
|
|
29
|
-
props: FormFieldComponentPropSchema
|
|
28
|
+
props: FormFieldComponentPropSchema
|
|
30
29
|
) => {
|
|
31
30
|
const dynamicSelectRef = useRef<HTMLUListElement>(null);
|
|
32
31
|
const formContext = useContext(FormContext);
|
|
@@ -34,7 +33,7 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
|
|
|
34
33
|
let hookProps = formContext.register(props.fieldConfig.name, registerOptions);
|
|
35
34
|
const [listOptions, setListOptions] = useState<FieldOptionsSchema[]>([]);
|
|
36
35
|
const [selectedValues, setSelectedValues] = useState<FieldOptionsSchema[]>(
|
|
37
|
-
[]
|
|
36
|
+
[]
|
|
38
37
|
);
|
|
39
38
|
const [loading, setLoading] = useState<boolean>(true);
|
|
40
39
|
|
|
@@ -45,7 +44,7 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
|
|
|
45
44
|
) {
|
|
46
45
|
formContext.setValue(
|
|
47
46
|
props.fieldConfig.name,
|
|
48
|
-
props.fieldConfig.defaultValue
|
|
47
|
+
props.fieldConfig.defaultValue
|
|
49
48
|
);
|
|
50
49
|
}
|
|
51
50
|
fetchData(undefined);
|
|
@@ -61,16 +60,14 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
|
|
|
61
60
|
if (_query) {
|
|
62
61
|
url = url.includes("?") ? url + "&q=" + _query : url + "?q=" + _query;
|
|
63
62
|
}
|
|
64
|
-
const axiosInstance = getAxiosInstance(
|
|
65
|
-
formContext.axiosInstance,
|
|
66
|
-
props.fieldConfig,
|
|
67
|
-
);
|
|
68
63
|
|
|
69
|
-
let response = await
|
|
64
|
+
let response = await (props.fieldConfig.disableHeaderInFetch
|
|
65
|
+
? axios.get(url)
|
|
66
|
+
: formContext.axiosInstance?.get(url));
|
|
70
67
|
if (response?.data) {
|
|
71
68
|
const data: FieldOptionsSchema[] = getListOptions(
|
|
72
69
|
response?.data,
|
|
73
|
-
props.fieldConfig.optionsConfig
|
|
70
|
+
props.fieldConfig.optionsConfig
|
|
74
71
|
);
|
|
75
72
|
setListOptions([...data]);
|
|
76
73
|
let values = formContext.getValues(props.fieldConfig.name) || [];
|
|
@@ -80,7 +77,7 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
|
|
|
80
77
|
(data.length === 0 || // If 'data' is empty
|
|
81
78
|
(!values.every((v: string) => data.some((i) => i.value === v)) && // Ensure none of 'values' match 'data'
|
|
82
79
|
!values.every((v: string) =>
|
|
83
|
-
selectedValues.some((i) => i.value === v)
|
|
80
|
+
selectedValues.some((i) => i.value === v)
|
|
84
81
|
))) // Ensure none of 'values' match 'selectedValues'
|
|
85
82
|
) {
|
|
86
83
|
fetchValue(values); // Call 'fetchValue()' if all conditions are met
|
|
@@ -97,7 +94,7 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
|
|
|
97
94
|
setLoading(false);
|
|
98
95
|
}
|
|
99
96
|
},
|
|
100
|
-
[props.fieldConfig.fetchUrl]
|
|
97
|
+
[props.fieldConfig.fetchUrl]
|
|
101
98
|
);
|
|
102
99
|
|
|
103
100
|
const fetchValue = async (value: string) => {
|
|
@@ -108,7 +105,7 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
|
|
|
108
105
|
|
|
109
106
|
let url = props.fieldConfig.fetchUrl;
|
|
110
107
|
|
|
111
|
-
url = url.includes("?")
|
|
108
|
+
url = url = url.includes("?")
|
|
112
109
|
? url + "&values=" + value
|
|
113
110
|
: url + "?values=" + value;
|
|
114
111
|
|
|
@@ -118,7 +115,7 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
|
|
|
118
115
|
if (response?.data) {
|
|
119
116
|
const data: FieldOptionsSchema[] = getListOptions(
|
|
120
117
|
response?.data,
|
|
121
|
-
props.fieldConfig.optionsConfig
|
|
118
|
+
props.fieldConfig.optionsConfig
|
|
122
119
|
);
|
|
123
120
|
let values: any[] = formContext.getValues(props.fieldConfig.name) || [];
|
|
124
121
|
setSelectedValues(
|
|
@@ -126,22 +123,22 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
|
|
|
126
123
|
...data,
|
|
127
124
|
props.fieldConfig.dropdownFieldConfig?.isSuggestionBox && values
|
|
128
125
|
? values
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
126
|
+
.filter(
|
|
127
|
+
(value) =>
|
|
128
|
+
data.some((d) => d.value !== value) || data.length == 0
|
|
129
|
+
)
|
|
130
|
+
.map((val) => ({ label: val, value: val }))
|
|
134
131
|
: [],
|
|
135
|
-
].flat()
|
|
132
|
+
].flat()
|
|
136
133
|
);
|
|
137
134
|
}
|
|
138
|
-
} catch (err) {
|
|
135
|
+
} catch (err) {}
|
|
139
136
|
};
|
|
140
137
|
|
|
141
138
|
const updateListOptions = (data: any) => {
|
|
142
139
|
const resData: FieldOptionsSchema = getListOption(
|
|
143
140
|
data,
|
|
144
|
-
props.fieldConfig.optionsConfig
|
|
141
|
+
props.fieldConfig.optionsConfig
|
|
145
142
|
);
|
|
146
143
|
setSelectedValues((prev) => [...prev, resData]);
|
|
147
144
|
let result = formContext.getValues(props.fieldConfig.name) || [];
|
|
@@ -163,14 +160,14 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
|
|
|
163
160
|
className={"relative form-listbox flex-1 overflow-hidden"}
|
|
164
161
|
onChange={(selectedOptions) => {
|
|
165
162
|
const chossenOptions = listOptions.filter((op) =>
|
|
166
|
-
selectedOptions.includes(op.value)
|
|
163
|
+
selectedOptions.includes(op.value)
|
|
167
164
|
);
|
|
168
|
-
|
|
165
|
+
setSelectedValues((prev) => [...prev, ...chossenOptions]);
|
|
169
166
|
handleChange(
|
|
170
167
|
selectedOptions,
|
|
171
168
|
formContext,
|
|
172
169
|
props.fieldConfig,
|
|
173
|
-
props.onChange
|
|
170
|
+
props.onChange
|
|
174
171
|
);
|
|
175
172
|
}}
|
|
176
173
|
multiple
|
|
@@ -179,7 +176,7 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
|
|
|
179
176
|
className={
|
|
180
177
|
props.fieldConfig.customClassNames?.fieldClassName
|
|
181
178
|
? "form-listbox-select " +
|
|
182
|
-
|
|
179
|
+
props.fieldConfig.customClassNames?.fieldClassName
|
|
183
180
|
: "form-listbox-select"
|
|
184
181
|
}
|
|
185
182
|
>
|
|
@@ -187,7 +184,7 @@ const TypeaheadMultiSelect: React.FC<FormFieldComponentPropSchema> = (
|
|
|
187
184
|
formContext,
|
|
188
185
|
props.fieldConfig,
|
|
189
186
|
[...selectedValues, ...listOptions],
|
|
190
|
-
props.onChange
|
|
187
|
+
props.onChange
|
|
191
188
|
)}
|
|
192
189
|
</ListboxButton>
|
|
193
190
|
<RenderListOptions
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useContext } from "react";
|
|
2
2
|
import { RegisterOptions } from "react-hook-form";
|
|
3
3
|
import { FormContext } from "../context/FormContext";
|
|
4
|
-
import { FormFieldComponentPropSchema
|
|
4
|
+
import { FormFieldComponentPropSchema } from "../schema/FormFieldSchema";
|
|
5
5
|
import { Input } from "@headlessui/react";
|
|
6
6
|
import RenderFormField from "../util/RenderFormField";
|
|
7
7
|
import { handleChange, registerFormField } from "../util";
|
|
@@ -11,12 +11,7 @@ const UrlField: React.FC<FormFieldComponentPropSchema> = (
|
|
|
11
11
|
) => {
|
|
12
12
|
const formContext = useContext(FormContext);
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
const fieldConfigWithUrlPattern = props.fieldConfig.formFieldPattern
|
|
16
|
-
? props.fieldConfig
|
|
17
|
-
: { ...props.fieldConfig, formFieldPattern: [FormFieldPatternsImpl.URL] };
|
|
18
|
-
|
|
19
|
-
let registerOptions: RegisterOptions = registerFormField(fieldConfigWithUrlPattern);
|
|
14
|
+
let registerOptions: RegisterOptions = registerFormField(props.fieldConfig);
|
|
20
15
|
|
|
21
16
|
let hookProps = formContext.register(props.fieldConfig.name, registerOptions);
|
|
22
17
|
|
|
@@ -52,7 +47,7 @@ const UrlField: React.FC<FormFieldComponentPropSchema> = (
|
|
|
52
47
|
};
|
|
53
48
|
|
|
54
49
|
return (
|
|
55
|
-
<RenderFormField fieldConfig={
|
|
50
|
+
<RenderFormField fieldConfig={props.fieldConfig} getInput={getInput} />
|
|
56
51
|
);
|
|
57
52
|
};
|
|
58
53
|
export default UrlField;
|
|
@@ -106,6 +106,7 @@ export type FormFieldSchema = {
|
|
|
106
106
|
max?: number;
|
|
107
107
|
min?: number;
|
|
108
108
|
rows?: number;
|
|
109
|
+
allowedMinQueryLength?: number;
|
|
109
110
|
defaultValue?: string | string[] | {} | boolean;
|
|
110
111
|
options?: FieldOptionsSchema[];
|
|
111
112
|
minDate?: Date | null | undefined;
|
|
@@ -128,15 +129,14 @@ export type FormFieldSchema = {
|
|
|
128
129
|
decimalAllowed?: boolean;
|
|
129
130
|
errorMessage?: string;
|
|
130
131
|
submitOnChange?: boolean;
|
|
132
|
+
isMultiple?: boolean;
|
|
131
133
|
formFieldPattern?: FormFieldPatternsImpl[];
|
|
132
134
|
fetchUrl?: string;
|
|
133
|
-
fetchSavedDataUrl?: string;
|
|
134
135
|
postUrl?: string;
|
|
136
|
+
fetchSavedDataUrl?: string;
|
|
135
137
|
fileAccept?: string;
|
|
136
138
|
icon?: ReactNode;
|
|
137
139
|
outputFormat?: OutputFormatType;
|
|
138
|
-
isMultiple?: boolean;
|
|
139
|
-
allowedMinQueryLength?: number;
|
|
140
140
|
children?: FormFieldSchema[];
|
|
141
141
|
defaultOptions?: FieldOptionsSchema[];
|
|
142
142
|
optionsConfig?: OptionMappingConfig;
|
|
@@ -205,7 +205,6 @@ export type FieldOptionsSchema = {
|
|
|
205
205
|
isDisabled?: boolean;
|
|
206
206
|
helpText?: string;
|
|
207
207
|
groupName?: string;
|
|
208
|
-
tooltip?: string;
|
|
209
208
|
icon?: React.ReactNode;
|
|
210
209
|
};
|
|
211
210
|
|
|
@@ -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";
|
|
14
15
|
import {
|
|
15
16
|
FieldOptionsSchema,
|
|
16
17
|
FormFieldSchema,
|
|
17
18
|
FormFieldType,
|
|
18
19
|
OutputFormatType,
|
|
19
20
|
} from "../schema/FormFieldSchema";
|
|
20
|
-
import Tippy from "@tippyjs/react";
|
|
21
21
|
|
|
22
22
|
type RenderListOptionsProps = {
|
|
23
23
|
formContext: FormContextType;
|
|
@@ -127,7 +127,7 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
|
|
|
127
127
|
}, {});
|
|
128
128
|
|
|
129
129
|
const handleQueryCallback = useCallback(() => {
|
|
130
|
-
if (filteredList.length
|
|
130
|
+
if (filteredList.length == 0 && isTypeahead) {
|
|
131
131
|
queryCallback && queryCallback(query);
|
|
132
132
|
}
|
|
133
133
|
}, [filteredList]);
|
|
@@ -189,21 +189,7 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
|
|
|
189
189
|
{option.icon && (
|
|
190
190
|
<span className="listbox-svg">{option.icon}</span>
|
|
191
191
|
)}
|
|
192
|
-
{option.
|
|
193
|
-
<Tippy
|
|
194
|
-
content={
|
|
195
|
-
<>
|
|
196
|
-
{option.label}
|
|
197
|
-
<br />
|
|
198
|
-
{option.helpText}
|
|
199
|
-
</>
|
|
200
|
-
}
|
|
201
|
-
>
|
|
202
|
-
<div className="truncate">{option.label}</div>
|
|
203
|
-
</Tippy>
|
|
204
|
-
) : (
|
|
205
|
-
option.label
|
|
206
|
-
)}
|
|
192
|
+
{option.label}
|
|
207
193
|
</span>
|
|
208
194
|
{isTypeahead &&
|
|
209
195
|
!fieldConfig.dropdownFieldConfig?.isSuggestionBox ? (
|
|
@@ -222,30 +208,11 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
|
|
|
222
208
|
)
|
|
223
209
|
)}
|
|
224
210
|
</div>
|
|
225
|
-
{option.helpText &&
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
{option.label}
|
|
231
|
-
<br />
|
|
232
|
-
{option.helpText}
|
|
233
|
-
</>
|
|
234
|
-
}
|
|
235
|
-
>
|
|
236
|
-
<div
|
|
237
|
-
className={`mt-0 text-xs text-gray-500 font-normal truncate w-full ${fieldConfig.customClassNames?.optionClassName}`}
|
|
238
|
-
>
|
|
239
|
-
{option.helpText}
|
|
240
|
-
</div>
|
|
241
|
-
</Tippy>
|
|
242
|
-
) : (
|
|
243
|
-
<div
|
|
244
|
-
className={`mt-0 text-xs text-gray-500 font-normal truncate w-full ${fieldConfig.customClassNames?.optionClassName}`}
|
|
245
|
-
>
|
|
246
|
-
{option.helpText}
|
|
247
|
-
</div>
|
|
248
|
-
))}
|
|
211
|
+
{option.helpText && (
|
|
212
|
+
<div className="mt-0 text-xs text-gray-500 font-normal truncate w-full !max-w-[150px]">
|
|
213
|
+
{option.helpText}
|
|
214
|
+
</div>
|
|
215
|
+
)}
|
|
249
216
|
</>
|
|
250
217
|
)}
|
|
251
218
|
</ListboxOption>
|
|
@@ -324,7 +291,7 @@ const RenderListOptions = forwardRef<HTMLUListElement, RenderListOptionsProps>(
|
|
|
324
291
|
fill="currentColor"
|
|
325
292
|
aria-hidden="true"
|
|
326
293
|
data-slot="icon"
|
|
327
|
-
className="h-
|
|
294
|
+
className="h-5 w-5 text-gray-400"
|
|
328
295
|
>
|
|
329
296
|
<path
|
|
330
297
|
fillRule="evenodd"
|
|
@@ -458,10 +425,9 @@ export function renderListBoxValue(
|
|
|
458
425
|
)
|
|
459
426
|
);
|
|
460
427
|
};
|
|
461
|
-
let outputFormat =
|
|
462
|
-
fieldConfig.outputFormat
|
|
463
|
-
|
|
464
|
-
: false;
|
|
428
|
+
let outputFormat = fieldConfig.outputFormat
|
|
429
|
+
? fieldConfig.outputFormat === OutputFormatType.ARRAY
|
|
430
|
+
: false;
|
|
465
431
|
const getPlaceholder = () => (
|
|
466
432
|
<span className="form-placeholder">
|
|
467
433
|
{fieldConfig.placeholder || "Select any option"}
|