react-core-ts 1.0.24 → 1.0.26
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/AutoCompleteWithTreeStructure.d.ts +2 -2
- package/dist/AutoCompleteWithTreeStructure.js +56 -94
- package/dist/AutoCompleteWithTreeStructure.js.map +1 -1
- package/dist/commontypes.d.ts +3 -1
- package/dist/components/TreeNode.d.ts +1 -1
- package/dist/components/TreeNode.js +10 -10
- package/dist/components/TreeNode.js.map +1 -1
- package/dist/utilities/convertFlatArraytoTreeStructure.d.ts +1 -0
- package/dist/utilities/convertFlatArraytoTreeStructure.js +29 -0
- package/dist/utilities/convertFlatArraytoTreeStructure.js.map +1 -0
- package/docs/App.tsx +1 -0
- package/docs/store.ts +59 -68
- package/package.json +2 -2
- package/tailwind.config.js +1 -0
- package/src/AutoComplete.tsx +0 -489
- package/src/AutoCompleteWithTreeStructure.tsx +0 -638
- package/src/ExpandableAutoComplete.tsx +0 -545
- package/src/commontypes.ts +0 -43
- package/src/components/DropdownList.tsx +0 -70
- package/src/components/Icons.tsx +0 -38
- package/src/components/InputActions.tsx +0 -63
- package/src/components/TreeNode.tsx +0 -79
- package/src/index.html +0 -1
- package/src/index.tsx +0 -12
- package/src/styles/components/autocomplete.css +0 -585
- package/src/styles/global.css +0 -5
- package/src/utilities/autosuggestions.ts +0 -83
- package/src/utilities/debounce.ts +0 -15
- package/src/utilities/deepEqual.ts +0 -54
- package/src/utilities/expandableTootltip.tsx +0 -147
- package/src/utilities/filterSuggestions.ts +0 -48
- package/src/utilities/icons.tsx +0 -86
- package/src/utilities/tooltip.tsx +0 -52
|
@@ -1,638 +0,0 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
forwardRef,
|
|
3
|
-
useCallback,
|
|
4
|
-
useEffect,
|
|
5
|
-
useImperativeHandle,
|
|
6
|
-
useRef,
|
|
7
|
-
useState,
|
|
8
|
-
} from "react";
|
|
9
|
-
import ReactDOM from "react-dom";
|
|
10
|
-
|
|
11
|
-
import { AutoSuggestionInputProps } from "./commontypes";
|
|
12
|
-
import InputActions from "./components/InputActions";
|
|
13
|
-
import TreeNode from "./components/TreeNode";
|
|
14
|
-
import { useSuggestions } from "./utilities/autosuggestions";
|
|
15
|
-
import { debounce } from "./utilities/debounce";
|
|
16
|
-
import { deepEqual } from "./utilities/deepEqual";
|
|
17
|
-
import { default as Tooltip } from "./utilities/expandableTootltip";
|
|
18
|
-
import { Search, Spinner } from "./utilities/icons";
|
|
19
|
-
|
|
20
|
-
type ValueProps = {
|
|
21
|
-
[key: string]: string;
|
|
22
|
-
};
|
|
23
|
-
const AutoCompleteWithTreeStructure = forwardRef<
|
|
24
|
-
HTMLInputElement,
|
|
25
|
-
AutoSuggestionInputProps
|
|
26
|
-
>(
|
|
27
|
-
(
|
|
28
|
-
{
|
|
29
|
-
label,
|
|
30
|
-
onChange,
|
|
31
|
-
getData = async () => [],
|
|
32
|
-
data = [],
|
|
33
|
-
errors,
|
|
34
|
-
required = false,
|
|
35
|
-
name,
|
|
36
|
-
fullWidth = false,
|
|
37
|
-
placeholder,
|
|
38
|
-
id,
|
|
39
|
-
type = "custom_select",
|
|
40
|
-
selectedItems: propsSeelctedItems = [],
|
|
41
|
-
readOnly = false,
|
|
42
|
-
disabled = false,
|
|
43
|
-
value,
|
|
44
|
-
isMultiple = false,
|
|
45
|
-
desc = "name",
|
|
46
|
-
descId = "id",
|
|
47
|
-
singleSelect,
|
|
48
|
-
className,
|
|
49
|
-
async = false,
|
|
50
|
-
paginationEnabled,
|
|
51
|
-
initialLoad,
|
|
52
|
-
actionLabel,
|
|
53
|
-
handleAction,
|
|
54
|
-
nextBlock,
|
|
55
|
-
notDataMessage,
|
|
56
|
-
onFocus,
|
|
57
|
-
expandable = false,
|
|
58
|
-
textCount = 10,
|
|
59
|
-
itemCount = 1,
|
|
60
|
-
scrollRef,
|
|
61
|
-
isTreeDropdown = true,
|
|
62
|
-
},
|
|
63
|
-
ref
|
|
64
|
-
) => {
|
|
65
|
-
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
66
|
-
// State Hooks Section
|
|
67
|
-
const [isInitialRender, setIsInitialRender] = useState(true);
|
|
68
|
-
|
|
69
|
-
const [inputValue, setInputValue] = useState<string>(value);
|
|
70
|
-
const [searchValue, setSearchValue] = useState<string>("");
|
|
71
|
-
const [nextPage, setNextPage] = useState<number | undefined>(1);
|
|
72
|
-
const [dropOpen, setDropOpen] = useState<boolean>(false);
|
|
73
|
-
const [selectedItems, setSelectedItems] = useState<ValueProps[]>([]);
|
|
74
|
-
// API call for suggestions through a custom hook
|
|
75
|
-
const [dropDownData, setDropDownData] = useState<ValueProps[]>(data);
|
|
76
|
-
const inputRef = useRef(null);
|
|
77
|
-
const dropRef = useRef(null);
|
|
78
|
-
|
|
79
|
-
useEffect(() => {
|
|
80
|
-
if (data) {
|
|
81
|
-
setDropDownData(data);
|
|
82
|
-
}
|
|
83
|
-
}, [data]);
|
|
84
|
-
useImperativeHandle(ref, () => inputRef.current);
|
|
85
|
-
const [dropdownStyle, setDropdownStyle] = useState({
|
|
86
|
-
top: 0,
|
|
87
|
-
left: 0,
|
|
88
|
-
width: 200,
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
const adjustDropdownPosition = () => {
|
|
92
|
-
if (dropdownRef.current) {
|
|
93
|
-
const inputRect = dropdownRef.current.getBoundingClientRect();
|
|
94
|
-
const dropdownPosition = {
|
|
95
|
-
left: inputRect.left + window.scrollX,
|
|
96
|
-
width: inputRect.width,
|
|
97
|
-
top: 0,
|
|
98
|
-
};
|
|
99
|
-
// Check if there's enough space below the input for the dropdown
|
|
100
|
-
const spaceBelow = window.innerHeight - inputRect.bottom;
|
|
101
|
-
const spaceAbove = inputRect.top + window.scrollY;
|
|
102
|
-
|
|
103
|
-
const dropdownHeight = 275; // Assume a fixed height or calculate based on content
|
|
104
|
-
if (spaceBelow >= dropdownHeight) {
|
|
105
|
-
dropdownPosition.top =
|
|
106
|
-
inputRect.top + window.scrollY + inputRect.height;
|
|
107
|
-
} else {
|
|
108
|
-
dropdownPosition.top =
|
|
109
|
-
inputRect.top + window.scrollY - dropdownHeight + 73;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
setDropdownStyle({
|
|
113
|
-
...dropdownPosition,
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
useEffect(() => {
|
|
119
|
-
window.addEventListener("resize", adjustDropdownPosition);
|
|
120
|
-
adjustDropdownPosition();
|
|
121
|
-
|
|
122
|
-
return () => {
|
|
123
|
-
window.removeEventListener("resize", adjustDropdownPosition);
|
|
124
|
-
};
|
|
125
|
-
}, [dropOpen, selectedItems]);
|
|
126
|
-
|
|
127
|
-
const { suggestions, isLoading, handlePickSuggestions } = useSuggestions(
|
|
128
|
-
getData,
|
|
129
|
-
data,
|
|
130
|
-
dropOpen,
|
|
131
|
-
async,
|
|
132
|
-
paginationEnabled,
|
|
133
|
-
initialLoad,
|
|
134
|
-
inputValue,
|
|
135
|
-
isMultiple,
|
|
136
|
-
setNextPage,
|
|
137
|
-
selectedItems
|
|
138
|
-
// nextBlock
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
// Handling the selection of a suggestion
|
|
142
|
-
const handleSuggestionClick = useCallback((suggestion: ValueProps) => {
|
|
143
|
-
if (isMultiple) {
|
|
144
|
-
setSelectedItems((prev) => [...prev, suggestion]);
|
|
145
|
-
} else {
|
|
146
|
-
setInputValue(suggestion[desc]);
|
|
147
|
-
}
|
|
148
|
-
setSearchValue("");
|
|
149
|
-
setInputValue(suggestion[desc]);
|
|
150
|
-
onChange(suggestion);
|
|
151
|
-
setDropOpen(false);
|
|
152
|
-
}, []);
|
|
153
|
-
|
|
154
|
-
// Adding debounce to avoid making API calls on every keystroke
|
|
155
|
-
const handleChangeWithDebounce = debounce((value) => {
|
|
156
|
-
if ((type === "auto_complete" || type === "auto_suggestion") && async) {
|
|
157
|
-
handlePickSuggestions(value, 1);
|
|
158
|
-
}
|
|
159
|
-
}, 1000);
|
|
160
|
-
|
|
161
|
-
const handleMultiSelect = (e: any, suggestion: ValueProps) => {
|
|
162
|
-
const { checked } = e.target;
|
|
163
|
-
if (isMultiple) {
|
|
164
|
-
if (checked) {
|
|
165
|
-
setSelectedItems((prev) => [...prev, suggestion]);
|
|
166
|
-
} else {
|
|
167
|
-
setSelectedItems((prev) => {
|
|
168
|
-
return prev.filter(
|
|
169
|
-
(item, i) => item[descId] !== suggestion[descId]
|
|
170
|
-
);
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
} else {
|
|
174
|
-
if (checked) {
|
|
175
|
-
setInputValue(suggestion[desc]);
|
|
176
|
-
onChange(suggestion);
|
|
177
|
-
} else {
|
|
178
|
-
setInputValue("");
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
useEffect(() => {
|
|
183
|
-
if (!deepEqual(selectedItems, propsSeelctedItems))
|
|
184
|
-
setSelectedItems(propsSeelctedItems);
|
|
185
|
-
}, [propsSeelctedItems]);
|
|
186
|
-
// Effect to set the input value whenever `value` prop changes
|
|
187
|
-
useEffect(() => {
|
|
188
|
-
setInputValue(value ?? "");
|
|
189
|
-
}, [value]);
|
|
190
|
-
|
|
191
|
-
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
192
|
-
const { value } = e.target;
|
|
193
|
-
setDropOpen(true);
|
|
194
|
-
setSearchValue(value);
|
|
195
|
-
handleChangeWithDebounce(value);
|
|
196
|
-
if (!value) {
|
|
197
|
-
setInputValue("");
|
|
198
|
-
// onChange({ [descId]: '', [desc]: '' });
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
const handleSuggestionChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
202
|
-
const { value } = e.target;
|
|
203
|
-
setDropOpen(true);
|
|
204
|
-
setSearchValue(value);
|
|
205
|
-
handleChangeWithDebounce(value);
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
const handleBlur = () => {
|
|
209
|
-
setTimeout(() => {
|
|
210
|
-
setDropOpen(false);
|
|
211
|
-
}, 200);
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
const handleClear = () => {
|
|
215
|
-
if (searchValue) {
|
|
216
|
-
setSearchValue("");
|
|
217
|
-
setDropOpen(false);
|
|
218
|
-
} else {
|
|
219
|
-
setInputValue("");
|
|
220
|
-
onChange({ [descId]: "", [desc]: "" });
|
|
221
|
-
setDropOpen(false);
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
const generateClassName = useCallback(() => {
|
|
226
|
-
return `qbs-textfield-default ${className} ${
|
|
227
|
-
errors && errors?.message ? "textfield-error" : "textfield"
|
|
228
|
-
} ${expandable ? "expandable" : ""}`;
|
|
229
|
-
}, [errors, name]);
|
|
230
|
-
const handleRemoveSelectedItem = (index: number) => {
|
|
231
|
-
setSelectedItems((prev) => {
|
|
232
|
-
return prev.filter((_, i) => i !== index);
|
|
233
|
-
});
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
useEffect(() => {
|
|
237
|
-
if (isInitialRender) {
|
|
238
|
-
setIsInitialRender(false);
|
|
239
|
-
} else {
|
|
240
|
-
onChange(selectedItems);
|
|
241
|
-
}
|
|
242
|
-
}, [selectedItems]);
|
|
243
|
-
|
|
244
|
-
useEffect(() => {
|
|
245
|
-
const handleClickOutside = (event: any) => {
|
|
246
|
-
if (
|
|
247
|
-
dropRef.current &&
|
|
248
|
-
event.target instanceof Node &&
|
|
249
|
-
!dropRef.current.contains(event.target) &&
|
|
250
|
-
event.target.nodeName !== "svg"
|
|
251
|
-
) {
|
|
252
|
-
setDropOpen(false);
|
|
253
|
-
setSearchValue("");
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
document.addEventListener("mousedown", handleClickOutside as any);
|
|
257
|
-
window.addEventListener("scroll", handleClickOutside as any);
|
|
258
|
-
|
|
259
|
-
const scrollableDivs = document.querySelectorAll(
|
|
260
|
-
'div[style*="overflow"]'
|
|
261
|
-
);
|
|
262
|
-
scrollableDivs.forEach((div) =>
|
|
263
|
-
div.addEventListener("scroll", handleClickOutside as any)
|
|
264
|
-
);
|
|
265
|
-
if (scrollRef && scrollRef.current && scrollRef.current !== null) {
|
|
266
|
-
scrollRef.current.addEventListener("scroll", handleClickOutside as any);
|
|
267
|
-
}
|
|
268
|
-
return () => {
|
|
269
|
-
document.removeEventListener("mousedown", handleClickOutside as any);
|
|
270
|
-
window.removeEventListener("scroll", handleClickOutside as any);
|
|
271
|
-
scrollableDivs.forEach((div) =>
|
|
272
|
-
div.removeEventListener("scroll", handleClickOutside as any)
|
|
273
|
-
);
|
|
274
|
-
if (scrollRef && scrollRef.current && scrollRef.current !== null) {
|
|
275
|
-
scrollRef.current.removeEventListener(
|
|
276
|
-
"scroll",
|
|
277
|
-
handleClickOutside as any
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
};
|
|
281
|
-
}, []);
|
|
282
|
-
|
|
283
|
-
// Filtering suggestions based on type and search value
|
|
284
|
-
const selected: any = isMultiple ? selectedItems : inputValue;
|
|
285
|
-
|
|
286
|
-
const handleOnClick = () => {
|
|
287
|
-
!disabled && !readOnly ? setDropOpen(true) : "";
|
|
288
|
-
};
|
|
289
|
-
const onInputFocus = () => {
|
|
290
|
-
if (inputRef.current) inputRef.current.focus();
|
|
291
|
-
handleOnClick();
|
|
292
|
-
};
|
|
293
|
-
|
|
294
|
-
const tooltipContent =
|
|
295
|
-
selectedItems?.length > itemCount
|
|
296
|
-
? selectedItems
|
|
297
|
-
?.slice(itemCount)
|
|
298
|
-
.map((item) => item[desc])
|
|
299
|
-
.join(", ")
|
|
300
|
-
: "";
|
|
301
|
-
|
|
302
|
-
const handleDropOpen = (e: any) => {
|
|
303
|
-
if (!dropOpen) setDropOpen(true);
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
const handleDropClose = (e: any) => {
|
|
307
|
-
if (dropOpen) setDropOpen(false);
|
|
308
|
-
};
|
|
309
|
-
const toggleExpand = (id: string) => {
|
|
310
|
-
const newTreeData = updateTreeNode(dropDownData, id, (node) => {
|
|
311
|
-
node.expanded = !node.expanded;
|
|
312
|
-
});
|
|
313
|
-
setDropDownData(newTreeData);
|
|
314
|
-
};
|
|
315
|
-
|
|
316
|
-
// Function to handle checkbox change (single or multi-select)
|
|
317
|
-
const updateNode = (id: string, checked: boolean) => {
|
|
318
|
-
let newTreeData;
|
|
319
|
-
let checkedNodes: any[] = []; // Array to store the entire checked node objects
|
|
320
|
-
|
|
321
|
-
if (isMultiple) {
|
|
322
|
-
// Multi-select logic: update only the clicked node
|
|
323
|
-
newTreeData = updateTreeNode(dropDownData, id, (node) => {
|
|
324
|
-
node.checked = checked;
|
|
325
|
-
|
|
326
|
-
// Update the checked nodes array
|
|
327
|
-
if (checked) {
|
|
328
|
-
checkedNodes.push(node); // Add the entire node if checked
|
|
329
|
-
} else {
|
|
330
|
-
// Remove the node from the array if unchecked
|
|
331
|
-
checkedNodes = checkedNodes.filter(
|
|
332
|
-
(checkedNode) => checkedNode[descId] !== node[descId]
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
// Find all checked nodes in the newTreeData (in case you need all checked nodes at any point)
|
|
338
|
-
checkedNodes = [];
|
|
339
|
-
newTreeData.forEach((node) => {
|
|
340
|
-
getCheckedNodes(node, checkedNodes); // Recursively collect checked nodes
|
|
341
|
-
});
|
|
342
|
-
setSelectedItems(checkedNodes);
|
|
343
|
-
onChange(checkedNodes);
|
|
344
|
-
} else {
|
|
345
|
-
// Single-select logic: uncheck all other nodes and check only the clicked one
|
|
346
|
-
newTreeData = dropDownData.map((node) =>
|
|
347
|
-
updateAllNodes(node, id, checked)
|
|
348
|
-
);
|
|
349
|
-
}
|
|
350
|
-
setDropDownData(newTreeData);
|
|
351
|
-
};
|
|
352
|
-
const getCheckedNodes = (node: any, checkedNodes: any[]) => {
|
|
353
|
-
if (node.checked) {
|
|
354
|
-
checkedNodes.push(node); // Push the entire node object
|
|
355
|
-
}
|
|
356
|
-
if (node.children && node.children.length > 0) {
|
|
357
|
-
node.children.forEach((child: any) =>
|
|
358
|
-
getCheckedNodes(child, checkedNodes)
|
|
359
|
-
);
|
|
360
|
-
}
|
|
361
|
-
};
|
|
362
|
-
|
|
363
|
-
// Recursive function to update a specific node by ID
|
|
364
|
-
const updateTreeNode = (
|
|
365
|
-
nodes: any[],
|
|
366
|
-
id: string,
|
|
367
|
-
updateFn: (node: any) => void
|
|
368
|
-
): any[] => {
|
|
369
|
-
return nodes.map((node) => {
|
|
370
|
-
if (node[descId] === id) {
|
|
371
|
-
updateFn(node);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
if (node.children) {
|
|
375
|
-
node.children = updateTreeNode(node.children, id, updateFn);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
return node;
|
|
379
|
-
});
|
|
380
|
-
};
|
|
381
|
-
|
|
382
|
-
// Recursive function to uncheck all other nodes in single-select mode
|
|
383
|
-
const updateAllNodes = (node: any, id: string, checked: boolean): any => {
|
|
384
|
-
// Reset all checked states to false, except for the selected node
|
|
385
|
-
node.checked = node[descId] === id ? checked : false;
|
|
386
|
-
if (node.checked) {
|
|
387
|
-
setInputValue(node[desc]);
|
|
388
|
-
onChange(node);
|
|
389
|
-
}
|
|
390
|
-
if (node.children) {
|
|
391
|
-
node.children = node.children.map((child: any) =>
|
|
392
|
-
updateAllNodes(child, id, checked)
|
|
393
|
-
);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
return node;
|
|
397
|
-
};
|
|
398
|
-
const renderTree = (node: any, index: number) => {
|
|
399
|
-
return (
|
|
400
|
-
<div className=" ml-4 mt-4 flex flex-col gap-4">
|
|
401
|
-
<TreeNode
|
|
402
|
-
key={node[descId]}
|
|
403
|
-
node={node}
|
|
404
|
-
// handleMultiSelect={handleMultiSelect}
|
|
405
|
-
selected={selected}
|
|
406
|
-
isMultiple={isMultiple}
|
|
407
|
-
singleSelect={singleSelect}
|
|
408
|
-
idx={node[descId]}
|
|
409
|
-
updateNode={updateNode}
|
|
410
|
-
desc={desc}
|
|
411
|
-
descId={descId}
|
|
412
|
-
toggleExpand={toggleExpand}
|
|
413
|
-
>
|
|
414
|
-
{node.expanded &&
|
|
415
|
-
node.children?.map((childNode: any, index: number) =>
|
|
416
|
-
renderTree(childNode, index)
|
|
417
|
-
)}
|
|
418
|
-
</TreeNode>
|
|
419
|
-
</div>
|
|
420
|
-
);
|
|
421
|
-
};
|
|
422
|
-
|
|
423
|
-
return (
|
|
424
|
-
<div className={fullWidth ? "fullWidth" : "autoWidth"} ref={dropdownRef}>
|
|
425
|
-
{label && (
|
|
426
|
-
<div
|
|
427
|
-
style={{
|
|
428
|
-
marginBottom: 5,
|
|
429
|
-
display: "flex",
|
|
430
|
-
justifyContent: "space-between",
|
|
431
|
-
}}
|
|
432
|
-
>
|
|
433
|
-
<label className={`labels label-text`}>
|
|
434
|
-
{label}
|
|
435
|
-
{required && <span className="text-error"> *</span>}
|
|
436
|
-
</label>
|
|
437
|
-
<span onClick={() => handleAction?.()} className={`action_label`}>
|
|
438
|
-
{actionLabel}
|
|
439
|
-
</span>
|
|
440
|
-
</div>
|
|
441
|
-
)}
|
|
442
|
-
{/* Displaying selected items for multi-select */}
|
|
443
|
-
|
|
444
|
-
<div
|
|
445
|
-
className={`${
|
|
446
|
-
expandable ? "qbs-expandable-container" : "qbs-container"
|
|
447
|
-
}`}
|
|
448
|
-
style={{ position: "relative" }}
|
|
449
|
-
>
|
|
450
|
-
{selectedItems?.length > 0 && (
|
|
451
|
-
<>
|
|
452
|
-
{selectedItems
|
|
453
|
-
.slice(
|
|
454
|
-
0,
|
|
455
|
-
selectedItems?.length > itemCount
|
|
456
|
-
? itemCount
|
|
457
|
-
: selectedItems?.length
|
|
458
|
-
)
|
|
459
|
-
.map((item, index) => (
|
|
460
|
-
<div key={index} className="selected-items-container">
|
|
461
|
-
<Tooltip
|
|
462
|
-
title={item?.[desc]}
|
|
463
|
-
enabled={item?.[desc]?.length > textCount}
|
|
464
|
-
>
|
|
465
|
-
<div key={item.id} className="selected-item">
|
|
466
|
-
{item?.[desc]?.length > textCount
|
|
467
|
-
? `${item?.[desc].substring(0, textCount)}...`
|
|
468
|
-
: item?.[desc]}
|
|
469
|
-
|
|
470
|
-
<button
|
|
471
|
-
onClick={() => handleRemoveSelectedItem(index)}
|
|
472
|
-
className="remove-item-btn"
|
|
473
|
-
aria-label={`Remove ${item?.[desc]}`}
|
|
474
|
-
>
|
|
475
|
-
X
|
|
476
|
-
</button>
|
|
477
|
-
</div>
|
|
478
|
-
</Tooltip>
|
|
479
|
-
</div>
|
|
480
|
-
))}
|
|
481
|
-
{selectedItems?.length > itemCount && (
|
|
482
|
-
<div className="selected-items-container">
|
|
483
|
-
<Tooltip title={tooltipContent} enabled={true}>
|
|
484
|
-
<div className="selected-item-more">
|
|
485
|
-
+{selectedItems?.length - itemCount} more
|
|
486
|
-
</div>
|
|
487
|
-
</Tooltip>
|
|
488
|
-
</div>
|
|
489
|
-
)}
|
|
490
|
-
</>
|
|
491
|
-
)}
|
|
492
|
-
|
|
493
|
-
<div
|
|
494
|
-
className={`qbs-textfield-expandable ${
|
|
495
|
-
!expandable ? "qbs-normal" : ""
|
|
496
|
-
}`}
|
|
497
|
-
data-value={
|
|
498
|
-
selectedItems?.length > 0 || searchValue
|
|
499
|
-
? searchValue
|
|
500
|
-
: placeholder ?? ""
|
|
501
|
-
}
|
|
502
|
-
onClick={() => onInputFocus()}
|
|
503
|
-
>
|
|
504
|
-
<input
|
|
505
|
-
id={id}
|
|
506
|
-
ref={inputRef}
|
|
507
|
-
type="text"
|
|
508
|
-
value={
|
|
509
|
-
type === "auto_suggestion" && !expandable
|
|
510
|
-
? inputValue
|
|
511
|
-
: searchValue || inputValue
|
|
512
|
-
}
|
|
513
|
-
onChange={handleChange}
|
|
514
|
-
// onBlur={handleBlur}
|
|
515
|
-
onFocus={onFocus}
|
|
516
|
-
onClick={() => handleOnClick()}
|
|
517
|
-
className={generateClassName()}
|
|
518
|
-
placeholder={selectedItems?.length > 0 ? "" : placeholder ?? ""}
|
|
519
|
-
readOnly={
|
|
520
|
-
readOnly ||
|
|
521
|
-
type === "custom_select" ||
|
|
522
|
-
(type == "auto_suggestion" && !expandable)
|
|
523
|
-
}
|
|
524
|
-
disabled={disabled}
|
|
525
|
-
data-testid="custom-autocomplete"
|
|
526
|
-
/>
|
|
527
|
-
</div>
|
|
528
|
-
|
|
529
|
-
{/* Icons for Clearing Input or Toggling Dropdown */}
|
|
530
|
-
|
|
531
|
-
<InputActions
|
|
532
|
-
inputValue={inputValue}
|
|
533
|
-
searchValue={searchValue}
|
|
534
|
-
dropOpen={dropOpen}
|
|
535
|
-
handleDropOpen={handleDropOpen}
|
|
536
|
-
handleDropClose={handleDropClose}
|
|
537
|
-
disabled={disabled}
|
|
538
|
-
readOnly={readOnly}
|
|
539
|
-
expandable={expandable}
|
|
540
|
-
handleClear={handleClear}
|
|
541
|
-
/>
|
|
542
|
-
{/* Displaying Loading Spinner */}
|
|
543
|
-
|
|
544
|
-
{/* Suggestions Dropdown */}
|
|
545
|
-
|
|
546
|
-
{dropOpen &&
|
|
547
|
-
ReactDOM.createPortal(
|
|
548
|
-
<ul
|
|
549
|
-
ref={dropRef}
|
|
550
|
-
style={{ ...dropdownStyle, minHeight: 192 }}
|
|
551
|
-
className={`qbs-autocomplete-suggestions`}
|
|
552
|
-
>
|
|
553
|
-
{type == "auto_suggestion" && !expandable && (
|
|
554
|
-
<div
|
|
555
|
-
style={{ position: "relative" }}
|
|
556
|
-
className="react-core-ts-search-container"
|
|
557
|
-
>
|
|
558
|
-
<span className="dropdown-search-icon">
|
|
559
|
-
<Search />
|
|
560
|
-
</span>
|
|
561
|
-
<input
|
|
562
|
-
className="dropdown-search-input"
|
|
563
|
-
onChange={handleSuggestionChange}
|
|
564
|
-
value={searchValue}
|
|
565
|
-
placeholder="Search"
|
|
566
|
-
/>
|
|
567
|
-
</div>
|
|
568
|
-
)}
|
|
569
|
-
|
|
570
|
-
<div
|
|
571
|
-
className={`qbs-autocomplete-suggestions-sub ${
|
|
572
|
-
isTreeDropdown ? " gap-4 min-h-[184px]" : ""
|
|
573
|
-
}`}
|
|
574
|
-
>
|
|
575
|
-
{dropDownData?.length > 0 ? (
|
|
576
|
-
dropDownData.map((suggestion: any, idx: number) => (
|
|
577
|
-
<TreeNode
|
|
578
|
-
key={suggestion[descId]}
|
|
579
|
-
node={suggestion}
|
|
580
|
-
// isSelected={isSelected}
|
|
581
|
-
// handleMultiSelect={handleMultiSelect}
|
|
582
|
-
selected={selected}
|
|
583
|
-
isMultiple={isMultiple}
|
|
584
|
-
singleSelect={singleSelect}
|
|
585
|
-
idx={suggestion[descId]}
|
|
586
|
-
updateNode={updateNode}
|
|
587
|
-
desc={desc}
|
|
588
|
-
descId={descId}
|
|
589
|
-
toggleExpand={toggleExpand}
|
|
590
|
-
>
|
|
591
|
-
{suggestion.expanded &&
|
|
592
|
-
suggestion.children?.map(
|
|
593
|
-
(childNode: any, index: number) =>
|
|
594
|
-
renderTree(childNode, index)
|
|
595
|
-
)}
|
|
596
|
-
</TreeNode>
|
|
597
|
-
))
|
|
598
|
-
) : (
|
|
599
|
-
<>
|
|
600
|
-
{isLoading ? (
|
|
601
|
-
<div
|
|
602
|
-
style={{ display: "flex", justifyContent: "center" }}
|
|
603
|
-
>
|
|
604
|
-
<span>
|
|
605
|
-
<Spinner />
|
|
606
|
-
</span>
|
|
607
|
-
</div>
|
|
608
|
-
) : (
|
|
609
|
-
<li
|
|
610
|
-
className="qbs-autocomplete-notfound"
|
|
611
|
-
onClick={handleBlur}
|
|
612
|
-
>
|
|
613
|
-
{notDataMessage ?? "No Results Found"}
|
|
614
|
-
</li>
|
|
615
|
-
)}
|
|
616
|
-
</>
|
|
617
|
-
)}
|
|
618
|
-
</div>
|
|
619
|
-
</ul>,
|
|
620
|
-
document.body
|
|
621
|
-
)}
|
|
622
|
-
</div>
|
|
623
|
-
|
|
624
|
-
{/* Displaying Validation Error */}
|
|
625
|
-
{errors && (
|
|
626
|
-
<div
|
|
627
|
-
className="text-error text-error-label mt-[1px]"
|
|
628
|
-
data-testid="autocomplete-error"
|
|
629
|
-
>
|
|
630
|
-
{errors.message}
|
|
631
|
-
</div>
|
|
632
|
-
)}
|
|
633
|
-
</div>
|
|
634
|
-
);
|
|
635
|
-
}
|
|
636
|
-
);
|
|
637
|
-
|
|
638
|
-
export default React.memo(AutoCompleteWithTreeStructure);
|