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
package/docs/store.ts
CHANGED
|
@@ -1,76 +1,67 @@
|
|
|
1
1
|
export const treeDropData = [
|
|
2
2
|
{
|
|
3
|
-
name:
|
|
3
|
+
name: 'Port A - This is the main port in the northern region with significant traffic and operations',
|
|
4
4
|
id: 22,
|
|
5
|
-
|
|
6
|
-
{
|
|
7
|
-
name: "Port C - A key sub-port with heavy container traffic in the southern bay area",
|
|
8
|
-
id: 1,
|
|
9
|
-
children: [
|
|
10
|
-
{
|
|
11
|
-
name: "Port G - A small port used for local shipments and fishing operations",
|
|
12
|
-
id: 2,
|
|
13
|
-
children: [
|
|
14
|
-
{
|
|
15
|
-
name: "Port H - A minor local port focusing on fisheries and small-scale cargo",
|
|
16
|
-
id: 3,
|
|
17
|
-
children: [],
|
|
18
|
-
},
|
|
19
|
-
],
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
name: "Port I - A coastal port for regional trade and tourism operations",
|
|
23
|
-
id: 4,
|
|
24
|
-
children: [
|
|
25
|
-
{
|
|
26
|
-
name: "Port J - A harbor for tourist cruises and commercial boats",
|
|
27
|
-
id: 5,
|
|
28
|
-
children: [],
|
|
29
|
-
},
|
|
30
|
-
],
|
|
31
|
-
},
|
|
32
|
-
],
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
name: "Port D - An industrial port dealing with large-scale cargo and ship repair services",
|
|
36
|
-
id: 6,
|
|
37
|
-
children: [
|
|
38
|
-
{
|
|
39
|
-
name: "Port K - A dock specializing in shipbuilding and marine repairs",
|
|
40
|
-
id: 7,
|
|
41
|
-
children: [
|
|
42
|
-
{
|
|
43
|
-
name: "Port L - A repair facility for both military and commercial vessels",
|
|
44
|
-
id: 8,
|
|
45
|
-
children: [],
|
|
46
|
-
},
|
|
47
|
-
],
|
|
48
|
-
},
|
|
49
|
-
],
|
|
50
|
-
},
|
|
51
|
-
],
|
|
5
|
+
parentId: null,
|
|
52
6
|
},
|
|
53
7
|
{
|
|
54
|
-
name:
|
|
8
|
+
name: 'Port C - A key sub-port with heavy container traffic in the southern bay area',
|
|
9
|
+
id: 1,
|
|
10
|
+
parentId: 22,
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'Port G - A small port used for local shipments and fishing operations',
|
|
14
|
+
id: 2,
|
|
15
|
+
parentId: 1,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'Port H - A minor local port focusing on fisheries and small-scale cargo',
|
|
19
|
+
id: 3,
|
|
20
|
+
parentId: 2,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'Port I - A coastal port for regional trade and tourism operations',
|
|
24
|
+
id: 4,
|
|
25
|
+
parentId: 1,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'Port J - A harbor for tourist cruises and commercial boats',
|
|
29
|
+
id: 5,
|
|
30
|
+
parentId: 4,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'Port D - An industrial port dealing with large-scale cargo and ship repair services',
|
|
34
|
+
id: 6,
|
|
35
|
+
parentId: 22,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'Port K - A dock specializing in shipbuilding and marine repairs',
|
|
39
|
+
id: 7,
|
|
40
|
+
parentId: 6,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'Port L - A repair facility for both military and commercial vessels',
|
|
44
|
+
id: 8,
|
|
45
|
+
parentId: 7,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'Port B - This is the largest port in the central region, supporting international trade',
|
|
55
49
|
id: 9,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
],
|
|
73
|
-
},
|
|
74
|
-
],
|
|
50
|
+
parentId: null,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'Port E - A primary export port dealing with raw materials and heavy goods',
|
|
54
|
+
id: 10,
|
|
55
|
+
parentId: 9,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'Port M - A small export hub for agricultural and mining products',
|
|
59
|
+
id: 11,
|
|
60
|
+
parentId: 10,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'Port N - A warehouse and storage center for exported goods',
|
|
64
|
+
id: 12,
|
|
65
|
+
parentId: 11,
|
|
75
66
|
},
|
|
76
67
|
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-core-ts",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.26",
|
|
4
4
|
"description": "React Components",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"start": "webpack serve --open --mode development",
|
|
9
9
|
"build": "tsc && cp -R src/styles dist/styles",
|
|
10
10
|
"test": "jest",
|
|
11
|
-
"prepublishOnly": "npm run build"
|
|
11
|
+
"prepublishOnly": "npm run build && rm -rf src"
|
|
12
12
|
},
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
package/tailwind.config.js
CHANGED
package/src/AutoComplete.tsx
DELETED
|
@@ -1,489 +0,0 @@
|
|
|
1
|
-
import Tooltip from './utilities/tooltip';
|
|
2
|
-
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
|
3
|
-
|
|
4
|
-
import { AutoSuggestionInputProps } from './commontypes';
|
|
5
|
-
import { useSuggestions } from './utilities/autosuggestions';
|
|
6
|
-
import { debounce } from './utilities/debounce';
|
|
7
|
-
import { deepEqual } from './utilities/deepEqual';
|
|
8
|
-
import { filterSuggestions } from './utilities/filterSuggestions';
|
|
9
|
-
import { Close, DropArrow, Search, Spinner } from './utilities/icons';
|
|
10
|
-
|
|
11
|
-
type ValueProps = {
|
|
12
|
-
[key: string]: string;
|
|
13
|
-
};
|
|
14
|
-
const AutoComplete = forwardRef<HTMLInputElement, AutoSuggestionInputProps>(
|
|
15
|
-
(
|
|
16
|
-
{
|
|
17
|
-
label,
|
|
18
|
-
onChange,
|
|
19
|
-
getData = async () => [],
|
|
20
|
-
data = [],
|
|
21
|
-
errors,
|
|
22
|
-
required = false,
|
|
23
|
-
name,
|
|
24
|
-
fullWidth = false,
|
|
25
|
-
placeholder,
|
|
26
|
-
id,
|
|
27
|
-
type = 'custom_select',
|
|
28
|
-
selectedItems: propsSeelctedItems = [],
|
|
29
|
-
readOnly = false,
|
|
30
|
-
disabled = false,
|
|
31
|
-
value,
|
|
32
|
-
isMultiple = false,
|
|
33
|
-
desc = 'name',
|
|
34
|
-
descId = 'id',
|
|
35
|
-
singleSelect,
|
|
36
|
-
className,
|
|
37
|
-
async = false,
|
|
38
|
-
paginationEnabled,
|
|
39
|
-
initialLoad,
|
|
40
|
-
actionLabel,
|
|
41
|
-
handleAction,
|
|
42
|
-
nextBlock,
|
|
43
|
-
notDataMessage,
|
|
44
|
-
onFocus,
|
|
45
|
-
},
|
|
46
|
-
ref
|
|
47
|
-
) => {
|
|
48
|
-
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
49
|
-
// State Hooks Section
|
|
50
|
-
const [isInitialRender, setIsInitialRender] = useState(true);
|
|
51
|
-
|
|
52
|
-
const [inputValue, setInputValue] = useState<string>(value);
|
|
53
|
-
const [searchValue, setSearchValue] = useState<string>('');
|
|
54
|
-
const [nextPage, setNextPage] = useState<number | undefined>(1);
|
|
55
|
-
const [dropOpen, setDropOpen] = useState<boolean>(false);
|
|
56
|
-
const [selectedItems, setSelectedItems] = useState<ValueProps[]>([]);
|
|
57
|
-
// API call for suggestions through a custom hook
|
|
58
|
-
const [dropdownPosition, setDropdownPosition] = useState('bottom');
|
|
59
|
-
const inputRef = useRef(null);
|
|
60
|
-
const dropRef = useRef(null);
|
|
61
|
-
useImperativeHandle(ref, () => inputRef.current);
|
|
62
|
-
|
|
63
|
-
const adjustDropdownPosition = () => {
|
|
64
|
-
if (inputRef.current && dropRef.current) {
|
|
65
|
-
const inputBoxRect = inputRef.current.getBoundingClientRect();
|
|
66
|
-
const viewportHeight = window.innerHeight;
|
|
67
|
-
|
|
68
|
-
const spaceAbove = inputBoxRect.top;
|
|
69
|
-
const spaceBelow = viewportHeight - inputBoxRect.bottom;
|
|
70
|
-
if (spaceAbove > spaceBelow) {
|
|
71
|
-
setDropdownPosition('top');
|
|
72
|
-
} else {
|
|
73
|
-
setDropdownPosition('bottom');
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
useEffect(() => {
|
|
79
|
-
window.addEventListener('resize', adjustDropdownPosition);
|
|
80
|
-
adjustDropdownPosition();
|
|
81
|
-
|
|
82
|
-
return () => {
|
|
83
|
-
window.removeEventListener('resize', adjustDropdownPosition);
|
|
84
|
-
};
|
|
85
|
-
}, [dropOpen]);
|
|
86
|
-
|
|
87
|
-
const { suggestions, isLoading, handlePickSuggestions } = useSuggestions(
|
|
88
|
-
getData,
|
|
89
|
-
data,
|
|
90
|
-
dropOpen,
|
|
91
|
-
async,
|
|
92
|
-
paginationEnabled,
|
|
93
|
-
initialLoad,
|
|
94
|
-
inputValue,
|
|
95
|
-
isMultiple,
|
|
96
|
-
setNextPage,
|
|
97
|
-
selectedItems
|
|
98
|
-
// nextBlock
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
// Handling the selection of a suggestion
|
|
102
|
-
const handleSuggestionClick = useCallback((suggestion: ValueProps) => {
|
|
103
|
-
if (isMultiple) {
|
|
104
|
-
setSelectedItems((prev) => [...prev, suggestion]);
|
|
105
|
-
} else {
|
|
106
|
-
setInputValue(suggestion[desc]);
|
|
107
|
-
}
|
|
108
|
-
setSearchValue('');
|
|
109
|
-
setInputValue(suggestion[desc]);
|
|
110
|
-
onChange(suggestion);
|
|
111
|
-
setDropOpen(false);
|
|
112
|
-
}, []);
|
|
113
|
-
|
|
114
|
-
// Adding debounce to avoid making API calls on every keystroke
|
|
115
|
-
const handleChangeWithDebounce = debounce((value) => {
|
|
116
|
-
if ((type === 'auto_complete' || type === 'auto_suggestion') && async) {
|
|
117
|
-
handlePickSuggestions(value, 1);
|
|
118
|
-
}
|
|
119
|
-
}, 1000);
|
|
120
|
-
|
|
121
|
-
const handleMultiSelect = (e: any, suggestion: ValueProps) => {
|
|
122
|
-
const { checked } = e.target;
|
|
123
|
-
if (isMultiple) {
|
|
124
|
-
if (checked) {
|
|
125
|
-
setSelectedItems((prev) => [...prev, suggestion]);
|
|
126
|
-
} else {
|
|
127
|
-
setSelectedItems((prev) => {
|
|
128
|
-
return prev.filter(
|
|
129
|
-
(item, i) => item[descId] !== suggestion[descId]
|
|
130
|
-
);
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
} else {
|
|
134
|
-
if (checked) {
|
|
135
|
-
setInputValue(suggestion[desc]);
|
|
136
|
-
onChange(suggestion);
|
|
137
|
-
} else {
|
|
138
|
-
setInputValue('');
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
useEffect(() => {
|
|
143
|
-
if (!deepEqual(selectedItems, propsSeelctedItems))
|
|
144
|
-
setSelectedItems(propsSeelctedItems);
|
|
145
|
-
}, [propsSeelctedItems]);
|
|
146
|
-
// Effect to set the input value whenever `value` prop changes
|
|
147
|
-
useEffect(() => {
|
|
148
|
-
setInputValue(value ?? '');
|
|
149
|
-
}, [value]);
|
|
150
|
-
|
|
151
|
-
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
152
|
-
const { value } = e.target;
|
|
153
|
-
setDropOpen(true);
|
|
154
|
-
setSearchValue(value);
|
|
155
|
-
handleChangeWithDebounce(value);
|
|
156
|
-
if (!value) {
|
|
157
|
-
setInputValue('');
|
|
158
|
-
onChange({ [descId]: '', [desc]: '' });
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
const handleSuggestionChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
162
|
-
const { value } = e.target;
|
|
163
|
-
setDropOpen(true);
|
|
164
|
-
setSearchValue(value);
|
|
165
|
-
handleChangeWithDebounce(value);
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const handleBlur = () => {
|
|
169
|
-
setTimeout(() => {
|
|
170
|
-
setDropOpen(false);
|
|
171
|
-
}, 200);
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
const handleClear = () => {
|
|
175
|
-
if (searchValue) {
|
|
176
|
-
setSearchValue('');
|
|
177
|
-
setDropOpen(false);
|
|
178
|
-
} else {
|
|
179
|
-
setInputValue('');
|
|
180
|
-
onChange({ [descId]: '', [desc]: '' });
|
|
181
|
-
setDropOpen(false);
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
const generateClassName = useCallback(() => {
|
|
186
|
-
return `qbs-textfield-default ${className} ${
|
|
187
|
-
errors && errors?.message ? 'textfield-error' : 'textfield'
|
|
188
|
-
}`;
|
|
189
|
-
}, [errors, name]);
|
|
190
|
-
const handleRemoveSelectedItem = (index: number) => {
|
|
191
|
-
setSelectedItems((prev) => {
|
|
192
|
-
return prev.filter((_, i) => i !== index);
|
|
193
|
-
});
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
useEffect(() => {
|
|
197
|
-
if (isInitialRender) {
|
|
198
|
-
setIsInitialRender(false);
|
|
199
|
-
} else {
|
|
200
|
-
onChange(selectedItems);
|
|
201
|
-
}
|
|
202
|
-
}, [selectedItems]);
|
|
203
|
-
|
|
204
|
-
useEffect(() => {
|
|
205
|
-
const handleClickOutside = (event: React.MouseEvent) => {
|
|
206
|
-
if (
|
|
207
|
-
dropdownRef.current &&
|
|
208
|
-
event.target instanceof Node &&
|
|
209
|
-
!dropdownRef.current.contains(event.target)
|
|
210
|
-
) {
|
|
211
|
-
setDropOpen(false);
|
|
212
|
-
setSearchValue('');
|
|
213
|
-
}
|
|
214
|
-
};
|
|
215
|
-
document.addEventListener('mousedown', handleClickOutside as any);
|
|
216
|
-
return () => {
|
|
217
|
-
document.removeEventListener('mousedown', handleClickOutside as any);
|
|
218
|
-
};
|
|
219
|
-
}, []);
|
|
220
|
-
|
|
221
|
-
// Filtering suggestions based on type and search value
|
|
222
|
-
const selected: any = isMultiple ? selectedItems : inputValue;
|
|
223
|
-
const filteredData: ValueProps[] = filterSuggestions(
|
|
224
|
-
suggestions,
|
|
225
|
-
searchValue,
|
|
226
|
-
type,
|
|
227
|
-
desc,
|
|
228
|
-
selected,
|
|
229
|
-
async
|
|
230
|
-
);
|
|
231
|
-
const isSelected = (
|
|
232
|
-
item: ValueProps,
|
|
233
|
-
selectedItems: ValueProps[] | string
|
|
234
|
-
): boolean => {
|
|
235
|
-
if (Array.isArray(selectedItems)) {
|
|
236
|
-
return selectedItems.some(
|
|
237
|
-
(selectedItem) => selectedItem[desc] === item[desc]
|
|
238
|
-
);
|
|
239
|
-
} else {
|
|
240
|
-
return item[desc] === selectedItems;
|
|
241
|
-
}
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
const handleLoadMore = () => {
|
|
245
|
-
if (paginationEnabled) {
|
|
246
|
-
handlePickSuggestions(searchValue, nextPage + 1, true);
|
|
247
|
-
setNextPage(nextPage + 1);
|
|
248
|
-
}
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
const handleOnClick = () => {
|
|
252
|
-
!disabled && !readOnly ? setDropOpen(true) : '';
|
|
253
|
-
};
|
|
254
|
-
const tooltipContent =
|
|
255
|
-
selectedItems?.length > 1
|
|
256
|
-
? selectedItems
|
|
257
|
-
?.slice(1)
|
|
258
|
-
.map((item) => item[desc])
|
|
259
|
-
.join(', ')
|
|
260
|
-
: '';
|
|
261
|
-
|
|
262
|
-
return (
|
|
263
|
-
<div className={fullWidth ? 'fullWidth' : 'autoWidth'} ref={dropdownRef}>
|
|
264
|
-
{label && (
|
|
265
|
-
<div
|
|
266
|
-
style={{
|
|
267
|
-
marginBottom: 5,
|
|
268
|
-
display: 'flex',
|
|
269
|
-
justifyContent: 'space-between',
|
|
270
|
-
}}
|
|
271
|
-
>
|
|
272
|
-
<label className={`labels label-text`}>
|
|
273
|
-
{label}
|
|
274
|
-
{required && <span className="text-error"> *</span>}
|
|
275
|
-
</label>
|
|
276
|
-
<span onClick={() => handleAction?.()} className={`action_label`}>
|
|
277
|
-
{actionLabel}
|
|
278
|
-
</span>
|
|
279
|
-
</div>
|
|
280
|
-
)}
|
|
281
|
-
{/* Displaying selected items for multi-select */}
|
|
282
|
-
|
|
283
|
-
<div style={{ position: 'relative' }}>
|
|
284
|
-
<div className="selected-items-container ">
|
|
285
|
-
{selectedItems?.length > 0 && (
|
|
286
|
-
<>
|
|
287
|
-
<div key={selectedItems[0].id} className="selected-item">
|
|
288
|
-
<Tooltip title={selectedItems[0]?.[desc]}>
|
|
289
|
-
{selectedItems[0]?.[desc]?.length > 8
|
|
290
|
-
? `${selectedItems[0]?.[desc].substring(0, 8)}...`
|
|
291
|
-
: selectedItems[0]?.[desc]}
|
|
292
|
-
</Tooltip>
|
|
293
|
-
<button
|
|
294
|
-
onClick={() => handleRemoveSelectedItem(0)}
|
|
295
|
-
className="remove-item-btn"
|
|
296
|
-
aria-label={`Remove ${selectedItems[0]?.[desc]}`}
|
|
297
|
-
>
|
|
298
|
-
X
|
|
299
|
-
</button>
|
|
300
|
-
</div>
|
|
301
|
-
<Tooltip title={tooltipContent}>
|
|
302
|
-
{selectedItems?.length > 1 && (
|
|
303
|
-
<div className="selected-item-more">
|
|
304
|
-
+{selectedItems?.length - 1} more
|
|
305
|
-
</div>
|
|
306
|
-
)}
|
|
307
|
-
</Tooltip>
|
|
308
|
-
</>
|
|
309
|
-
)}
|
|
310
|
-
</div>
|
|
311
|
-
<input
|
|
312
|
-
id={id}
|
|
313
|
-
ref={inputRef}
|
|
314
|
-
type="text"
|
|
315
|
-
value={
|
|
316
|
-
type === 'auto_suggestion'
|
|
317
|
-
? inputValue
|
|
318
|
-
: searchValue || inputValue
|
|
319
|
-
}
|
|
320
|
-
onChange={handleChange}
|
|
321
|
-
// onBlur={handleBlur}
|
|
322
|
-
onFocus={onFocus}
|
|
323
|
-
onClick={() => handleOnClick()}
|
|
324
|
-
className={generateClassName()}
|
|
325
|
-
placeholder={selectedItems?.length > 0 ? '' : placeholder ?? ''}
|
|
326
|
-
readOnly={
|
|
327
|
-
readOnly || type === 'custom_select' || type == 'auto_suggestion'
|
|
328
|
-
}
|
|
329
|
-
disabled={disabled}
|
|
330
|
-
data-testid="custom-autocomplete"
|
|
331
|
-
/>
|
|
332
|
-
|
|
333
|
-
{/* Icons for Clearing Input or Toggling Dropdown */}
|
|
334
|
-
<div className="qbs-autocomplete-close-icon">
|
|
335
|
-
{(inputValue || searchValue) && !disabled && !readOnly && (
|
|
336
|
-
<button
|
|
337
|
-
onClick={handleClear}
|
|
338
|
-
className="icon-button"
|
|
339
|
-
aria-label="clear"
|
|
340
|
-
>
|
|
341
|
-
<Close />
|
|
342
|
-
</button>
|
|
343
|
-
)}
|
|
344
|
-
|
|
345
|
-
<button
|
|
346
|
-
disabled={disabled || readOnly}
|
|
347
|
-
type="button"
|
|
348
|
-
onClick={() => setDropOpen(!dropOpen)}
|
|
349
|
-
className="icon-button"
|
|
350
|
-
aria-label="toggle"
|
|
351
|
-
>
|
|
352
|
-
<DropArrow />
|
|
353
|
-
</button>
|
|
354
|
-
</div>
|
|
355
|
-
|
|
356
|
-
{/* Displaying Loading Spinner */}
|
|
357
|
-
|
|
358
|
-
{/* Suggestions Dropdown */}
|
|
359
|
-
{dropOpen && (
|
|
360
|
-
<ul
|
|
361
|
-
ref={dropRef}
|
|
362
|
-
className={`qbs-autocomplete-suggestions ${dropdownPosition}`}
|
|
363
|
-
>
|
|
364
|
-
{type == 'auto_suggestion' && (
|
|
365
|
-
<div
|
|
366
|
-
style={{ position: 'relative' }}
|
|
367
|
-
className="react-core-ts-search-container"
|
|
368
|
-
>
|
|
369
|
-
<span className="dropdown-search-icon">
|
|
370
|
-
<Search />
|
|
371
|
-
</span>
|
|
372
|
-
<input
|
|
373
|
-
className="dropdown-search-input"
|
|
374
|
-
onChange={handleSuggestionChange}
|
|
375
|
-
value={searchValue}
|
|
376
|
-
placeholder="Search"
|
|
377
|
-
/>
|
|
378
|
-
</div>
|
|
379
|
-
)}
|
|
380
|
-
|
|
381
|
-
<div className="qbs-autocomplete-suggestions-sub">
|
|
382
|
-
{/* Displaying Suggestions or Not Found Message */}
|
|
383
|
-
{/* {isLoading && (
|
|
384
|
-
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
|
385
|
-
<span>
|
|
386
|
-
<Spinner />
|
|
387
|
-
</span>
|
|
388
|
-
</div>
|
|
389
|
-
|
|
390
|
-
{/* )} */}
|
|
391
|
-
|
|
392
|
-
{filteredData?.length > 0 ? (
|
|
393
|
-
filteredData.map((suggestion: ValueProps, idx: number) => (
|
|
394
|
-
<div
|
|
395
|
-
key={idx.toString()}
|
|
396
|
-
className={`qbs-autocomplete-listitem-container ${
|
|
397
|
-
(isMultiple || singleSelect) &&
|
|
398
|
-
'qbs-autocomplete-checkbox-container'
|
|
399
|
-
} ${
|
|
400
|
-
isSelected(suggestion, selected) ? 'is-selected' : ''
|
|
401
|
-
}`}
|
|
402
|
-
>
|
|
403
|
-
{(isMultiple || singleSelect) && (
|
|
404
|
-
<div className="qbs-autocomplete-checkbox">
|
|
405
|
-
<input
|
|
406
|
-
onChange={(e) => handleMultiSelect(e, suggestion)}
|
|
407
|
-
type="checkbox"
|
|
408
|
-
checked={isSelected(suggestion, selected)}
|
|
409
|
-
id={`qbs-checkbox-${idx.toString()}`}
|
|
410
|
-
/>
|
|
411
|
-
<label htmlFor={`qbs-checkbox-${idx.toString()}`}>
|
|
412
|
-
<svg
|
|
413
|
-
width="8"
|
|
414
|
-
height="6"
|
|
415
|
-
viewBox="0 0 8 6"
|
|
416
|
-
fill="none"
|
|
417
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
418
|
-
>
|
|
419
|
-
<path
|
|
420
|
-
d="M0 3.21739L2.89883 6L8 1.06994L6.89494 0L2.89883 3.86768L1.09728 2.14745L0 3.21739Z"
|
|
421
|
-
fill="white"
|
|
422
|
-
/>
|
|
423
|
-
</svg>
|
|
424
|
-
</label>
|
|
425
|
-
</div>
|
|
426
|
-
)}
|
|
427
|
-
<li
|
|
428
|
-
key={idx}
|
|
429
|
-
className={`qbs-autocomplete-suggestions-item ${
|
|
430
|
-
isSelected(suggestion, selected) ? 'is-selected' : ''
|
|
431
|
-
}`}
|
|
432
|
-
onClick={() => handleSuggestionClick(suggestion)}
|
|
433
|
-
data-testid={suggestion[desc]}
|
|
434
|
-
>
|
|
435
|
-
{suggestion[desc]}
|
|
436
|
-
</li>
|
|
437
|
-
</div>
|
|
438
|
-
))
|
|
439
|
-
) : (
|
|
440
|
-
<>
|
|
441
|
-
{isLoading ? (
|
|
442
|
-
<div
|
|
443
|
-
style={{ display: 'flex', justifyContent: 'center' }}
|
|
444
|
-
>
|
|
445
|
-
<span>
|
|
446
|
-
<Spinner />
|
|
447
|
-
</span>
|
|
448
|
-
</div>
|
|
449
|
-
) : (
|
|
450
|
-
<li
|
|
451
|
-
className="qbs-autocomplete-notfound"
|
|
452
|
-
onClick={handleBlur}
|
|
453
|
-
>
|
|
454
|
-
{notDataMessage ?? 'No Results Found'}
|
|
455
|
-
</li>
|
|
456
|
-
)}
|
|
457
|
-
</>
|
|
458
|
-
)}
|
|
459
|
-
{paginationEnabled &&
|
|
460
|
-
nextBlock !== 0 &&
|
|
461
|
-
nextBlock !== undefined &&
|
|
462
|
-
filteredData?.length > 0 && (
|
|
463
|
-
<div
|
|
464
|
-
className="loadMoreSection"
|
|
465
|
-
onClick={() => handleLoadMore()}
|
|
466
|
-
>
|
|
467
|
-
<p style={{ margin: 2 }}>Load More</p>
|
|
468
|
-
</div>
|
|
469
|
-
)}
|
|
470
|
-
</div>
|
|
471
|
-
</ul>
|
|
472
|
-
)}
|
|
473
|
-
</div>
|
|
474
|
-
|
|
475
|
-
{/* Displaying Validation Error */}
|
|
476
|
-
{errors && (
|
|
477
|
-
<div
|
|
478
|
-
className="text-error text-error-label mt-[1px]"
|
|
479
|
-
data-testid="autocomplete-error"
|
|
480
|
-
>
|
|
481
|
-
{errors.message}
|
|
482
|
-
</div>
|
|
483
|
-
)}
|
|
484
|
-
</div>
|
|
485
|
-
);
|
|
486
|
-
}
|
|
487
|
-
);
|
|
488
|
-
|
|
489
|
-
export default React.memo(AutoComplete);
|