react-core-ts 1.0.7 → 1.0.8
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/ExpandableAutoComplete.d.ts +4 -0
- package/dist/ExpandableAutoComplete.js +335 -0
- package/dist/ExpandableAutoComplete.js.map +1 -0
- package/dist/commontypes.d.ts +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/styles/components/autocomplete.css +60 -5
- package/dist/utilities/expandableTootltip.d.ts +3 -0
- package/dist/utilities/expandableTootltip.js +155 -0
- package/dist/utilities/expandableTootltip.js.map +1 -0
- package/docs/App.tsx +31 -1
- package/package.json +1 -1
- package/src/ExpandableAutoComplete.tsx +543 -0
- package/src/commontypes.ts +3 -0
- package/src/index.tsx +2 -0
- package/src/styles/components/autocomplete.css +60 -5
- package/src/utilities/expandableTootltip.tsx +149 -0
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useCallback,
|
|
4
|
+
useEffect,
|
|
5
|
+
useImperativeHandle,
|
|
6
|
+
useRef,
|
|
7
|
+
useState,
|
|
8
|
+
} from "react";
|
|
9
|
+
|
|
10
|
+
import { AutoSuggestionInputProps } from "./commontypes";
|
|
11
|
+
import { useSuggestions } from "./utilities/autosuggestions";
|
|
12
|
+
import { debounce } from "./utilities/debounce";
|
|
13
|
+
import { deepEqual } from "./utilities/deepEqual";
|
|
14
|
+
import { filterSuggestions } from "./utilities/filterSuggestions";
|
|
15
|
+
import { Close, DropArrow, Search, Spinner } from "./utilities/icons";
|
|
16
|
+
import { default as Tooltip } from "./utilities/expandableTootltip";
|
|
17
|
+
|
|
18
|
+
type ValueProps = {
|
|
19
|
+
[key: string]: string;
|
|
20
|
+
};
|
|
21
|
+
const ExpandableAutoComplete = forwardRef<
|
|
22
|
+
HTMLInputElement,
|
|
23
|
+
AutoSuggestionInputProps
|
|
24
|
+
>(
|
|
25
|
+
(
|
|
26
|
+
{
|
|
27
|
+
label,
|
|
28
|
+
onChange,
|
|
29
|
+
getData = async () => [],
|
|
30
|
+
data = [],
|
|
31
|
+
errors,
|
|
32
|
+
required = false,
|
|
33
|
+
name,
|
|
34
|
+
fullWidth = false,
|
|
35
|
+
placeholder,
|
|
36
|
+
id,
|
|
37
|
+
type = "custom_select",
|
|
38
|
+
selectedItems: propsSeelctedItems = [],
|
|
39
|
+
readOnly = false,
|
|
40
|
+
disabled = false,
|
|
41
|
+
value,
|
|
42
|
+
isMultiple = false,
|
|
43
|
+
desc = "name",
|
|
44
|
+
descId = "id",
|
|
45
|
+
singleSelect,
|
|
46
|
+
className,
|
|
47
|
+
async = false,
|
|
48
|
+
paginationEnabled,
|
|
49
|
+
initialLoad,
|
|
50
|
+
actionLabel,
|
|
51
|
+
handleAction,
|
|
52
|
+
nextBlock,
|
|
53
|
+
notDataMessage,
|
|
54
|
+
onFocus,
|
|
55
|
+
expandable = false,
|
|
56
|
+
textCount = 10,
|
|
57
|
+
itemCount = 1,
|
|
58
|
+
},
|
|
59
|
+
ref
|
|
60
|
+
) => {
|
|
61
|
+
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
62
|
+
// State Hooks Section
|
|
63
|
+
const [isInitialRender, setIsInitialRender] = useState(true);
|
|
64
|
+
|
|
65
|
+
const [inputValue, setInputValue] = useState<string>(value);
|
|
66
|
+
const [searchValue, setSearchValue] = useState<string>("");
|
|
67
|
+
const [nextPage, setNextPage] = useState<number | undefined>(1);
|
|
68
|
+
const [dropOpen, setDropOpen] = useState<boolean>(false);
|
|
69
|
+
const [selectedItems, setSelectedItems] = useState<ValueProps[]>([]);
|
|
70
|
+
// API call for suggestions through a custom hook
|
|
71
|
+
const [dropdownPosition, setDropdownPosition] = useState("bottom");
|
|
72
|
+
const inputRef = useRef(null);
|
|
73
|
+
const dropRef = useRef(null);
|
|
74
|
+
useImperativeHandle(ref, () => inputRef.current);
|
|
75
|
+
|
|
76
|
+
const adjustDropdownPosition = () => {
|
|
77
|
+
if (inputRef.current && dropRef.current) {
|
|
78
|
+
const inputBoxRect = inputRef.current.getBoundingClientRect();
|
|
79
|
+
const viewportHeight = window.innerHeight;
|
|
80
|
+
|
|
81
|
+
const spaceAbove = inputBoxRect.top;
|
|
82
|
+
const spaceBelow = viewportHeight - inputBoxRect.bottom;
|
|
83
|
+
if (spaceAbove > spaceBelow) {
|
|
84
|
+
setDropdownPosition("top");
|
|
85
|
+
} else {
|
|
86
|
+
setDropdownPosition("bottom");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
window.addEventListener("resize", adjustDropdownPosition);
|
|
93
|
+
adjustDropdownPosition();
|
|
94
|
+
|
|
95
|
+
return () => {
|
|
96
|
+
window.removeEventListener("resize", adjustDropdownPosition);
|
|
97
|
+
};
|
|
98
|
+
}, [dropOpen]);
|
|
99
|
+
|
|
100
|
+
const { suggestions, isLoading, handlePickSuggestions } = useSuggestions(
|
|
101
|
+
getData,
|
|
102
|
+
data,
|
|
103
|
+
dropOpen,
|
|
104
|
+
async,
|
|
105
|
+
paginationEnabled,
|
|
106
|
+
initialLoad,
|
|
107
|
+
inputValue,
|
|
108
|
+
isMultiple,
|
|
109
|
+
setNextPage,
|
|
110
|
+
selectedItems
|
|
111
|
+
// nextBlock
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// Handling the selection of a suggestion
|
|
115
|
+
const handleSuggestionClick = useCallback((suggestion: ValueProps) => {
|
|
116
|
+
if (isMultiple) {
|
|
117
|
+
setSelectedItems((prev) => [...prev, suggestion]);
|
|
118
|
+
} else {
|
|
119
|
+
setInputValue(suggestion[desc]);
|
|
120
|
+
}
|
|
121
|
+
setSearchValue("");
|
|
122
|
+
setInputValue(suggestion[desc]);
|
|
123
|
+
onChange(suggestion);
|
|
124
|
+
setDropOpen(false);
|
|
125
|
+
}, []);
|
|
126
|
+
|
|
127
|
+
// Adding debounce to avoid making API calls on every keystroke
|
|
128
|
+
const handleChangeWithDebounce = debounce((value) => {
|
|
129
|
+
if ((type === "auto_complete" || type === "auto_suggestion") && async) {
|
|
130
|
+
handlePickSuggestions(value, 1);
|
|
131
|
+
}
|
|
132
|
+
}, 1000);
|
|
133
|
+
|
|
134
|
+
const handleMultiSelect = (e: any, suggestion: ValueProps) => {
|
|
135
|
+
const { checked } = e.target;
|
|
136
|
+
if (isMultiple) {
|
|
137
|
+
if (checked) {
|
|
138
|
+
setSelectedItems((prev) => [...prev, suggestion]);
|
|
139
|
+
} else {
|
|
140
|
+
setSelectedItems((prev) => {
|
|
141
|
+
return prev.filter(
|
|
142
|
+
(item, i) => item[descId] !== suggestion[descId]
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
} else {
|
|
147
|
+
if (checked) {
|
|
148
|
+
setInputValue(suggestion[desc]);
|
|
149
|
+
onChange(suggestion);
|
|
150
|
+
} else {
|
|
151
|
+
setInputValue("");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
if (!deepEqual(selectedItems, propsSeelctedItems))
|
|
157
|
+
setSelectedItems(propsSeelctedItems);
|
|
158
|
+
}, [propsSeelctedItems]);
|
|
159
|
+
// Effect to set the input value whenever `value` prop changes
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
setInputValue(value ?? "");
|
|
162
|
+
}, [value]);
|
|
163
|
+
|
|
164
|
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
165
|
+
const { value } = e.target;
|
|
166
|
+
setDropOpen(true);
|
|
167
|
+
setSearchValue(value);
|
|
168
|
+
handleChangeWithDebounce(value);
|
|
169
|
+
if (!value) {
|
|
170
|
+
setInputValue("");
|
|
171
|
+
onChange({ [descId]: "", [desc]: "" });
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
const handleSuggestionChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
175
|
+
const { value } = e.target;
|
|
176
|
+
setDropOpen(true);
|
|
177
|
+
setSearchValue(value);
|
|
178
|
+
handleChangeWithDebounce(value);
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const handleBlur = () => {
|
|
182
|
+
setTimeout(() => {
|
|
183
|
+
setDropOpen(false);
|
|
184
|
+
}, 200);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const handleClear = () => {
|
|
188
|
+
if (searchValue) {
|
|
189
|
+
setSearchValue("");
|
|
190
|
+
setDropOpen(false);
|
|
191
|
+
} else {
|
|
192
|
+
setInputValue("");
|
|
193
|
+
onChange({ [descId]: "", [desc]: "" });
|
|
194
|
+
setDropOpen(false);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const generateClassName = useCallback(() => {
|
|
199
|
+
return `qbs-textfield-default ${className} ${
|
|
200
|
+
errors && errors?.message ? "textfield-error" : "textfield"
|
|
201
|
+
} ${expandable ? "expandable" : ""}`;
|
|
202
|
+
}, [errors, name]);
|
|
203
|
+
const handleRemoveSelectedItem = (index: number) => {
|
|
204
|
+
setSelectedItems((prev) => {
|
|
205
|
+
return prev.filter((_, i) => i !== index);
|
|
206
|
+
});
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
useEffect(() => {
|
|
210
|
+
if (isInitialRender) {
|
|
211
|
+
setIsInitialRender(false);
|
|
212
|
+
} else {
|
|
213
|
+
onChange(selectedItems);
|
|
214
|
+
}
|
|
215
|
+
}, [selectedItems]);
|
|
216
|
+
|
|
217
|
+
useEffect(() => {
|
|
218
|
+
const handleClickOutside = (event: React.MouseEvent) => {
|
|
219
|
+
if (
|
|
220
|
+
dropdownRef.current &&
|
|
221
|
+
event.target instanceof Node &&
|
|
222
|
+
!dropdownRef.current.contains(event.target)
|
|
223
|
+
) {
|
|
224
|
+
setDropOpen(false);
|
|
225
|
+
setSearchValue("");
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
document.addEventListener("mousedown", handleClickOutside as any);
|
|
229
|
+
return () => {
|
|
230
|
+
document.removeEventListener("mousedown", handleClickOutside as any);
|
|
231
|
+
};
|
|
232
|
+
}, []);
|
|
233
|
+
|
|
234
|
+
// Filtering suggestions based on type and search value
|
|
235
|
+
const selected: any = isMultiple ? selectedItems : inputValue;
|
|
236
|
+
const filteredData: ValueProps[] = filterSuggestions(
|
|
237
|
+
suggestions,
|
|
238
|
+
searchValue,
|
|
239
|
+
type,
|
|
240
|
+
desc,
|
|
241
|
+
selected,
|
|
242
|
+
async
|
|
243
|
+
);
|
|
244
|
+
const isSelected = (
|
|
245
|
+
item: ValueProps,
|
|
246
|
+
selectedItems: ValueProps[] | string
|
|
247
|
+
): boolean => {
|
|
248
|
+
if (Array.isArray(selectedItems)) {
|
|
249
|
+
return selectedItems.some(
|
|
250
|
+
(selectedItem) => selectedItem[desc] === item[desc]
|
|
251
|
+
);
|
|
252
|
+
} else {
|
|
253
|
+
return item[desc] === selectedItems;
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const handleLoadMore = () => {
|
|
258
|
+
if (paginationEnabled) {
|
|
259
|
+
handlePickSuggestions(searchValue, nextPage + 1, true);
|
|
260
|
+
setNextPage(nextPage + 1);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const handleOnClick = () => {
|
|
265
|
+
!disabled && !readOnly ? setDropOpen(true) : "";
|
|
266
|
+
};
|
|
267
|
+
const onInputFocus = () => {
|
|
268
|
+
if (inputRef.current) inputRef.current.focus();
|
|
269
|
+
handleOnClick();
|
|
270
|
+
};
|
|
271
|
+
const tooltipContent =
|
|
272
|
+
selectedItems?.length > itemCount
|
|
273
|
+
? selectedItems
|
|
274
|
+
?.slice(1)
|
|
275
|
+
.map((item) => item[desc])
|
|
276
|
+
.join(", ")
|
|
277
|
+
: "";
|
|
278
|
+
|
|
279
|
+
return (
|
|
280
|
+
<div className={fullWidth ? "fullWidth" : "autoWidth"} ref={dropdownRef}>
|
|
281
|
+
{label && (
|
|
282
|
+
<div
|
|
283
|
+
style={{
|
|
284
|
+
marginBottom: 5,
|
|
285
|
+
display: "flex",
|
|
286
|
+
justifyContent: "space-between",
|
|
287
|
+
}}
|
|
288
|
+
>
|
|
289
|
+
<label className={`labels label-text`}>
|
|
290
|
+
{label}
|
|
291
|
+
{required && <span className="text-error"> *</span>}
|
|
292
|
+
</label>
|
|
293
|
+
<span onClick={() => handleAction?.()} className={`action_label`}>
|
|
294
|
+
{actionLabel}
|
|
295
|
+
</span>
|
|
296
|
+
</div>
|
|
297
|
+
)}
|
|
298
|
+
{/* Displaying selected items for multi-select */}
|
|
299
|
+
|
|
300
|
+
<div
|
|
301
|
+
className={`${
|
|
302
|
+
expandable ? "qbs-expandable-container" : "qbs-container"
|
|
303
|
+
}`}
|
|
304
|
+
style={{ position: "relative" }}
|
|
305
|
+
>
|
|
306
|
+
{selectedItems?.length > 0 && (
|
|
307
|
+
<>
|
|
308
|
+
{selectedItems
|
|
309
|
+
.slice(
|
|
310
|
+
0,
|
|
311
|
+
selectedItems?.length > itemCount
|
|
312
|
+
? itemCount
|
|
313
|
+
: selectedItems?.length
|
|
314
|
+
)
|
|
315
|
+
.map((item, index) => (
|
|
316
|
+
<>
|
|
317
|
+
<div className="selected-items-container">
|
|
318
|
+
<Tooltip
|
|
319
|
+
title={item?.[desc]}
|
|
320
|
+
enabled={item?.[desc]?.length > textCount}
|
|
321
|
+
>
|
|
322
|
+
<div key={item.id} className="selected-item">
|
|
323
|
+
{item?.[desc]?.length > textCount
|
|
324
|
+
? `${item?.[desc].substring(0, textCount)}...`
|
|
325
|
+
: item?.[desc]}
|
|
326
|
+
|
|
327
|
+
<button
|
|
328
|
+
onClick={() => handleRemoveSelectedItem(index)}
|
|
329
|
+
className="remove-item-btn"
|
|
330
|
+
aria-label={`Remove ${item?.[desc]}`}
|
|
331
|
+
>
|
|
332
|
+
X
|
|
333
|
+
</button>
|
|
334
|
+
</div>
|
|
335
|
+
</Tooltip>
|
|
336
|
+
</div>
|
|
337
|
+
</>
|
|
338
|
+
))}
|
|
339
|
+
{selectedItems?.length > itemCount && (
|
|
340
|
+
<div className="selected-items-container">
|
|
341
|
+
<Tooltip title={tooltipContent} enabled={true}>
|
|
342
|
+
<div className="selected-item-more">
|
|
343
|
+
+{selectedItems?.length - itemCount} more
|
|
344
|
+
</div>
|
|
345
|
+
</Tooltip>
|
|
346
|
+
</div>
|
|
347
|
+
)}
|
|
348
|
+
</>
|
|
349
|
+
)}
|
|
350
|
+
|
|
351
|
+
<div
|
|
352
|
+
className="qbs-textfield-expandable"
|
|
353
|
+
data-value={
|
|
354
|
+
selectedItems?.length > 0 || searchValue
|
|
355
|
+
? searchValue
|
|
356
|
+
: placeholder ?? ""
|
|
357
|
+
}
|
|
358
|
+
onClick={() => onInputFocus()}
|
|
359
|
+
>
|
|
360
|
+
<input
|
|
361
|
+
id={id}
|
|
362
|
+
ref={inputRef}
|
|
363
|
+
type="text"
|
|
364
|
+
value={
|
|
365
|
+
type === "auto_suggestion" && !expandable
|
|
366
|
+
? inputValue
|
|
367
|
+
: searchValue || inputValue
|
|
368
|
+
}
|
|
369
|
+
onChange={handleChange}
|
|
370
|
+
// onBlur={handleBlur}
|
|
371
|
+
onFocus={onFocus}
|
|
372
|
+
onClick={() => handleOnClick()}
|
|
373
|
+
className={generateClassName()}
|
|
374
|
+
placeholder={selectedItems?.length > 0 ? "" : placeholder ?? ""}
|
|
375
|
+
readOnly={
|
|
376
|
+
readOnly ||
|
|
377
|
+
type === "custom_select" ||
|
|
378
|
+
(type == "auto_suggestion" && !expandable)
|
|
379
|
+
}
|
|
380
|
+
disabled={disabled}
|
|
381
|
+
data-testid="custom-autocomplete"
|
|
382
|
+
/>
|
|
383
|
+
</div>
|
|
384
|
+
|
|
385
|
+
{/* Icons for Clearing Input or Toggling Dropdown */}
|
|
386
|
+
{!expandable && (
|
|
387
|
+
<div className="qbs-autocomplete-close-icon">
|
|
388
|
+
{(inputValue || searchValue) && !disabled && !readOnly && (
|
|
389
|
+
<button
|
|
390
|
+
onClick={handleClear}
|
|
391
|
+
className="icon-button"
|
|
392
|
+
aria-label="clear"
|
|
393
|
+
>
|
|
394
|
+
<Close />
|
|
395
|
+
</button>
|
|
396
|
+
)}
|
|
397
|
+
|
|
398
|
+
<button
|
|
399
|
+
disabled={disabled || readOnly}
|
|
400
|
+
type="button"
|
|
401
|
+
onClick={() => setDropOpen(!dropOpen)}
|
|
402
|
+
className="icon-button"
|
|
403
|
+
aria-label="toggle"
|
|
404
|
+
>
|
|
405
|
+
<DropArrow />
|
|
406
|
+
</button>
|
|
407
|
+
</div>
|
|
408
|
+
)}
|
|
409
|
+
|
|
410
|
+
{/* Displaying Loading Spinner */}
|
|
411
|
+
|
|
412
|
+
{/* Suggestions Dropdown */}
|
|
413
|
+
{dropOpen && (
|
|
414
|
+
<ul
|
|
415
|
+
ref={dropRef}
|
|
416
|
+
className={`qbs-autocomplete-suggestions ${dropdownPosition}`}
|
|
417
|
+
>
|
|
418
|
+
{type == "auto_suggestion" && !expandable && (
|
|
419
|
+
<div
|
|
420
|
+
style={{ position: "relative" }}
|
|
421
|
+
className="react-core-ts-search-container"
|
|
422
|
+
>
|
|
423
|
+
<span className="dropdown-search-icon">
|
|
424
|
+
<Search />
|
|
425
|
+
</span>
|
|
426
|
+
<input
|
|
427
|
+
className="dropdown-search-input"
|
|
428
|
+
onChange={handleSuggestionChange}
|
|
429
|
+
value={searchValue}
|
|
430
|
+
placeholder="Search"
|
|
431
|
+
/>
|
|
432
|
+
</div>
|
|
433
|
+
)}
|
|
434
|
+
|
|
435
|
+
<div className="qbs-autocomplete-suggestions-sub">
|
|
436
|
+
{/* Displaying Suggestions or Not Found Message */}
|
|
437
|
+
{/* {isLoading && (
|
|
438
|
+
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
|
439
|
+
<span>
|
|
440
|
+
<Spinner />
|
|
441
|
+
</span>
|
|
442
|
+
</div>
|
|
443
|
+
|
|
444
|
+
{/* )} */}
|
|
445
|
+
|
|
446
|
+
{filteredData?.length > 0 ? (
|
|
447
|
+
filteredData.map((suggestion: ValueProps, idx: number) => (
|
|
448
|
+
<div
|
|
449
|
+
key={idx.toString()}
|
|
450
|
+
className={`qbs-autocomplete-listitem-container ${
|
|
451
|
+
(isMultiple || singleSelect) &&
|
|
452
|
+
"qbs-autocomplete-checkbox-container"
|
|
453
|
+
} ${
|
|
454
|
+
isSelected(suggestion, selected) ? "is-selected" : ""
|
|
455
|
+
}`}
|
|
456
|
+
>
|
|
457
|
+
{(isMultiple || singleSelect) && (
|
|
458
|
+
<div className="qbs-autocomplete-checkbox">
|
|
459
|
+
<input
|
|
460
|
+
onChange={(e) => handleMultiSelect(e, suggestion)}
|
|
461
|
+
type="checkbox"
|
|
462
|
+
checked={isSelected(suggestion, selected)}
|
|
463
|
+
id={`qbs-checkbox-${idx.toString()}`}
|
|
464
|
+
/>
|
|
465
|
+
<label htmlFor={`qbs-checkbox-${idx.toString()}`}>
|
|
466
|
+
<svg
|
|
467
|
+
width="8"
|
|
468
|
+
height="6"
|
|
469
|
+
viewBox="0 0 8 6"
|
|
470
|
+
fill="none"
|
|
471
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
472
|
+
>
|
|
473
|
+
<path
|
|
474
|
+
d="M0 3.21739L2.89883 6L8 1.06994L6.89494 0L2.89883 3.86768L1.09728 2.14745L0 3.21739Z"
|
|
475
|
+
fill="white"
|
|
476
|
+
/>
|
|
477
|
+
</svg>
|
|
478
|
+
</label>
|
|
479
|
+
</div>
|
|
480
|
+
)}
|
|
481
|
+
<li
|
|
482
|
+
key={idx}
|
|
483
|
+
className={`qbs-autocomplete-suggestions-item ${
|
|
484
|
+
isSelected(suggestion, selected) ? "is-selected" : ""
|
|
485
|
+
}`}
|
|
486
|
+
onClick={() => handleSuggestionClick(suggestion)}
|
|
487
|
+
data-testid={suggestion[desc]}
|
|
488
|
+
>
|
|
489
|
+
{suggestion[desc]}
|
|
490
|
+
</li>
|
|
491
|
+
</div>
|
|
492
|
+
))
|
|
493
|
+
) : (
|
|
494
|
+
<>
|
|
495
|
+
{isLoading ? (
|
|
496
|
+
<div
|
|
497
|
+
style={{ display: "flex", justifyContent: "center" }}
|
|
498
|
+
>
|
|
499
|
+
<span>
|
|
500
|
+
<Spinner />
|
|
501
|
+
</span>
|
|
502
|
+
</div>
|
|
503
|
+
) : (
|
|
504
|
+
<li
|
|
505
|
+
className="qbs-autocomplete-notfound"
|
|
506
|
+
onClick={handleBlur}
|
|
507
|
+
>
|
|
508
|
+
{notDataMessage ?? "No Results Found"}
|
|
509
|
+
</li>
|
|
510
|
+
)}
|
|
511
|
+
</>
|
|
512
|
+
)}
|
|
513
|
+
{paginationEnabled &&
|
|
514
|
+
nextBlock !== 0 &&
|
|
515
|
+
nextBlock !== undefined &&
|
|
516
|
+
filteredData?.length > 0 && (
|
|
517
|
+
<div
|
|
518
|
+
className="loadMoreSection"
|
|
519
|
+
onClick={() => handleLoadMore()}
|
|
520
|
+
>
|
|
521
|
+
<p style={{ margin: 2 }}>Load More</p>
|
|
522
|
+
</div>
|
|
523
|
+
)}
|
|
524
|
+
</div>
|
|
525
|
+
</ul>
|
|
526
|
+
)}
|
|
527
|
+
</div>
|
|
528
|
+
|
|
529
|
+
{/* Displaying Validation Error */}
|
|
530
|
+
{errors && (
|
|
531
|
+
<div
|
|
532
|
+
className="text-error text-error-label mt-[1px]"
|
|
533
|
+
data-testid="autocomplete-error"
|
|
534
|
+
>
|
|
535
|
+
{errors.message}
|
|
536
|
+
</div>
|
|
537
|
+
)}
|
|
538
|
+
</div>
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
);
|
|
542
|
+
|
|
543
|
+
export default React.memo(ExpandableAutoComplete);
|
package/src/commontypes.ts
CHANGED
package/src/index.tsx
CHANGED
|
@@ -2,6 +2,8 @@ import './styles/global.css';
|
|
|
2
2
|
|
|
3
3
|
export { default as AutoComplete } from './AutoComplete';
|
|
4
4
|
export { default as CustomAutoComplete } from './AutoComplete';
|
|
5
|
+
export { default as ExpandableAutoComplete } from './ExpandableAutoComplete';
|
|
6
|
+
|
|
5
7
|
export { default as ToolTip } from './utilities/tooltip';
|
|
6
8
|
export { deepEqual } from './utilities/deepEqual';
|
|
7
9
|
export type { AutoSuggestionInputProps } from './commontypes';
|
|
@@ -181,6 +181,55 @@ body * {
|
|
|
181
181
|
color: #313131;
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
.qbs-expandable-container {
|
|
185
|
+
border: 1px solid #d6d8dc;
|
|
186
|
+
background: #fff;
|
|
187
|
+
box-sizing: border-box;
|
|
188
|
+
padding: 5px;
|
|
189
|
+
display: flex;
|
|
190
|
+
flex-wrap: wrap;
|
|
191
|
+
flex: 1 1 0%;
|
|
192
|
+
gap: 5px;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.qbs-textfield-default.expandable {
|
|
196
|
+
height: auto;
|
|
197
|
+
border: none;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.qbs-expandable-container .qbs-textfield-expandable {
|
|
201
|
+
flex: 1 1 auto;
|
|
202
|
+
display: inline-grid;
|
|
203
|
+
grid-template-columns: 0px min-content;
|
|
204
|
+
grid-area: 1 / 1 / 2 / 3;
|
|
205
|
+
overflow: hidden;
|
|
206
|
+
}
|
|
207
|
+
.qbs-expandable-container .qbs-textfield-expandable::after {
|
|
208
|
+
content: attr(data-value) " ";
|
|
209
|
+
visibility: hidden;
|
|
210
|
+
white-space: pre;
|
|
211
|
+
grid-area: 1 / 2;
|
|
212
|
+
font: inherit;
|
|
213
|
+
min-width: 2px;
|
|
214
|
+
border: 0px;
|
|
215
|
+
margin: 0px;
|
|
216
|
+
outline: 0px;
|
|
217
|
+
padding: 0px;
|
|
218
|
+
}
|
|
219
|
+
.qbs-expandable-container .qbs-textfield-expandable input {
|
|
220
|
+
opacity: 1;
|
|
221
|
+
width: 100%;
|
|
222
|
+
grid-area: 1 / 2;
|
|
223
|
+
min-width: 10px;
|
|
224
|
+
border: 0px;
|
|
225
|
+
margin: 0px;
|
|
226
|
+
outline: 0px;
|
|
227
|
+
padding: 0px;
|
|
228
|
+
height: auto;
|
|
229
|
+
min-height: auto;
|
|
230
|
+
border-radius: 0;
|
|
231
|
+
}
|
|
232
|
+
|
|
184
233
|
.qbs-textfield-default::placeholder {
|
|
185
234
|
font-size: 14px;
|
|
186
235
|
font-style: normal;
|
|
@@ -290,6 +339,12 @@ ul {
|
|
|
290
339
|
flex-wrap: wrap;
|
|
291
340
|
}
|
|
292
341
|
|
|
342
|
+
.qbs-expandable-container .selected-items-container {
|
|
343
|
+
position: relative;
|
|
344
|
+
top: 0;
|
|
345
|
+
left: 0;
|
|
346
|
+
}
|
|
347
|
+
|
|
293
348
|
.selected-item-more {
|
|
294
349
|
display: flex;
|
|
295
350
|
align-items: center;
|
|
@@ -467,7 +522,7 @@ span.dropdown-search-icon {
|
|
|
467
522
|
cursor: pointer;
|
|
468
523
|
}
|
|
469
524
|
|
|
470
|
-
.qbs-table-tooltip .tooltiptext {
|
|
525
|
+
.qbs-table-tooltip .tooltiptext, .tooltiptext {
|
|
471
526
|
visibility: hidden;
|
|
472
527
|
background-color: #000;
|
|
473
528
|
color: #fff;
|
|
@@ -501,21 +556,21 @@ span.dropdown-search-icon {
|
|
|
501
556
|
opacity: 1;
|
|
502
557
|
}
|
|
503
558
|
|
|
504
|
-
.qbs-table-tooltip .tooltiptext::after {
|
|
505
|
-
content:
|
|
559
|
+
.qbs-table-tooltip .tooltiptext::after, .tooltiptext::after {
|
|
560
|
+
content: "";
|
|
506
561
|
position: absolute;
|
|
507
562
|
border-width: 5px;
|
|
508
563
|
border-style: solid;
|
|
509
564
|
}
|
|
510
565
|
|
|
511
|
-
.qbs-table-tooltip.up .tooltiptext::after {
|
|
566
|
+
.qbs-table-tooltip.up .tooltiptext::after, .up.tooltiptext::after {
|
|
512
567
|
top: 100%;
|
|
513
568
|
left: 50%;
|
|
514
569
|
margin-left: -5px;
|
|
515
570
|
border-color: #000 transparent transparent transparent;
|
|
516
571
|
}
|
|
517
572
|
|
|
518
|
-
.qbs-table-tooltip.down .tooltiptext::after {
|
|
573
|
+
.qbs-table-tooltip.down .tooltiptext::after, .down.tooltiptext::after {
|
|
519
574
|
bottom: 100%;
|
|
520
575
|
left: 50%;
|
|
521
576
|
margin-left: -5px;
|