infinity-ui-elements 1.8.27 → 1.8.29

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/index.js CHANGED
@@ -2618,7 +2618,7 @@ const selectVariants = classVarianceAuthority.cva("relative flex items-center ga
2618
2618
  isDisabled: false,
2619
2619
  },
2620
2620
  });
2621
- const Select = React__namespace.forwardRef(({ className, options = [], value: controlledValue, defaultValue, onChange, placeholder = "Select an option", label, helperText, errorText, successText, validationState = "none", isDisabled = false, isRequired = false, isOptional = false, isLoading = false, size = "medium", prefix, suffix, showClearButton = false, onClear, containerClassName, labelClassName, triggerClassName, menuClassName, menuWidth = "full", sectionHeading, emptyTitle = "No options available", emptyDescription = "There are no options to select from.", emptyIcon, infoHeading, infoDescription, LinkComponent, linkText, linkHref, onLinkClick, ...props }, ref) => {
2621
+ const Select = React__namespace.forwardRef(({ className, options = [], value: controlledValue, defaultValue, onChange, placeholder = "Select an option", label, helperText, errorText, successText, validationState = "none", isDisabled = false, isRequired = false, isOptional = false, isLoading = false, size = "medium", prefix, suffix, showClearButton = false, onClear, showLeadingIcon = false, containerClassName, labelClassName, triggerClassName, menuClassName, menuWidth = "full", sectionHeading, emptyTitle = "No options available", emptyDescription = "There are no options to select from.", emptyIcon, infoHeading, infoDescription, LinkComponent, linkText, linkHref, onLinkClick, ...props }, ref) => {
2622
2622
  const [uncontrolledValue, setUncontrolledValue] = React__namespace.useState(defaultValue);
2623
2623
  const [isOpen, setIsOpen] = React__namespace.useState(false);
2624
2624
  const [dropdownPlacement, setDropdownPlacement] = React__namespace.useState("bottom");
@@ -2797,13 +2797,19 @@ const Select = React__namespace.forwardRef(({ className, options = [], value: co
2797
2797
  size,
2798
2798
  validationState: currentValidationState,
2799
2799
  isDisabled,
2800
- }), "relative w-full cursor-pointer", className), onClick: !isDisabled && !isLoading ? toggleOpen : undefined, role: "combobox", "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-disabled": isDisabled, ...props, children: [prefix && (jsxRuntime.jsx("span", { className: cn("shrink-0 flex items-center", isDisabled
2800
+ }), "relative w-full cursor-pointer", className), onClick: !isDisabled && !isLoading ? toggleOpen : undefined, role: "combobox", "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-disabled": isDisabled, ...props, children: [prefix ? (jsxRuntime.jsx("span", { className: cn("shrink-0 flex items-center", isDisabled
2801
2801
  ? "text-surface-ink-neutral-disabled"
2802
2802
  : currentValidationState === "positive"
2803
2803
  ? "text-feedback-ink-positive-intense"
2804
2804
  : currentValidationState === "negative"
2805
2805
  ? "text-feedback-ink-negative-subtle"
2806
- : "text-surface-ink-neutral-muted"), children: prefix })), jsxRuntime.jsx("span", { className: cn("flex-1 text-left truncate", !selectedOption && "text-surface-ink-neutral-muted", isDisabled && "text-surface-ink-neutral-disabled"), children: isLoading ? "Loading..." : selectedOption?.label || placeholder }), showClearButton && hasValue && !isDisabled && !isLoading && (jsxRuntime.jsx("button", { type: "button", onClick: handleClear, className: "shrink-0 flex items-center justify-center text-surface-ink-neutral-muted hover:text-surface-ink-neutral-normal transition-colors", tabIndex: -1, children: jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsxRuntime.jsx("path", { d: "M12 4L4 12M4 4L12 12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) })), suffix && !showClearButton && (jsxRuntime.jsx("span", { className: cn("shrink-0 flex items-center", isDisabled
2806
+ : "text-surface-ink-neutral-muted"), children: prefix })) : showLeadingIcon && selectedOption?.leadingIcon ? (jsxRuntime.jsx("span", { className: cn("shrink-0 flex items-center", isDisabled
2807
+ ? "text-surface-ink-neutral-disabled"
2808
+ : currentValidationState === "positive"
2809
+ ? "text-feedback-ink-positive-intense"
2810
+ : currentValidationState === "negative"
2811
+ ? "text-feedback-ink-negative-subtle"
2812
+ : "text-surface-ink-neutral-muted"), children: selectedOption.leadingIcon })) : null, jsxRuntime.jsx("span", { className: cn("flex-1 text-left truncate", !selectedOption && "text-surface-ink-neutral-muted", isDisabled && "text-surface-ink-neutral-disabled"), children: isLoading ? "Loading..." : selectedOption?.label || placeholder }), showClearButton && hasValue && !isDisabled && !isLoading && (jsxRuntime.jsx("button", { type: "button", onClick: handleClear, className: "shrink-0 flex items-center justify-center text-surface-ink-neutral-muted hover:text-surface-ink-neutral-normal transition-colors", tabIndex: -1, children: jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsxRuntime.jsx("path", { d: "M12 4L4 12M4 4L12 12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) })), suffix && !showClearButton && (jsxRuntime.jsx("span", { className: cn("shrink-0 flex items-center", isDisabled
2807
2813
  ? "text-surface-ink-neutral-disabled"
2808
2814
  : currentValidationState === "positive"
2809
2815
  ? "text-feedback-ink-positive-intense"
@@ -3222,8 +3228,21 @@ const defaultFilter = (item, query) => {
3222
3228
  return (item.label?.toLowerCase()?.includes(searchQuery) ||
3223
3229
  (item.description?.toLowerCase()?.includes(searchQuery) ?? false));
3224
3230
  };
3225
- const SearchableDropdown = React__namespace.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) => {
3226
- const [uncontrolledSearchValue, setUncontrolledSearchValue] = React__namespace.useState(defaultSearchValue);
3231
+ const SearchableDropdown = React__namespace.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) => {
3232
+ // Find the selected item based on value/defaultValue
3233
+ const findSelectedItem = React__namespace.useCallback((val) => {
3234
+ if (val === undefined || val === "")
3235
+ return undefined;
3236
+ return items.find((item) => item.value === val);
3237
+ }, [items]);
3238
+ // Initialize uncontrolled value state
3239
+ const initialValue = controlledValue !== undefined ? controlledValue : defaultValue;
3240
+ const initialSelectedItem = findSelectedItem(initialValue);
3241
+ const initialSearchValue = initialSelectedItem
3242
+ ? initialSelectedItem.label
3243
+ : defaultSearchValue;
3244
+ const [uncontrolledSearchValue, setUncontrolledSearchValue] = React__namespace.useState(initialSearchValue);
3245
+ const [uncontrolledValue, setUncontrolledValue] = React__namespace.useState(defaultValue);
3227
3246
  const [isOpen, setIsOpen] = React__namespace.useState(false);
3228
3247
  const [focusedIndex, setFocusedIndex] = React__namespace.useState(-1);
3229
3248
  const [isInsideModal, setIsInsideModal] = React__namespace.useState(false);
@@ -3236,6 +3255,33 @@ const SearchableDropdown = React__namespace.forwardRef(({ className, items = [],
3236
3255
  width: 0,
3237
3256
  });
3238
3257
  React__namespace.useImperativeHandle(ref, () => inputRef.current);
3258
+ // Determine current value (controlled or uncontrolled)
3259
+ const value = controlledValue !== undefined ? controlledValue : uncontrolledValue;
3260
+ // Sync search value when value prop changes
3261
+ React__namespace.useEffect(() => {
3262
+ const selectedItem = findSelectedItem(value);
3263
+ if (selectedItem) {
3264
+ const newSearchValue = selectedItem.label;
3265
+ if (controlledSearchValue === undefined) {
3266
+ setUncontrolledSearchValue(newSearchValue);
3267
+ }
3268
+ // If controlled, we still need to call onSearchChange to notify parent
3269
+ // but only if the search value is different
3270
+ if (controlledSearchValue !== undefined &&
3271
+ controlledSearchValue !== newSearchValue) {
3272
+ onSearchChange?.(newSearchValue);
3273
+ }
3274
+ }
3275
+ else if (value === undefined || value === "") {
3276
+ // If value is cleared, clear search value too
3277
+ if (controlledSearchValue === undefined) {
3278
+ setUncontrolledSearchValue("");
3279
+ }
3280
+ else {
3281
+ onSearchChange?.("");
3282
+ }
3283
+ }
3284
+ }, [value, findSelectedItem, controlledSearchValue, onSearchChange]);
3239
3285
  // Check if dropdown is inside a modal
3240
3286
  React__namespace.useEffect(() => {
3241
3287
  if (isOpen && dropdownRef.current) {
@@ -3277,6 +3323,15 @@ const SearchableDropdown = React__namespace.forwardRef(({ className, items = [],
3277
3323
  setUncontrolledSearchValue(newValue);
3278
3324
  }
3279
3325
  onSearchChange?.(newValue);
3326
+ // If user is typing and the search value no longer matches the selected item's label,
3327
+ // clear the value to indicate no item is selected
3328
+ const selectedItem = findSelectedItem(value);
3329
+ if (selectedItem && selectedItem.label !== newValue) {
3330
+ if (controlledValue === undefined) {
3331
+ setUncontrolledValue(undefined);
3332
+ }
3333
+ onChange?.(undefined, undefined);
3334
+ }
3280
3335
  // Show dropdown if minimum search length is met
3281
3336
  if (newValue.length >= minSearchLength) {
3282
3337
  setIsOpen(true);
@@ -3292,9 +3347,19 @@ const SearchableDropdown = React__namespace.forwardRef(({ className, items = [],
3292
3347
  };
3293
3348
  const handleItemSelect = (item) => {
3294
3349
  onItemSelect?.(item);
3350
+ // Update search value
3295
3351
  if (controlledSearchValue === undefined) {
3296
3352
  setUncontrolledSearchValue(item.label);
3297
3353
  }
3354
+ else {
3355
+ onSearchChange?.(item.label);
3356
+ }
3357
+ // Update value (controlled or uncontrolled)
3358
+ if (controlledValue === undefined) {
3359
+ setUncontrolledValue(item.value);
3360
+ }
3361
+ // Call onChange callback
3362
+ onChange?.(item.value, item);
3298
3363
  setIsOpen(false);
3299
3364
  inputRef.current?.focus();
3300
3365
  };