react-core-ts 2.0.12 → 2.0.13
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/dist/ReactAutoCompleteTableView.d.ts +53 -0
- package/dist/ReactAutoCompleteTableView.js +653 -0
- package/dist/ReactAutoCompleteTableView.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/docs/App.tsx +20 -1
- package/package.json +1 -1
- package/src/ReactAutoCompleteTableView.tsx +969 -0
- package/src/index.tsx +1 -0
- package/dist/styles/styles/components/autocomplete.css +0 -744
- package/dist/styles/styles/components/selectedautocomplete.css +0 -179
- package/dist/styles/styles/components/tooltip.css +0 -497
- package/dist/styles/styles/components/treedropdown.css +0 -97
- package/dist/styles/styles/global.css +0 -8
|
@@ -0,0 +1,969 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { FieldErrors } from "react-hook-form";
|
|
3
|
+
|
|
4
|
+
import CustomIcons from "./components/customIcons";
|
|
5
|
+
import Spinner from "./components/loader/Spinner";
|
|
6
|
+
import Portal from "./components/portal";
|
|
7
|
+
import { debounce } from "./utilities/debounce";
|
|
8
|
+
import { applyPositionClass } from "./utilities/getPosition";
|
|
9
|
+
import { DropArrow } from "./utilities/icons";
|
|
10
|
+
|
|
11
|
+
type valueProps = {
|
|
12
|
+
id?: string | number;
|
|
13
|
+
name: string;
|
|
14
|
+
label?: string;
|
|
15
|
+
param1?: string | number | null;
|
|
16
|
+
param2?: string | number | null;
|
|
17
|
+
param3?: string | number | null;
|
|
18
|
+
param4?: string | number | null;
|
|
19
|
+
from?: number;
|
|
20
|
+
};
|
|
21
|
+
interface AutoSuggestionInputProps {
|
|
22
|
+
id?: string;
|
|
23
|
+
label?: string;
|
|
24
|
+
fullWidth?: boolean;
|
|
25
|
+
className?: string;
|
|
26
|
+
autoFocus?: boolean;
|
|
27
|
+
required?: boolean;
|
|
28
|
+
hideClear?: boolean;
|
|
29
|
+
value?: valueProps;
|
|
30
|
+
onChange: (value?: valueProps) => void;
|
|
31
|
+
data?: any[];
|
|
32
|
+
type?: "custom_select" | "auto_complete" | "custom_search_select";
|
|
33
|
+
placeholder?: string;
|
|
34
|
+
getData?: ((key?: string) => Promise<any>) | ((key?: string) => any);
|
|
35
|
+
errors?: FieldErrors;
|
|
36
|
+
name: string;
|
|
37
|
+
readOnly?: boolean;
|
|
38
|
+
disabled?: boolean;
|
|
39
|
+
autoFilter?: boolean;
|
|
40
|
+
|
|
41
|
+
insideOpen?: boolean;
|
|
42
|
+
isClose?: boolean;
|
|
43
|
+
noLocalFilter?: boolean;
|
|
44
|
+
isStaticList?: boolean;
|
|
45
|
+
isCustomPlaceholder?: boolean;
|
|
46
|
+
checkParams?: string[];
|
|
47
|
+
fromPrefix?: boolean;
|
|
48
|
+
hasParentError?: boolean;
|
|
49
|
+
width?: number;
|
|
50
|
+
labelTitle?: string;
|
|
51
|
+
isModern?: boolean;
|
|
52
|
+
size?: "xs" | "sm" | "md" | "lg" | "xxs";
|
|
53
|
+
tableView?: boolean;
|
|
54
|
+
dropdownMinWidth?: number; // Minimum additional width in pixels to add to dropdown
|
|
55
|
+
additionalColumns?: Array<{
|
|
56
|
+
key: string; // Field name in the suggestion object
|
|
57
|
+
width?: number; // Optional width in pixels (if not set, uses flex-1 for equal width)
|
|
58
|
+
order?: number; // Optional order/position (lower numbers appear first, default maintains array order)
|
|
59
|
+
}>; // Additional columns to display
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const ReactAutoCompleteTableView: React.FC<AutoSuggestionInputProps> = ({
|
|
63
|
+
label,
|
|
64
|
+
onChange,
|
|
65
|
+
|
|
66
|
+
getData,
|
|
67
|
+
data,
|
|
68
|
+
errors,
|
|
69
|
+
required = false,
|
|
70
|
+
autoFocus,
|
|
71
|
+
name,
|
|
72
|
+
fullWidth,
|
|
73
|
+
placeholder,
|
|
74
|
+
id,
|
|
75
|
+
className: propsClassName,
|
|
76
|
+
type = "custom_select",
|
|
77
|
+
readOnly,
|
|
78
|
+
disabled = false,
|
|
79
|
+
value,
|
|
80
|
+
autoFilter = false,
|
|
81
|
+
insideOpen = false,
|
|
82
|
+
isClose = true,
|
|
83
|
+
noLocalFilter = false,
|
|
84
|
+
isStaticList = false,
|
|
85
|
+
isCustomPlaceholder = false,
|
|
86
|
+
checkParams,
|
|
87
|
+
width,
|
|
88
|
+
hasParentError,
|
|
89
|
+
fromPrefix,
|
|
90
|
+
labelTitle,
|
|
91
|
+
isModern = true,
|
|
92
|
+
size = "md",
|
|
93
|
+
dropdownMinWidth = 200,
|
|
94
|
+
tableView = false,
|
|
95
|
+
additionalColumns = [],
|
|
96
|
+
}) => {
|
|
97
|
+
const [inputValue, setInputValue] = useState<any>(value?.name ?? "");
|
|
98
|
+
const [isHovered, setIsHovered] = useState<boolean>(false);
|
|
99
|
+
const [isLoading, setIsLoading] = useState<boolean>(false);
|
|
100
|
+
const [dropOpen, setDropOpen] = useState<boolean>(false);
|
|
101
|
+
|
|
102
|
+
const [isDisabled, setIsDisabled] = useState<boolean>(disabled);
|
|
103
|
+
const [showClose, setShowClose] = useState(false);
|
|
104
|
+
const [showToolTip, setShowTooltip] = useState(false);
|
|
105
|
+
const [tooltipIsHovered, setTooltipIsHovered] = useState(false);
|
|
106
|
+
const [suggestions, setSuggestions] = useState<any>([]);
|
|
107
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
108
|
+
const adorementRef = useRef<HTMLDivElement>(null);
|
|
109
|
+
const dropdownref = useRef<HTMLDivElement>(null);
|
|
110
|
+
const dropBtnRef = useRef<HTMLButtonElement>(null);
|
|
111
|
+
const timerRef = useRef<number>(0); // To fix the no results found issue on second type
|
|
112
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
113
|
+
const itemRefs = useRef<(HTMLLIElement | null)[]>([]);
|
|
114
|
+
const scrollContainerRef = useRef<HTMLUListElement | null>(null);
|
|
115
|
+
const [dropPosition, setDropPosition] = useState<any>({
|
|
116
|
+
top: 0,
|
|
117
|
+
left: 0,
|
|
118
|
+
bottom: 0,
|
|
119
|
+
right: 0,
|
|
120
|
+
});
|
|
121
|
+
const checkIncludes = (
|
|
122
|
+
mainString: string,
|
|
123
|
+
subString: string,
|
|
124
|
+
param1?: string | number,
|
|
125
|
+
param2?: string | number,
|
|
126
|
+
param3?: string | number,
|
|
127
|
+
param4?: string | number
|
|
128
|
+
): boolean => {
|
|
129
|
+
const checkIncludesIfExists = (
|
|
130
|
+
mainString?: string | number,
|
|
131
|
+
substring?: string
|
|
132
|
+
) => {
|
|
133
|
+
return substring &&
|
|
134
|
+
mainString &&
|
|
135
|
+
typeof mainString === "string" &&
|
|
136
|
+
typeof subString === "string"
|
|
137
|
+
? mainString
|
|
138
|
+
.toString()
|
|
139
|
+
.toLowerCase()
|
|
140
|
+
.startsWith(substring.toString().toLowerCase())
|
|
141
|
+
: false;
|
|
142
|
+
};
|
|
143
|
+
const checkArrayContains = (
|
|
144
|
+
strArray: string[] | undefined,
|
|
145
|
+
param: string
|
|
146
|
+
): boolean => strArray?.includes(param) ?? false;
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
checkIncludesIfExists(mainString, subString) ||
|
|
150
|
+
(!!checkParams &&
|
|
151
|
+
((checkArrayContains(checkParams, "param1") &&
|
|
152
|
+
checkIncludesIfExists(param1, subString)) ||
|
|
153
|
+
(checkArrayContains(checkParams, "param2") &&
|
|
154
|
+
checkIncludesIfExists(param2, subString)) ||
|
|
155
|
+
(checkArrayContains(checkParams, "param3") &&
|
|
156
|
+
checkIncludesIfExists(param3, subString)) ||
|
|
157
|
+
(checkArrayContains(checkParams, "param4") &&
|
|
158
|
+
checkIncludesIfExists(param4, subString))))
|
|
159
|
+
);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const getHeight = () => {
|
|
163
|
+
switch (size) {
|
|
164
|
+
case "sm":
|
|
165
|
+
return " h-[34px] ";
|
|
166
|
+
default:
|
|
167
|
+
return " ";
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
172
|
+
const { value } = event.target;
|
|
173
|
+
timerRef.current = 0;
|
|
174
|
+
|
|
175
|
+
setInputValue(value);
|
|
176
|
+
handleValChange(value);
|
|
177
|
+
if (!value) {
|
|
178
|
+
setInputValue("");
|
|
179
|
+
onChange({ id: undefined, name: "", from: 2 });
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const handleValChange = useCallback(
|
|
184
|
+
debounce(
|
|
185
|
+
(value: string) => {
|
|
186
|
+
setDropOpen(true);
|
|
187
|
+
onChange({ id: undefined, name: "", from: 1 });
|
|
188
|
+
if (value.trim() === "" && type === "auto_complete") {
|
|
189
|
+
setSuggestions([]);
|
|
190
|
+
if (autoFilter) {
|
|
191
|
+
handleDropData("*");
|
|
192
|
+
} else {
|
|
193
|
+
setDropOpen(false);
|
|
194
|
+
}
|
|
195
|
+
} else {
|
|
196
|
+
handleDropData(value);
|
|
197
|
+
}
|
|
198
|
+
setTimeout(() => {
|
|
199
|
+
timerRef.current = 1;
|
|
200
|
+
}, 200);
|
|
201
|
+
},
|
|
202
|
+
isStaticList ? 0 : 1000
|
|
203
|
+
),
|
|
204
|
+
[]
|
|
205
|
+
);
|
|
206
|
+
const handleDropData = (value?: string) => {
|
|
207
|
+
if (type === "auto_complete") {
|
|
208
|
+
if (isStaticList) {
|
|
209
|
+
loadStaticData();
|
|
210
|
+
const filteredData =
|
|
211
|
+
value && value !== "*"
|
|
212
|
+
? data?.filter((item) =>
|
|
213
|
+
checkIncludes(
|
|
214
|
+
item.name,
|
|
215
|
+
value,
|
|
216
|
+
item.param1,
|
|
217
|
+
item.param2,
|
|
218
|
+
item.param3
|
|
219
|
+
)
|
|
220
|
+
)
|
|
221
|
+
: data;
|
|
222
|
+
setSuggestions(filteredData);
|
|
223
|
+
} else handlePickSuggestions(value);
|
|
224
|
+
} else if (
|
|
225
|
+
(type === "custom_select" || type === "custom_search_select") &&
|
|
226
|
+
(!data || data?.length === 0)
|
|
227
|
+
) {
|
|
228
|
+
handlePickSuggestions();
|
|
229
|
+
} else if (type === "custom_search_select" && data && data?.length > 0) {
|
|
230
|
+
const filteredData =
|
|
231
|
+
value && value !== "*"
|
|
232
|
+
? data.filter((item) =>
|
|
233
|
+
checkIncludes(
|
|
234
|
+
item.name,
|
|
235
|
+
value,
|
|
236
|
+
item.param1,
|
|
237
|
+
item.param2,
|
|
238
|
+
item.param3
|
|
239
|
+
)
|
|
240
|
+
)
|
|
241
|
+
: data;
|
|
242
|
+
setSuggestions(filteredData);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
const handlePickSuggestions = async (value?: string) => {
|
|
246
|
+
if (getData) {
|
|
247
|
+
setIsLoading(true);
|
|
248
|
+
try {
|
|
249
|
+
const fetchedSuggestions = await getData?.(value);
|
|
250
|
+
setSuggestions(fetchedSuggestions);
|
|
251
|
+
setIsLoading(false);
|
|
252
|
+
} catch (error) {
|
|
253
|
+
setIsLoading(false);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const onLabelClick = () => {
|
|
259
|
+
if (!isDisabled) {
|
|
260
|
+
inputRef?.current?.focus();
|
|
261
|
+
if (autoFilter && inputValue === "") {
|
|
262
|
+
handleValChange("*");
|
|
263
|
+
} else if (
|
|
264
|
+
(type === "custom_select" || type === "custom_search_select") &&
|
|
265
|
+
data &&
|
|
266
|
+
data?.length > 0
|
|
267
|
+
) {
|
|
268
|
+
handleDropData();
|
|
269
|
+
setDropOpen(true);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
274
|
+
if (
|
|
275
|
+
dropdownref.current &&
|
|
276
|
+
!dropdownref.current.contains(event.target as Node)
|
|
277
|
+
) {
|
|
278
|
+
setDropOpen(false);
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
const checkIsEmptyField = (): boolean => {
|
|
282
|
+
if (inputValue === undefined) return true;
|
|
283
|
+
else return (inputValue?.toString().length ?? 0) <= 0 ? true : false;
|
|
284
|
+
};
|
|
285
|
+
const loadStaticData = async () => {
|
|
286
|
+
if (!data) {
|
|
287
|
+
data = [];
|
|
288
|
+
if (getData) {
|
|
289
|
+
try {
|
|
290
|
+
setIsLoading(true);
|
|
291
|
+
handlePickSuggestions("*");
|
|
292
|
+
const fetchedSuggestions = await getData?.("*");
|
|
293
|
+
fetchedSuggestions?.forEach((element: any) => {
|
|
294
|
+
data?.push(element);
|
|
295
|
+
});
|
|
296
|
+
setIsLoading(false);
|
|
297
|
+
} catch (error) {
|
|
298
|
+
setIsLoading(false);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
useEffect(() => {
|
|
304
|
+
document.addEventListener("click", handleClickOutside);
|
|
305
|
+
return () => {
|
|
306
|
+
document.removeEventListener("click", handleClickOutside);
|
|
307
|
+
};
|
|
308
|
+
}, []);
|
|
309
|
+
|
|
310
|
+
useEffect(() => {
|
|
311
|
+
if (autoFocus) {
|
|
312
|
+
onLabelClick();
|
|
313
|
+
}
|
|
314
|
+
}, [autoFocus]);
|
|
315
|
+
const onInputFocus = () => {
|
|
316
|
+
if (!isDisabled) {
|
|
317
|
+
if (autoFilter && inputValue === "") {
|
|
318
|
+
handleValChange("*");
|
|
319
|
+
} else if (
|
|
320
|
+
(type === "custom_select" || type === "custom_search_select") &&
|
|
321
|
+
data &&
|
|
322
|
+
data?.length > 0
|
|
323
|
+
) {
|
|
324
|
+
handleDropData();
|
|
325
|
+
//if (insideOpen) setDropOpen(true) // commeted this code bcoz added onclick function inside
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
useEffect(() => {
|
|
330
|
+
setIsDisabled(disabled);
|
|
331
|
+
}, [disabled]);
|
|
332
|
+
const handleSuggestionClick = (suggestion: valueProps, index: number) => {
|
|
333
|
+
onChange({
|
|
334
|
+
...suggestion,
|
|
335
|
+
id: suggestion?.id,
|
|
336
|
+
name: suggestion.name,
|
|
337
|
+
...(suggestion?.label ? { label: suggestion?.label } : {}),
|
|
338
|
+
});
|
|
339
|
+
if (type !== "custom_select") setInputValue(suggestion.name);
|
|
340
|
+
setDropOpen(false);
|
|
341
|
+
setSelectedIndex(index);
|
|
342
|
+
};
|
|
343
|
+
const handleOpen = (e: any) => {
|
|
344
|
+
if (!suggestions || suggestions?.length === 0) {
|
|
345
|
+
handleDropData();
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
if (!isLoading) {
|
|
349
|
+
setDropOpen(!dropOpen);
|
|
350
|
+
if (type !== "custom_select") setInputValue("");
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
const handleScroll = () => {
|
|
355
|
+
setDropOpen(false);
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
useEffect(() => {
|
|
359
|
+
if (!insideOpen) {
|
|
360
|
+
setSelectedIndex(0);
|
|
361
|
+
window.addEventListener("scroll", handleScroll);
|
|
362
|
+
|
|
363
|
+
const mainElement = document.querySelector("main");
|
|
364
|
+
mainElement?.addEventListener("scroll", handleScroll);
|
|
365
|
+
|
|
366
|
+
const gridElements = document.querySelectorAll(
|
|
367
|
+
".k-grid-content, .overflow-auto, .overflow-y-auto, .overflow-x-auto"
|
|
368
|
+
);
|
|
369
|
+
gridElements.forEach((gridElement: any) => {
|
|
370
|
+
gridElement.addEventListener("scroll", handleScroll);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
window.addEventListener("resize", getDropPosition);
|
|
374
|
+
getDropPosition();
|
|
375
|
+
return () => {
|
|
376
|
+
window.removeEventListener("scroll", handleScroll);
|
|
377
|
+
const mainElement = document.querySelector("main");
|
|
378
|
+
mainElement?.removeEventListener("scroll", handleScroll);
|
|
379
|
+
window.addEventListener("resize", getDropPosition);
|
|
380
|
+
const gridElements = document.querySelectorAll(".k-grid-content");
|
|
381
|
+
gridElements.forEach((gridElement: any) => {
|
|
382
|
+
gridElement.removeEventListener("scroll", handleScroll);
|
|
383
|
+
});
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
}, [dropOpen]);
|
|
387
|
+
|
|
388
|
+
const handleClear = () => {
|
|
389
|
+
setDropOpen(false);
|
|
390
|
+
setInputValue("");
|
|
391
|
+
onChange({ id: undefined, name: "", from: 3 });
|
|
392
|
+
onLabelClick();
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
useEffect(() => {
|
|
396
|
+
setInputValue(value?.name ?? "");
|
|
397
|
+
}, [value?.name]);
|
|
398
|
+
|
|
399
|
+
useEffect(() => {
|
|
400
|
+
setSuggestions(data);
|
|
401
|
+
}, [data]);
|
|
402
|
+
|
|
403
|
+
const getErrors = (err: any) => {
|
|
404
|
+
let errMsg = "";
|
|
405
|
+
if (err.message) {
|
|
406
|
+
errMsg = err?.message;
|
|
407
|
+
} else if (err?.id?.message) {
|
|
408
|
+
errMsg = err?.id?.message;
|
|
409
|
+
} else if (err?.name?.message) {
|
|
410
|
+
errMsg = err?.name?.message;
|
|
411
|
+
}
|
|
412
|
+
return errMsg;
|
|
413
|
+
};
|
|
414
|
+
const generateClassName = (
|
|
415
|
+
type: "input" | "label" | "message" | "adorement"
|
|
416
|
+
): string => {
|
|
417
|
+
let className = propsClassName;
|
|
418
|
+
switch (type) {
|
|
419
|
+
case "input":
|
|
420
|
+
className += ` block text-common text-input-text font-normal px-3.5 w-full text-sm text-gray-900 bg-transparent border appearance-none peer h-10 rounded-[4px] disabled:text-input-disabled bg-white disabled:bg-disabled ${
|
|
421
|
+
label && isModern
|
|
422
|
+
? "placeholder-transparent"
|
|
423
|
+
: "focus:placeholder-grey-secondary placeholder-input-label"
|
|
424
|
+
} focus:placeholder-grey-secondary`;
|
|
425
|
+
|
|
426
|
+
if (errors && errors[name]) {
|
|
427
|
+
className +=
|
|
428
|
+
" border-[#FDA29B] focus:border-error-[#FDA29B] focus:ring-[#FDA29B] focus:ring-3 focus:outline-[#FDA29B] input-outline";
|
|
429
|
+
} else {
|
|
430
|
+
className +=
|
|
431
|
+
" text-grey-dark border-input-light focus:border-blue-navy focus:outline-none focus:ring-0";
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
className += getHeight();
|
|
435
|
+
|
|
436
|
+
break;
|
|
437
|
+
case "label":
|
|
438
|
+
className += ` flex modern-input-label-truncate peer-focus:modern-input-peer-focus-label-size
|
|
439
|
+
${
|
|
440
|
+
isDisabled
|
|
441
|
+
? "cursor-pointer"
|
|
442
|
+
: "cursor-text peer-focus:cursor-pointer"
|
|
443
|
+
} ${
|
|
444
|
+
isDisabled && !checkIsEmptyField()
|
|
445
|
+
? "disabled-input-label-bg"
|
|
446
|
+
: !isDisabled || !checkIsEmptyField()
|
|
447
|
+
? "active-input-label-bg"
|
|
448
|
+
: ""
|
|
449
|
+
} absolute duration-300 transform -translate-y-4 top-2 z-1 origin-[0] px-0 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:top-1/2 peer-focus:top-2 peer-focus:-translate-y-4 start-[14px] rtl:peer-focus:translate-x-1/4 rtl:peer-focus:left-auto ${
|
|
450
|
+
isDisabled
|
|
451
|
+
? "cursor-pointer"
|
|
452
|
+
: "cursor-text peer-focus:cursor-pointer"
|
|
453
|
+
}
|
|
454
|
+
${
|
|
455
|
+
checkIsEmptyField()
|
|
456
|
+
? "modern-input-label-size"
|
|
457
|
+
: "modern-input-peer-focus-label-size"
|
|
458
|
+
}`;
|
|
459
|
+
if (errors && errors[name]) {
|
|
460
|
+
className += " text-error-light ";
|
|
461
|
+
} else {
|
|
462
|
+
className += " text-grey-dark peer-focus:text-blue-navy";
|
|
463
|
+
}
|
|
464
|
+
break;
|
|
465
|
+
case "message":
|
|
466
|
+
className = " text-error-icon ";
|
|
467
|
+
break;
|
|
468
|
+
case "adorement":
|
|
469
|
+
className += " absolute right-0 adorement gap-1 flex items-center ";
|
|
470
|
+
break;
|
|
471
|
+
default:
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
return className;
|
|
475
|
+
};
|
|
476
|
+
const handleClose = () => {
|
|
477
|
+
setTimeout(() => {
|
|
478
|
+
setDropOpen(false);
|
|
479
|
+
}, 500);
|
|
480
|
+
};
|
|
481
|
+
const handleClearInputValue = () => {
|
|
482
|
+
// setInputValue(value?.name ?? '')
|
|
483
|
+
};
|
|
484
|
+
const getInnerWidth = () => {
|
|
485
|
+
const innerwidth = adorementRef.current
|
|
486
|
+
? adorementRef.current.offsetWidth
|
|
487
|
+
: 0;
|
|
488
|
+
return innerwidth;
|
|
489
|
+
};
|
|
490
|
+
// const getPosition = () => {
|
|
491
|
+
// return 'bottom'
|
|
492
|
+
// }
|
|
493
|
+
const filteredData =
|
|
494
|
+
inputValue !== "*" &&
|
|
495
|
+
inputValue !== "" &&
|
|
496
|
+
type !== "custom_select" &&
|
|
497
|
+
!noLocalFilter
|
|
498
|
+
? suggestions?.filter((item: valueProps) =>
|
|
499
|
+
checkIncludes(
|
|
500
|
+
item.name,
|
|
501
|
+
inputValue,
|
|
502
|
+
item.param1 ?? "",
|
|
503
|
+
item.param2 ?? "",
|
|
504
|
+
item.param3 ?? "",
|
|
505
|
+
item.param4 ?? ""
|
|
506
|
+
)
|
|
507
|
+
)
|
|
508
|
+
: suggestions;
|
|
509
|
+
|
|
510
|
+
const handleError = (data: any) => {
|
|
511
|
+
if (
|
|
512
|
+
getErrors(data[name]) === "required" ||
|
|
513
|
+
getErrors(data[name]) === "Required"
|
|
514
|
+
) {
|
|
515
|
+
return `${label ?? labelTitle} is ${getErrors(data[name])}`;
|
|
516
|
+
} else {
|
|
517
|
+
return getErrors(data[name]) ?? "";
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
const getDropPosition = (): any => {
|
|
522
|
+
if (!insideOpen && inputRef?.current) {
|
|
523
|
+
const dropdownHeight = 200; // Assume the height of the dropdown is 200px, adjust as needed
|
|
524
|
+
|
|
525
|
+
const inputRect = inputRef.current.getBoundingClientRect();
|
|
526
|
+
const spaceBelow =
|
|
527
|
+
window.innerHeight - (inputRect.bottom + window.scrollY);
|
|
528
|
+
const spaceAbove = inputRect.top + window.scrollY;
|
|
529
|
+
|
|
530
|
+
let top;
|
|
531
|
+
let bottom;
|
|
532
|
+
if (spaceBelow < dropdownHeight && spaceAbove > dropdownHeight) {
|
|
533
|
+
bottom = spaceBelow + 43;
|
|
534
|
+
} else {
|
|
535
|
+
top = inputRect.top + window.scrollY + inputRect.height + 2;
|
|
536
|
+
}
|
|
537
|
+
setDropPosition({
|
|
538
|
+
left: inputRect.left + window.scrollX,
|
|
539
|
+
top,
|
|
540
|
+
bottom,
|
|
541
|
+
width: inputRect.width + dropdownMinWidth,
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
return undefined;
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
const [showNoResults, setShowNoResults] = useState(false);
|
|
549
|
+
|
|
550
|
+
useEffect(() => {
|
|
551
|
+
if (
|
|
552
|
+
inputValue &&
|
|
553
|
+
filteredData?.length === 0 &&
|
|
554
|
+
!isLoading &&
|
|
555
|
+
timerRef.current === 1
|
|
556
|
+
) {
|
|
557
|
+
const timer = setTimeout(() => {
|
|
558
|
+
setShowNoResults(true);
|
|
559
|
+
}, 500); // Delay in milliseconds
|
|
560
|
+
return () => clearTimeout(timer);
|
|
561
|
+
} else {
|
|
562
|
+
setShowNoResults(false);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Handle keyboard navigation
|
|
566
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
567
|
+
if (!dropOpen) return;
|
|
568
|
+
|
|
569
|
+
const nextIndex =
|
|
570
|
+
e.key === "ArrowDown"
|
|
571
|
+
? (selectedIndex + 1) % filteredData.length
|
|
572
|
+
: e.key === "ArrowUp"
|
|
573
|
+
? (selectedIndex - 1 + filteredData.length) % filteredData.length
|
|
574
|
+
: selectedIndex;
|
|
575
|
+
|
|
576
|
+
switch (e.key) {
|
|
577
|
+
case "ArrowDown":
|
|
578
|
+
case "ArrowUp":
|
|
579
|
+
e.preventDefault();
|
|
580
|
+
|
|
581
|
+
// Update index first
|
|
582
|
+
setSelectedIndex(nextIndex);
|
|
583
|
+
|
|
584
|
+
// Wait for DOM update before scrolling
|
|
585
|
+
requestAnimationFrame(() => {
|
|
586
|
+
const el = itemRefs.current[nextIndex];
|
|
587
|
+
const container = scrollContainerRef.current;
|
|
588
|
+
|
|
589
|
+
if (el && container) {
|
|
590
|
+
const elTop = el.offsetTop;
|
|
591
|
+
const elBottom = elTop + el.offsetHeight;
|
|
592
|
+
|
|
593
|
+
const containerTop = container.scrollTop;
|
|
594
|
+
const containerBottom = containerTop + container.clientHeight;
|
|
595
|
+
|
|
596
|
+
if (elBottom > containerBottom) {
|
|
597
|
+
container.scrollTop = elBottom - container.clientHeight;
|
|
598
|
+
} else if (elTop < containerTop) {
|
|
599
|
+
container.scrollTop = elTop;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
break;
|
|
604
|
+
|
|
605
|
+
case "Enter":
|
|
606
|
+
e.preventDefault();
|
|
607
|
+
handleSuggestionClick(filteredData[selectedIndex], selectedIndex);
|
|
608
|
+
break;
|
|
609
|
+
|
|
610
|
+
case "Escape":
|
|
611
|
+
e.preventDefault();
|
|
612
|
+
setDropOpen(false);
|
|
613
|
+
break;
|
|
614
|
+
|
|
615
|
+
default:
|
|
616
|
+
break;
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
if (filteredData?.length > 0 && !isLoading) {
|
|
621
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
622
|
+
return () => {
|
|
623
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
}, [
|
|
627
|
+
inputValue,
|
|
628
|
+
filteredData,
|
|
629
|
+
isLoading,
|
|
630
|
+
timerRef.current,
|
|
631
|
+
selectedIndex,
|
|
632
|
+
dropOpen,
|
|
633
|
+
]);
|
|
634
|
+
|
|
635
|
+
const setDropDown = () => {
|
|
636
|
+
return (
|
|
637
|
+
(filteredData?.length > 0 || showNoResults) && (
|
|
638
|
+
<div
|
|
639
|
+
className="autocomplete-suggections absolute bg-white shadow-gray-300 shadow-md border border-grey-light z-50 mt-9"
|
|
640
|
+
style={dropPosition}
|
|
641
|
+
>
|
|
642
|
+
<ul
|
|
643
|
+
className={`h-auto max-h-40 overflow-auto w-full ${tableView ? '' : 'py-1.5'}`}
|
|
644
|
+
ref={scrollContainerRef}
|
|
645
|
+
>
|
|
646
|
+
{filteredData?.length > 0 ? (
|
|
647
|
+
<>
|
|
648
|
+
{filteredData.map((suggestion: any, index: number) => (
|
|
649
|
+
<li
|
|
650
|
+
className={`${
|
|
651
|
+
value?.id === suggestion?.id
|
|
652
|
+
? "bg-blue-navy text-white"
|
|
653
|
+
: `${
|
|
654
|
+
index === selectedIndex ? "is-selected" : ""
|
|
655
|
+
} hover:bg-table-hover`
|
|
656
|
+
} cursor-pointer text-xxs qbs-autocomplete-suggections-items ${
|
|
657
|
+
tableView
|
|
658
|
+
? "border-b border-grey-light last:border-b-0"
|
|
659
|
+
: "p-1 ps-3.5 pl-[10px]"
|
|
660
|
+
}`}
|
|
661
|
+
key={suggestion?.id}
|
|
662
|
+
data-testid={suggestion.name}
|
|
663
|
+
onClick={() => handleSuggestionClick(suggestion, index)}
|
|
664
|
+
tabIndex={index}
|
|
665
|
+
ref={(el) => (itemRefs.current[index] = el)}
|
|
666
|
+
>
|
|
667
|
+
<ul className="flex items-stretch w-full list-none p-0 m-0">
|
|
668
|
+
{(() => {
|
|
669
|
+
// Sort columns by order if specified, otherwise maintain array order
|
|
670
|
+
const sortedColumns = additionalColumns
|
|
671
|
+
? [...additionalColumns].sort((a, b) => {
|
|
672
|
+
const orderA = a.order !== undefined ? a.order : Infinity;
|
|
673
|
+
const orderB = b.order !== undefined ? b.order : Infinity;
|
|
674
|
+
return orderA - orderB;
|
|
675
|
+
})
|
|
676
|
+
: [];
|
|
677
|
+
|
|
678
|
+
// Separate columns before first column (order < 0) and after (order >= 0 or undefined)
|
|
679
|
+
const columnsBefore = sortedColumns.filter(col => col.order !== undefined && col.order < 0);
|
|
680
|
+
const columnsAfter = sortedColumns.filter(col => col.order === undefined || col.order >= 0);
|
|
681
|
+
|
|
682
|
+
// Determine if first column needs a separator
|
|
683
|
+
const hasColumnsAfter = columnsAfter.length > 0;
|
|
684
|
+
const hasColumnsBefore = columnsBefore.length > 0;
|
|
685
|
+
|
|
686
|
+
return (
|
|
687
|
+
<>
|
|
688
|
+
{/* Columns before first column */}
|
|
689
|
+
{columnsBefore.map((column, colIndex) => {
|
|
690
|
+
const hasValue = suggestion?.[column.key];
|
|
691
|
+
const isLastBefore = colIndex === columnsBefore.length - 1 && !hasColumnsAfter;
|
|
692
|
+
return hasValue ? (
|
|
693
|
+
<li
|
|
694
|
+
key={column.key}
|
|
695
|
+
className={`${column.width ? 'flex-shrink-0' : 'flex-1'} min-w-0 px-3 ${tableView ? 'py-2' : ''} ${!isLastBefore ? 'border-r border-grey-light' : ''} break-words flex flex-col`}
|
|
696
|
+
style={column.width ? { width: `${column.width}px` } : undefined}
|
|
697
|
+
>
|
|
698
|
+
<span className="block whitespace-normal">
|
|
699
|
+
{suggestion?.[column.key]}
|
|
700
|
+
</span>
|
|
701
|
+
</li>
|
|
702
|
+
) : null;
|
|
703
|
+
})}
|
|
704
|
+
|
|
705
|
+
{/* First column (label/name) */}
|
|
706
|
+
<li className={`flex-1 min-w-0 px-3 ${tableView ? 'py-2' : ''} ${hasColumnsAfter ? 'border-r border-grey-light' : ''} break-words flex flex-col`}>
|
|
707
|
+
<span className="block whitespace-normal">
|
|
708
|
+
{suggestion?.label ? suggestion?.label : suggestion.name}
|
|
709
|
+
</span>
|
|
710
|
+
</li>
|
|
711
|
+
|
|
712
|
+
{/* Columns after first column */}
|
|
713
|
+
{columnsAfter.map((column, colIndex) => {
|
|
714
|
+
const hasValue = suggestion?.[column.key];
|
|
715
|
+
const isLastColumn = colIndex === columnsAfter.length - 1;
|
|
716
|
+
return hasValue ? (
|
|
717
|
+
<li
|
|
718
|
+
key={column.key}
|
|
719
|
+
className={`${column.width ? 'flex-shrink-0' : 'flex-1'} min-w-0 px-3 ${tableView ? 'py-2' : ''} ${!isLastColumn ? 'border-r border-grey-light' : ''} break-words flex flex-col`}
|
|
720
|
+
style={column.width ? { width: `${column.width}px` } : undefined}
|
|
721
|
+
>
|
|
722
|
+
<span className="block whitespace-normal">
|
|
723
|
+
{suggestion?.[column.key]}
|
|
724
|
+
</span>
|
|
725
|
+
</li>
|
|
726
|
+
) : null;
|
|
727
|
+
})}
|
|
728
|
+
</>
|
|
729
|
+
);
|
|
730
|
+
})()}
|
|
731
|
+
</ul>
|
|
732
|
+
</li>
|
|
733
|
+
))}
|
|
734
|
+
</>
|
|
735
|
+
) : (
|
|
736
|
+
showNoResults &&
|
|
737
|
+
!isLoading &&
|
|
738
|
+
timerRef.current === 1 && (
|
|
739
|
+
<li
|
|
740
|
+
className={`$ cursor-pointer p-1 rounded-sm text-xxs`}
|
|
741
|
+
onClick={handleClose}
|
|
742
|
+
>
|
|
743
|
+
No Results Found
|
|
744
|
+
</li>
|
|
745
|
+
)
|
|
746
|
+
)}
|
|
747
|
+
</ul>
|
|
748
|
+
</div>
|
|
749
|
+
)
|
|
750
|
+
);
|
|
751
|
+
};
|
|
752
|
+
const getBorderRight = () => {
|
|
753
|
+
if (fromPrefix) {
|
|
754
|
+
if (
|
|
755
|
+
(hasParentError && Boolean(errors && errors[name])) ||
|
|
756
|
+
hasParentError
|
|
757
|
+
) {
|
|
758
|
+
return 0;
|
|
759
|
+
} else if (errors && errors[name]) {
|
|
760
|
+
return undefined;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return 0;
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
useEffect(() => {
|
|
767
|
+
applyPositionClass(dropdownref, isHovered);
|
|
768
|
+
setDropOpen(false);
|
|
769
|
+
}, [isHovered]);
|
|
770
|
+
|
|
771
|
+
useEffect(() => {
|
|
772
|
+
const shouldShowClose =
|
|
773
|
+
value?.name &&
|
|
774
|
+
(value?.id || String(value?.id) === "0") &&
|
|
775
|
+
!disabled &&
|
|
776
|
+
!readOnly &&
|
|
777
|
+
isClose;
|
|
778
|
+
if (showClose !== shouldShowClose) {
|
|
779
|
+
setShowClose(shouldShowClose);
|
|
780
|
+
}
|
|
781
|
+
}, [value, disabled, readOnly]);
|
|
782
|
+
useEffect(() => {
|
|
783
|
+
// Check if input content overflows
|
|
784
|
+
const checkOverflow = () => {
|
|
785
|
+
if (inputRef.current) {
|
|
786
|
+
const { scrollWidth, clientWidth, value } = inputRef.current;
|
|
787
|
+
// Check for overflow only when there's content
|
|
788
|
+
if (value.trim() !== "") {
|
|
789
|
+
setShowTooltip(scrollWidth > clientWidth + 2);
|
|
790
|
+
} else {
|
|
791
|
+
setShowTooltip(false); // No tooltip for empty input
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
};
|
|
795
|
+
|
|
796
|
+
if (inputRef.current) {
|
|
797
|
+
inputRef.current.addEventListener("input", checkOverflow);
|
|
798
|
+
checkOverflow(); // Initial check
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
return () => {
|
|
802
|
+
if (inputRef.current) {
|
|
803
|
+
inputRef.current.removeEventListener("input", checkOverflow);
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
}, [inputValue]);
|
|
807
|
+
|
|
808
|
+
const handleMouseEnter = () => {
|
|
809
|
+
setTooltipIsHovered(true);
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
const handleMouseLeave = () => {
|
|
813
|
+
setTooltipIsHovered(false);
|
|
814
|
+
};
|
|
815
|
+
|
|
816
|
+
return (
|
|
817
|
+
<div
|
|
818
|
+
id={
|
|
819
|
+
id ? `autocomplete-container-${id}` : `autocomplete-container-${name}`
|
|
820
|
+
}
|
|
821
|
+
className={` flex-grow ${fullWidth ? "w-full" : "w-auto"}`}
|
|
822
|
+
ref={dropdownref}
|
|
823
|
+
>
|
|
824
|
+
{label && !isModern && (
|
|
825
|
+
<div className="mb-3">
|
|
826
|
+
<label className={`text-xs font-medium`}>
|
|
827
|
+
{label}
|
|
828
|
+
{required ? <span className="text-error"> *</span> : <></>}
|
|
829
|
+
</label>
|
|
830
|
+
</div>
|
|
831
|
+
)}
|
|
832
|
+
<div className="tooltip-container">
|
|
833
|
+
{isHovered && errors && errors[name] && (
|
|
834
|
+
<span className="tooltip">{handleError(errors)} </span>
|
|
835
|
+
)}
|
|
836
|
+
{tooltipIsHovered && showToolTip && !dropOpen && inputValue && (
|
|
837
|
+
<div className={`tooltip-info`}>{inputValue}</div>
|
|
838
|
+
)}
|
|
839
|
+
|
|
840
|
+
<div
|
|
841
|
+
className={`flex relative ${fullWidth ? "w-full" : "w-auto"}`}
|
|
842
|
+
style={{ width: width }}
|
|
843
|
+
>
|
|
844
|
+
<div
|
|
845
|
+
className="relative w-full"
|
|
846
|
+
onMouseEnter={() => handleMouseEnter()}
|
|
847
|
+
onMouseLeave={() => handleMouseLeave()}
|
|
848
|
+
>
|
|
849
|
+
<input
|
|
850
|
+
type="text"
|
|
851
|
+
readOnly={readOnly ?? type === "custom_select"}
|
|
852
|
+
value={inputValue ? inputValue : ""}
|
|
853
|
+
onBlur={handleClearInputValue}
|
|
854
|
+
autoComplete="off"
|
|
855
|
+
disabled={disabled}
|
|
856
|
+
ref={inputRef}
|
|
857
|
+
id={
|
|
858
|
+
id ? `custom-autocomplete-${id}` : `custom-autocomplete-${name}`
|
|
859
|
+
}
|
|
860
|
+
data-testid="custom-autocomplete"
|
|
861
|
+
// onBlur={() => {
|
|
862
|
+
// setInputValue("")
|
|
863
|
+
// }}
|
|
864
|
+
aria-describedby={id}
|
|
865
|
+
style={{
|
|
866
|
+
paddingRight: getInnerWidth(),
|
|
867
|
+
...(fromPrefix
|
|
868
|
+
? {
|
|
869
|
+
borderRight: getBorderRight(),
|
|
870
|
+
borderTopRightRadius: 0,
|
|
871
|
+
borderBottomRightRadius: 0,
|
|
872
|
+
}
|
|
873
|
+
: {}),
|
|
874
|
+
}}
|
|
875
|
+
className={` ${generateClassName("input")}`}
|
|
876
|
+
onChange={handleChange}
|
|
877
|
+
placeholder={
|
|
878
|
+
type === "auto_complete"
|
|
879
|
+
? placeholder && isCustomPlaceholder
|
|
880
|
+
? placeholder
|
|
881
|
+
: "Type to search"
|
|
882
|
+
: placeholder ?? "--Select--"
|
|
883
|
+
}
|
|
884
|
+
onFocus={onInputFocus}
|
|
885
|
+
onClick={(e) => {
|
|
886
|
+
if (type === "custom_select") {
|
|
887
|
+
setDropOpen(!dropOpen);
|
|
888
|
+
handleOpen(e);
|
|
889
|
+
} else {
|
|
890
|
+
setDropOpen(!dropOpen);
|
|
891
|
+
}
|
|
892
|
+
}}
|
|
893
|
+
/>
|
|
894
|
+
{isModern && (
|
|
895
|
+
<label
|
|
896
|
+
htmlFor={id}
|
|
897
|
+
onClick={() => onLabelClick()}
|
|
898
|
+
className={generateClassName("label")}
|
|
899
|
+
>
|
|
900
|
+
{label ? <span className="truncate">{label}</span> : ""}
|
|
901
|
+
{required ? <span className="text-error"> *</span> : <></>}
|
|
902
|
+
</label>
|
|
903
|
+
)}
|
|
904
|
+
</div>
|
|
905
|
+
<div className="flex items-center justify-center ">
|
|
906
|
+
<div
|
|
907
|
+
ref={adorementRef}
|
|
908
|
+
className={`${generateClassName(
|
|
909
|
+
"adorement"
|
|
910
|
+
)} qbs-autocomplete-adorement custom-dorpdown-adorement mr-[1px] ${
|
|
911
|
+
isLoading ? "bg-white" : ""
|
|
912
|
+
}`}
|
|
913
|
+
>
|
|
914
|
+
{showClose && (
|
|
915
|
+
<button
|
|
916
|
+
onClick={() => handleClear()}
|
|
917
|
+
className=" text-table-bodyColor text-[#667085]"
|
|
918
|
+
aria-label="close"
|
|
919
|
+
type="button"
|
|
920
|
+
id="autocomplete-close-icon"
|
|
921
|
+
>
|
|
922
|
+
<CustomIcons name="close" type="large-m" />
|
|
923
|
+
</button>
|
|
924
|
+
)}
|
|
925
|
+
{isLoading && <Spinner />}
|
|
926
|
+
{type !== "auto_complete" && !disabled && !readOnly && (
|
|
927
|
+
<button
|
|
928
|
+
disabled={disabled ?? readOnly}
|
|
929
|
+
onClick={(e) => handleOpen(e)}
|
|
930
|
+
onBlur={handleClose}
|
|
931
|
+
className=" text-[#667085] focus-visible:outline-slate-100"
|
|
932
|
+
data-testid="drop-arrow"
|
|
933
|
+
type="button"
|
|
934
|
+
id="autocomplete-drop-icon"
|
|
935
|
+
ref={dropBtnRef}
|
|
936
|
+
>
|
|
937
|
+
{!dropOpen ? (
|
|
938
|
+
<DropArrow uniqueDropArrowId="drop-arrow-icon" />
|
|
939
|
+
) : (
|
|
940
|
+
<DropArrow
|
|
941
|
+
className="rotate-180"
|
|
942
|
+
uniqueDropArrowId="drop-arrow-icon"
|
|
943
|
+
/>
|
|
944
|
+
)}
|
|
945
|
+
</button>
|
|
946
|
+
)}
|
|
947
|
+
{errors && errors[name] && (
|
|
948
|
+
<div
|
|
949
|
+
className={` text-error-label relative cursor-pointer ${generateClassName(
|
|
950
|
+
"message"
|
|
951
|
+
)}`}
|
|
952
|
+
onMouseEnter={() => setIsHovered(true)}
|
|
953
|
+
onMouseLeave={() => setIsHovered(false)}
|
|
954
|
+
>
|
|
955
|
+
<CustomIcons name="alert" type="medium" />
|
|
956
|
+
</div>
|
|
957
|
+
)}
|
|
958
|
+
</div>
|
|
959
|
+
</div>
|
|
960
|
+
{dropOpen && (!isLoading || filteredData?.length > 0) && (
|
|
961
|
+
<>{insideOpen ? setDropDown() : <Portal>{setDropDown()}</Portal>}</>
|
|
962
|
+
)}
|
|
963
|
+
</div>
|
|
964
|
+
</div>
|
|
965
|
+
</div>
|
|
966
|
+
);
|
|
967
|
+
};
|
|
968
|
+
|
|
969
|
+
export default ReactAutoCompleteTableView;
|