infinity-ui-elements 1.8.2 → 1.8.4

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
@@ -1889,10 +1889,10 @@ const DropdownMenu = React__namespace.forwardRef(({ items = [], customContent, s
1889
1889
  if (isEmpty || items.length === 0) {
1890
1890
  return (jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-8 px-6 text-center", children: [emptyIcon || (jsxRuntime.jsx(lucideReact.Search, { className: "w-12 h-12 text-surface-ink-neutral-muted mb-4" })), jsxRuntime.jsx(Text, { as: "h3", variant: "body", size: "small", weight: "semibold", className: "text-surface-ink-neutral-normal mb-2", children: emptyTitle }), jsxRuntime.jsx(Text, { as: "p", variant: "body", size: "small", weight: "regular", className: "text-surface-ink-neutral-muted mb-3", children: emptyDescription }), emptyLinkText && (jsxRuntime.jsx(Link, { type: "anchor", color: "primary", size: "small", onClick: onEmptyLinkClick, children: emptyLinkText }))] }));
1891
1891
  }
1892
- return (jsxRuntime.jsxs("div", { className: "py-3 px-3 max-h-[400px] overflow-y-auto", children: [sectionHeading && (jsxRuntime.jsx(Text, { as: "div", variant: "body", size: "small", weight: "medium", className: "text-surface-ink-neutral-muted px-3 py-2 mb-1", children: sectionHeading })), jsxRuntime.jsx("div", { className: "flex flex-col gap-1", children: items.map((item, index) => (jsxRuntime.jsx(ListItem, { title: item.title, description: item.description, leadingIcon: item.leadingIcon, trailingIcon: item.trailingIcon, showChevron: showChevron, isDisabled: item.isDisabled, isSelected: index === focusedIndex, onClick: () => {
1892
+ return (jsxRuntime.jsxs("div", { className: "py-3 px-3 max-h-[400px] overflow-y-auto", children: [sectionHeading && (jsxRuntime.jsx(Text, { as: "div", variant: "body", size: "small", weight: "medium", className: "text-surface-ink-neutral-muted px-3 py-2 mb-1", children: sectionHeading })), jsxRuntime.jsx("div", { className: "flex flex-col gap-1", children: items.map((item, index) => (jsxRuntime.jsx(ListItem, { title: item.label, description: item.description, leadingIcon: item.leadingIcon, trailingIcon: item.trailingIcon, showChevron: showChevron, isDisabled: item.isDisabled, isSelected: index === focusedIndex, variant: item.variant, onClick: () => {
1893
1893
  item.onClick?.();
1894
1894
  onClose?.();
1895
- }, containerClassName: cn(index === focusedIndex && "bg-action-fill-primary-faded") }, item.id))) })] }));
1895
+ }, containerClassName: cn(index === focusedIndex && "bg-action-fill-primary-faded") }, item.value))) })] }));
1896
1896
  };
1897
1897
  const widthClass = width === "full" ? "w-full" : width === "auto" ? "w-auto" : "";
1898
1898
  const footerVisible = showFooter ?? !disableFooter;
@@ -2234,7 +2234,7 @@ const Modal = React__namespace.forwardRef(({ isOpen, onClose, title, description
2234
2234
  };
2235
2235
  // Handle escape key
2236
2236
  React__namespace.useEffect(() => {
2237
- if (!isOpen || !closeOnEscape)
2237
+ if (!isOpen || !closeOnEscape || !onClose)
2238
2238
  return;
2239
2239
  const handleEscape = (e) => {
2240
2240
  if (e.key === "Escape") {
@@ -2258,7 +2258,7 @@ const Modal = React__namespace.forwardRef(({ isOpen, onClose, title, description
2258
2258
  }, [isOpen]);
2259
2259
  // Handle overlay click
2260
2260
  const handleOverlayClick = (e) => {
2261
- if (closeOnOverlayClick && e.target === e.currentTarget) {
2261
+ if (closeOnOverlayClick && e.target === e.currentTarget && onClose) {
2262
2262
  onClose();
2263
2263
  }
2264
2264
  };
@@ -2266,7 +2266,7 @@ const Modal = React__namespace.forwardRef(({ isOpen, onClose, title, description
2266
2266
  if (!isOpen)
2267
2267
  return null;
2268
2268
  const hasHeader = title || description;
2269
- return (jsxRuntime.jsxs("div", { className: cn("fixed inset-0 z-50 flex items-center justify-center p-4", className), role: "dialog", "aria-modal": "true", "aria-label": ariaLabel || title, "aria-describedby": ariaDescribedBy, children: [jsxRuntime.jsx("div", { className: cn("absolute inset-0 bg-black/50 backdrop-blur-sm transition-opacity", overlayClassName), onClick: handleOverlayClick, "aria-hidden": "true" }), jsxRuntime.jsxs("div", { ref: contentRef, className: cn("relative w-full bg-white rounded-large shadow-xl transition-all", "flex flex-col max-h-[90vh]", sizeConfig[size], contentClassName), children: [hasHeader && (jsxRuntime.jsxs("div", { className: cn("flex items-start justify-between gap-4 px-6 pt-6", !description && "pb-4", description && "pb-2", headerClassName), children: [jsxRuntime.jsxs("div", { className: "flex-1", children: [title && (jsxRuntime.jsx(Text, { as: "h2", variant: "body", size: "large", weight: "semibold", color: "default", children: title })), description && (jsxRuntime.jsx(Text, { as: "p", variant: "body", size: "small", weight: "regular", color: "subtle", className: "mt-1", children: description }))] }), showCloseButton && (jsxRuntime.jsx("button", { type: "button", onClick: onClose, className: cn("shrink-0 rounded-medium p-1.5 transition-colors", "text-surface-ink-neutral-muted hover:text-surface-ink-neutral-default", "hover:bg-surface-fill-neutral-faded focus:outline-none focus:ring-2", "focus:ring-action-outline-primary-default focus:ring-offset-2"), "aria-label": "Close modal", children: jsxRuntime.jsx(lucideReact.X, { className: "h-5 w-5" }) }))] })), !hasHeader && showCloseButton && (jsxRuntime.jsx("div", { className: "absolute top-4 right-4 z-10", children: jsxRuntime.jsx("button", { type: "button", onClick: onClose, className: cn("shrink-0 rounded-medium p-1.5 transition-colors", "text-surface-ink-neutral-muted hover:text-surface-ink-neutral-default", "hover:bg-surface-fill-neutral-faded focus:outline-none focus:ring-2", "focus:ring-action-outline-primary-default focus:ring-offset-2"), "aria-label": "Close modal", children: jsxRuntime.jsx(lucideReact.X, { className: "h-5 w-5" }) }) })), jsxRuntime.jsx("div", { className: cn("flex-1 overflow-y-auto px-6", hasHeader ? "py-4" : "pt-6 pb-4", !footer && "pb-6", bodyClassName), children: children }), footer && (jsxRuntime.jsxs("div", { className: "flex flex-col", children: [jsxRuntime.jsx(Divider, { thickness: "thin", variant: "muted" }), jsxRuntime.jsx("div", { className: cn("flex items-center justify-end gap-3 px-6 py-4", footerClassName), children: footer })] }))] })] }));
2269
+ return (jsxRuntime.jsxs("div", { className: cn("fixed inset-0 z-50 flex items-center justify-center p-4", className), role: "dialog", "aria-modal": "true", "aria-label": ariaLabel || title, "aria-describedby": ariaDescribedBy, children: [jsxRuntime.jsx("div", { className: cn("absolute inset-0 bg-black/50 backdrop-blur-sm transition-opacity", overlayClassName), onClick: handleOverlayClick, "aria-hidden": "true" }), jsxRuntime.jsxs("div", { ref: contentRef, className: cn("relative w-full bg-white rounded-large shadow-xl transition-all", "flex flex-col max-h-[90vh]", sizeConfig[size], contentClassName), children: [hasHeader && (jsxRuntime.jsxs("div", { className: cn("flex items-start justify-between gap-4 px-6 pt-6", !description && "pb-4", description && "pb-2", headerClassName), children: [jsxRuntime.jsxs("div", { className: "flex-1", children: [title && (jsxRuntime.jsx(Text, { as: "h2", variant: "body", size: "large", weight: "semibold", color: "default", children: title })), description && (jsxRuntime.jsx(Text, { as: "p", variant: "body", size: "small", weight: "regular", color: "subtle", className: "mt-1", children: description }))] }), showCloseButton && onClose && (jsxRuntime.jsx(IconButton, { icon: "close", onClick: onClose, color: "neutral", size: "small", "aria-label": "Close modal", className: "shrink-0" }))] })), !hasHeader && showCloseButton && onClose && (jsxRuntime.jsx("div", { className: "absolute top-4 right-4 z-10", children: jsxRuntime.jsx(IconButton, { icon: "close", onClick: onClose, color: "neutral", size: "small", "aria-label": "Close modal" }) })), jsxRuntime.jsx("div", { className: cn("flex-1 overflow-y-auto px-6", hasHeader ? "py-4" : "pt-6 pb-4", !footer && "pb-6", bodyClassName), children: children }), footer && (jsxRuntime.jsxs("div", { className: "flex flex-col", children: [jsxRuntime.jsx(Divider, { thickness: "thin", variant: "muted" }), jsxRuntime.jsx("div", { className: cn("flex items-center justify-end gap-3 px-6 py-4", footerClassName), children: footer })] }))] })] }));
2270
2270
  });
2271
2271
  Modal.displayName = "Modal";
2272
2272
 
@@ -2906,10 +2906,10 @@ TextField.displayName = "TextField";
2906
2906
 
2907
2907
  const defaultFilter = (item, query) => {
2908
2908
  const searchQuery = query.toLowerCase();
2909
- return (item.title.toLowerCase().includes(searchQuery) ||
2909
+ return (item.label.toLowerCase().includes(searchQuery) ||
2910
2910
  (item.description?.toLowerCase().includes(searchQuery) ?? false));
2911
2911
  };
2912
- 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 = "Primary", secondaryButtonText = "Secondary", onPrimaryClick, onSecondaryClick, dropdownWidth = "full", showChevron = false, emptyIcon, disableFooter = false, footerLayout = "horizontal", onSearchChange, onItemSelect, filterFunction = defaultFilter, searchValue: controlledSearchValue, defaultSearchValue = "", dropdownClassName, minSearchLength = 0, showOnFocus = true, containerClassName, ...textFieldProps }, ref) => {
2912
+ 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, containerClassName, ...textFieldProps }, ref) => {
2913
2913
  const [uncontrolledSearchValue, setUncontrolledSearchValue] = React__namespace.useState(defaultSearchValue);
2914
2914
  const [isOpen, setIsOpen] = React__namespace.useState(false);
2915
2915
  const [focusedIndex, setFocusedIndex] = React__namespace.useState(-1);
@@ -2958,7 +2958,7 @@ const SearchableDropdown = React__namespace.forwardRef(({ className, items = [],
2958
2958
  const handleItemSelect = (item) => {
2959
2959
  onItemSelect?.(item);
2960
2960
  if (controlledSearchValue === undefined) {
2961
- setUncontrolledSearchValue(item.title);
2961
+ setUncontrolledSearchValue(item.label);
2962
2962
  }
2963
2963
  setIsOpen(false);
2964
2964
  inputRef.current?.focus();
@@ -2969,6 +2969,40 @@ const SearchableDropdown = React__namespace.forwardRef(({ className, items = [],
2969
2969
  return items;
2970
2970
  return items.filter((item) => filterFunction(item, searchValue));
2971
2971
  }, [items, searchValue, filterFunction]);
2972
+ // Add "Add New" option if showAddNew is true and no items match
2973
+ const itemsWithAddNew = React__namespace.useMemo(() => {
2974
+ if (!showAddNew || !searchValue || filteredItems.length > 0) {
2975
+ return filteredItems;
2976
+ }
2977
+ const addNewItem = {
2978
+ value: searchValue,
2979
+ label: `+ Add ${searchValue}`,
2980
+ variant: "primary",
2981
+ onClick: () => {
2982
+ const newItem = {
2983
+ value: searchValue,
2984
+ label: searchValue,
2985
+ };
2986
+ onItemSelect?.(newItem);
2987
+ if (controlledSearchValue === undefined) {
2988
+ setUncontrolledSearchValue(searchValue);
2989
+ }
2990
+ setIsOpen(false);
2991
+ inputRef.current?.focus();
2992
+ },
2993
+ };
2994
+ return [addNewItem];
2995
+ }, [
2996
+ showAddNew,
2997
+ searchValue,
2998
+ filteredItems,
2999
+ onItemSelect,
3000
+ controlledSearchValue,
3001
+ ]);
3002
+ // Reset focused index when items change
3003
+ React__namespace.useEffect(() => {
3004
+ setFocusedIndex(-1);
3005
+ }, [itemsWithAddNew.length]);
2972
3006
  // Close dropdown when clicking outside
2973
3007
  React__namespace.useEffect(() => {
2974
3008
  const handleClickOutside = (event) => {
@@ -2998,7 +3032,7 @@ const SearchableDropdown = React__namespace.forwardRef(({ className, items = [],
2998
3032
  switch (e.key) {
2999
3033
  case "ArrowDown":
3000
3034
  e.preventDefault();
3001
- setFocusedIndex((prev) => prev < filteredItems.length - 1 ? prev + 1 : prev);
3035
+ setFocusedIndex((prev) => prev < itemsWithAddNew.length - 1 ? prev + 1 : prev);
3002
3036
  break;
3003
3037
  case "ArrowUp":
3004
3038
  e.preventDefault();
@@ -3006,8 +3040,8 @@ const SearchableDropdown = React__namespace.forwardRef(({ className, items = [],
3006
3040
  break;
3007
3041
  case "Enter":
3008
3042
  e.preventDefault();
3009
- if (focusedIndex >= 0 && filteredItems[focusedIndex]) {
3010
- handleItemSelect(filteredItems[focusedIndex]);
3043
+ if (focusedIndex >= 0 && itemsWithAddNew[focusedIndex]) {
3044
+ handleItemSelect(itemsWithAddNew[focusedIndex]);
3011
3045
  }
3012
3046
  break;
3013
3047
  case "Escape":
@@ -3018,9 +3052,10 @@ const SearchableDropdown = React__namespace.forwardRef(({ className, items = [],
3018
3052
  }
3019
3053
  };
3020
3054
  // Update items with onClick handlers that call handleItemSelect
3021
- const itemsWithHandlers = filteredItems.map((item) => ({
3055
+ // Only add onClick if it doesn't already exist (for Add New items)
3056
+ const itemsWithHandlers = itemsWithAddNew.map((item) => ({
3022
3057
  ...item,
3023
- onClick: () => handleItemSelect(item),
3058
+ onClick: item.onClick || (() => handleItemSelect(item)),
3024
3059
  }));
3025
3060
  const showDropdown = isOpen && searchValue.length >= minSearchLength;
3026
3061
  const dropdownMenu = showDropdown && (jsxRuntime.jsx("div", { ref: menuRef, style: {
@@ -3029,7 +3064,9 @@ const SearchableDropdown = React__namespace.forwardRef(({ className, items = [],
3029
3064
  left: `${position.left}px`,
3030
3065
  width: dropdownWidth === "full" ? `${position.width}px` : "auto",
3031
3066
  zIndex: 9999,
3032
- }, children: jsxRuntime.jsx(DropdownMenu, { items: itemsWithHandlers, sectionHeading: sectionHeading, isLoading: isLoading, isEmpty: filteredItems.length === 0, emptyTitle: emptyTitle, emptyDescription: emptyDescription, emptyLinkText: emptyLinkText, onEmptyLinkClick: onEmptyLinkClick, primaryButtonText: primaryButtonText, secondaryButtonText: secondaryButtonText, onPrimaryClick: onPrimaryClick, onSecondaryClick: onSecondaryClick, showChevron: showChevron, emptyIcon: emptyIcon, disableFooter: disableFooter, footerLayout: footerLayout, onClose: () => setIsOpen(false), focusedIndex: focusedIndex, className: dropdownClassName, width: dropdownWidth === "full" ? "full" : "auto" }) }));
3067
+ }, children: jsxRuntime.jsx(DropdownMenu, { items: itemsWithHandlers, sectionHeading: sectionHeading, isLoading: isLoading, isEmpty: itemsWithAddNew.length === 0 && !showAddNew, emptyTitle: emptyTitle, emptyDescription: emptyDescription, emptyLinkText: emptyLinkText, onEmptyLinkClick: onEmptyLinkClick, primaryButtonText: primaryButtonText, secondaryButtonText: secondaryButtonText, onPrimaryClick: onPrimaryClick, onSecondaryClick: onSecondaryClick, showChevron: showChevron, emptyIcon: emptyIcon, disableFooter: disableFooter, showFooter: (primaryButtonText || secondaryButtonText) && !disableFooter
3068
+ ? true
3069
+ : false, footerLayout: footerLayout, onClose: () => setIsOpen(false), focusedIndex: focusedIndex, className: dropdownClassName, width: dropdownWidth === "full" ? "full" : "auto" }) }));
3033
3070
  return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { ref: dropdownRef, className: cn("relative", containerClassName), children: jsxRuntime.jsx(TextField, { ref: inputRef, value: searchValue, onChange: handleSearchChange, onFocus: handleFocus, onKeyDown: handleKeyDown, containerClassName: "mb-0", ...textFieldProps }) }), typeof document !== "undefined" &&
3034
3071
  dropdownMenu &&
3035
3072
  reactDom.createPortal(dropdownMenu, document.body)] }));