infinity-ui-elements 1.8.26 → 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/components/Table/Table.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.esm.js +63 -2
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +63 -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
|
};
|
|
@@ -3970,6 +4029,7 @@ function TableComponent({ className, wrapperClassName, containerClassName, varia
|
|
|
3970
4029
|
maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight,
|
|
3971
4030
|
};
|
|
3972
4031
|
}, [maxHeight]);
|
|
4032
|
+
// Resolve loading state: prefer 'loading' prop, fallback to 'isLoading' for backward compatibility
|
|
3973
4033
|
const resolvedLoading = typeof loading === "boolean" ? loading : Boolean(isLoading);
|
|
3974
4034
|
const skeletonRowCount = loadingSkeletonRows ?? 5;
|
|
3975
4035
|
const sizeKey = size || "medium";
|
|
@@ -4020,6 +4080,7 @@ function TableComponent({ className, wrapperClassName, containerClassName, varia
|
|
|
4020
4080
|
onRowClick(row);
|
|
4021
4081
|
}
|
|
4022
4082
|
}, [onRowClick]);
|
|
4083
|
+
// ==================== Render Helpers ====================
|
|
4023
4084
|
const renderEmptyState = () => {
|
|
4024
4085
|
if (emptyComponent)
|
|
4025
4086
|
return emptyComponent;
|