infinity-ui-elements 1.8.27 → 1.8.28
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/components/SearchableDropdown/SearchableDropdown.d.ts +14 -0
- package/dist/components/SearchableDropdown/SearchableDropdown.d.ts.map +1 -1
- package/dist/components/SearchableDropdown/SearchableDropdown.stories.d.ts +5 -1
- package/dist/components/SearchableDropdown/SearchableDropdown.stories.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.esm.js +61 -2
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +61 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -3201,8 +3201,21 @@ const defaultFilter = (item, query) => {
|
|
|
3201
3201
|
return (item.label?.toLowerCase()?.includes(searchQuery) ||
|
|
3202
3202
|
(item.description?.toLowerCase()?.includes(searchQuery) ?? false));
|
|
3203
3203
|
};
|
|
3204
|
-
const SearchableDropdown = React.forwardRef(({ className, items = [], sectionHeading, isLoading = false, emptyTitle = "No Search Results Found", emptyDescription = "Add description of what the user can search for here.", emptyLinkText = "Link to support site", onEmptyLinkClick, primaryButtonText, secondaryButtonText, onPrimaryClick, onSecondaryClick, dropdownWidth = "full", showChevron = false, emptyIcon, disableFooter = false, footerLayout = "horizontal", onSearchChange, onItemSelect, filterFunction = defaultFilter, searchValue: controlledSearchValue, defaultSearchValue = "", dropdownClassName, minSearchLength = 0, showOnFocus = true, showAddNew = false, showAddNewIfDoesNotMatch = true, onAddNew, containerClassName, ...textFieldProps }, ref) => {
|
|
3205
|
-
|
|
3204
|
+
const SearchableDropdown = React.forwardRef(({ className, items = [], sectionHeading, isLoading = false, emptyTitle = "No Search Results Found", emptyDescription = "Add description of what the user can search for here.", emptyLinkText = "Link to support site", onEmptyLinkClick, primaryButtonText, secondaryButtonText, onPrimaryClick, onSecondaryClick, dropdownWidth = "full", showChevron = false, emptyIcon, disableFooter = false, footerLayout = "horizontal", onSearchChange, onItemSelect, filterFunction = defaultFilter, searchValue: controlledSearchValue, defaultSearchValue = "", dropdownClassName, minSearchLength = 0, showOnFocus = true, showAddNew = false, showAddNewIfDoesNotMatch = true, onAddNew, containerClassName, value: controlledValue, defaultValue, onChange, ...textFieldProps }, ref) => {
|
|
3205
|
+
// Find the selected item based on value/defaultValue
|
|
3206
|
+
const findSelectedItem = React.useCallback((val) => {
|
|
3207
|
+
if (val === undefined || val === "")
|
|
3208
|
+
return undefined;
|
|
3209
|
+
return items.find((item) => item.value === val);
|
|
3210
|
+
}, [items]);
|
|
3211
|
+
// Initialize uncontrolled value state
|
|
3212
|
+
const initialValue = controlledValue !== undefined ? controlledValue : defaultValue;
|
|
3213
|
+
const initialSelectedItem = findSelectedItem(initialValue);
|
|
3214
|
+
const initialSearchValue = initialSelectedItem
|
|
3215
|
+
? initialSelectedItem.label
|
|
3216
|
+
: defaultSearchValue;
|
|
3217
|
+
const [uncontrolledSearchValue, setUncontrolledSearchValue] = React.useState(initialSearchValue);
|
|
3218
|
+
const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue);
|
|
3206
3219
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
3207
3220
|
const [focusedIndex, setFocusedIndex] = React.useState(-1);
|
|
3208
3221
|
const [isInsideModal, setIsInsideModal] = React.useState(false);
|
|
@@ -3215,6 +3228,33 @@ const SearchableDropdown = React.forwardRef(({ className, items = [], sectionHea
|
|
|
3215
3228
|
width: 0,
|
|
3216
3229
|
});
|
|
3217
3230
|
React.useImperativeHandle(ref, () => inputRef.current);
|
|
3231
|
+
// Determine current value (controlled or uncontrolled)
|
|
3232
|
+
const value = controlledValue !== undefined ? controlledValue : uncontrolledValue;
|
|
3233
|
+
// Sync search value when value prop changes
|
|
3234
|
+
React.useEffect(() => {
|
|
3235
|
+
const selectedItem = findSelectedItem(value);
|
|
3236
|
+
if (selectedItem) {
|
|
3237
|
+
const newSearchValue = selectedItem.label;
|
|
3238
|
+
if (controlledSearchValue === undefined) {
|
|
3239
|
+
setUncontrolledSearchValue(newSearchValue);
|
|
3240
|
+
}
|
|
3241
|
+
// If controlled, we still need to call onSearchChange to notify parent
|
|
3242
|
+
// but only if the search value is different
|
|
3243
|
+
if (controlledSearchValue !== undefined &&
|
|
3244
|
+
controlledSearchValue !== newSearchValue) {
|
|
3245
|
+
onSearchChange?.(newSearchValue);
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
else if (value === undefined || value === "") {
|
|
3249
|
+
// If value is cleared, clear search value too
|
|
3250
|
+
if (controlledSearchValue === undefined) {
|
|
3251
|
+
setUncontrolledSearchValue("");
|
|
3252
|
+
}
|
|
3253
|
+
else {
|
|
3254
|
+
onSearchChange?.("");
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
}, [value, findSelectedItem, controlledSearchValue, onSearchChange]);
|
|
3218
3258
|
// Check if dropdown is inside a modal
|
|
3219
3259
|
React.useEffect(() => {
|
|
3220
3260
|
if (isOpen && dropdownRef.current) {
|
|
@@ -3256,6 +3296,15 @@ const SearchableDropdown = React.forwardRef(({ className, items = [], sectionHea
|
|
|
3256
3296
|
setUncontrolledSearchValue(newValue);
|
|
3257
3297
|
}
|
|
3258
3298
|
onSearchChange?.(newValue);
|
|
3299
|
+
// If user is typing and the search value no longer matches the selected item's label,
|
|
3300
|
+
// clear the value to indicate no item is selected
|
|
3301
|
+
const selectedItem = findSelectedItem(value);
|
|
3302
|
+
if (selectedItem && selectedItem.label !== newValue) {
|
|
3303
|
+
if (controlledValue === undefined) {
|
|
3304
|
+
setUncontrolledValue(undefined);
|
|
3305
|
+
}
|
|
3306
|
+
onChange?.(undefined, undefined);
|
|
3307
|
+
}
|
|
3259
3308
|
// Show dropdown if minimum search length is met
|
|
3260
3309
|
if (newValue.length >= minSearchLength) {
|
|
3261
3310
|
setIsOpen(true);
|
|
@@ -3271,9 +3320,19 @@ const SearchableDropdown = React.forwardRef(({ className, items = [], sectionHea
|
|
|
3271
3320
|
};
|
|
3272
3321
|
const handleItemSelect = (item) => {
|
|
3273
3322
|
onItemSelect?.(item);
|
|
3323
|
+
// Update search value
|
|
3274
3324
|
if (controlledSearchValue === undefined) {
|
|
3275
3325
|
setUncontrolledSearchValue(item.label);
|
|
3276
3326
|
}
|
|
3327
|
+
else {
|
|
3328
|
+
onSearchChange?.(item.label);
|
|
3329
|
+
}
|
|
3330
|
+
// Update value (controlled or uncontrolled)
|
|
3331
|
+
if (controlledValue === undefined) {
|
|
3332
|
+
setUncontrolledValue(item.value);
|
|
3333
|
+
}
|
|
3334
|
+
// Call onChange callback
|
|
3335
|
+
onChange?.(item.value, item);
|
|
3277
3336
|
setIsOpen(false);
|
|
3278
3337
|
inputRef.current?.focus();
|
|
3279
3338
|
};
|