@seekora-ai/ui-sdk-react 0.2.7 → 0.2.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.
@@ -1518,13 +1518,17 @@ interface SearchInputProps {
1518
1518
  placeholder?: string;
1519
1519
  autoFocus?: boolean;
1520
1520
  showClearButton?: boolean;
1521
+ /** When false, blur does not close the dropdown (e.g. for overlay mode). Default true. */
1522
+ closeOnBlur?: boolean;
1523
+ /** Optional icon (e.g. magnifying glass) rendered to the left of the input */
1524
+ leftIcon?: React__default.ReactNode;
1521
1525
  className?: string;
1522
1526
  style?: React__default.CSSProperties;
1523
1527
  inputClassName?: string;
1524
1528
  inputStyle?: React__default.CSSProperties;
1525
1529
  ariaLabel?: string;
1526
1530
  }
1527
- declare function SearchInput({ placeholder, autoFocus, showClearButton, className, style, inputClassName, inputStyle, ariaLabel, }: SearchInputProps): React__default.JSX.Element;
1531
+ declare function SearchInput({ placeholder, autoFocus, showClearButton, closeOnBlur, leftIcon, className, style, inputClassName, inputStyle, ariaLabel, }: SearchInputProps): React__default.JSX.Element;
1528
1532
 
1529
1533
  /**
1530
1534
  * DropdownPanel – floating panel for suggestions (primitive)
@@ -4534,15 +4534,16 @@ function parseHighlight(highlighted) {
4534
4534
  .replace(/__\/ais-highlight__/g, '</mark>');
4535
4535
  }
4536
4536
  function transformProduct(raw) {
4537
+ const meta = raw.metadata || {};
4537
4538
  return {
4538
4539
  id: raw.id || raw.objectID,
4539
4540
  objectID: raw.objectID || raw.id,
4540
- title: raw.title || raw.name || raw.productName,
4541
- name: raw.name || raw.title,
4542
- image: raw.image || raw.imageUrl || raw.metadata?.image,
4543
- price: raw.price || raw.sellPrice || raw.metadata?.sellPrice,
4544
- currency: raw.currency || raw.metadata?.currency || '',
4545
- url: raw.url || raw.productId || raw.metadata?.url,
4541
+ title: raw.title || raw.name || raw.productName || meta.name || meta.productName || '',
4542
+ name: raw.name || raw.title || meta.name || meta.productName || '',
4543
+ image: raw.image || raw.imageUrl || meta.image || meta.image_url || meta.images?.[0] || '',
4544
+ price: raw.price ?? raw.sellPrice ?? meta.sellPrice ?? meta.price,
4545
+ currency: raw.currency || meta.currency || '',
4546
+ url: raw.url || raw.productId || meta.url || meta.productId || '',
4546
4547
  clicks: raw.clicks,
4547
4548
  conversions: raw.conversions,
4548
4549
  revenue: raw.revenue,
@@ -4592,7 +4593,10 @@ function useQuerySuggestionsEnhanced(options) {
4592
4593
  }, [enableRecentSearches, recentSearchesKey, maxRecentSearches]);
4593
4594
  // Fetch suggestions
4594
4595
  const fetchSuggestions = useCallback(async (searchQuery) => {
4595
- if (!client || !searchQuery.trim()) {
4596
+ if (!client)
4597
+ return;
4598
+ // When minQueryLength is 0, allow empty query so overlay can show default/trending recommendations on open
4599
+ if (!searchQuery.trim() && minQueryLength > 0) {
4596
4600
  setSuggestions([]);
4597
4601
  setDropdownRecommendations(null);
4598
4602
  return;
@@ -4627,12 +4631,17 @@ function useQuerySuggestionsEnhanced(options) {
4627
4631
  setSuggestions(transformedSuggestions);
4628
4632
  // Extract dropdown recommendations from extensions
4629
4633
  const extensions = (response.extensions || {});
4634
+ const rawResults = response.results;
4635
+ const secondResultHits = Array.isArray(rawResults?.[1]?.hits) ? rawResults[1].hits : [];
4636
+ // Multi-index response: results[0]=suggestions, results[1]=product hits; expose results[1].hits as product_hits for fallback when extensions have no products
4637
+ const productHits = secondResultHits.length > 0 ? secondResultHits.map((h) => transformProduct(h)) : [];
4630
4638
  const recommendations = {
4631
4639
  trending_searches: Array.isArray(extensions.trending_searches) ? extensions.trending_searches : [],
4632
4640
  top_searches: Array.isArray(extensions.top_searches) ? extensions.top_searches : [],
4633
4641
  related_searches: Array.isArray(extensions.related_searches) ? extensions.related_searches : [],
4634
4642
  trending_products: Array.isArray(extensions.trending_products) ? extensions.trending_products.map(transformProduct) : [],
4635
4643
  item_recommendations: Array.isArray(extensions.item_recommendations) ? extensions.item_recommendations.map(transformProduct) : [],
4644
+ product_hits: productHits.length > 0 ? productHits : undefined,
4636
4645
  popular_brands: Array.isArray(extensions.popular_brands) ? extensions.popular_brands : [],
4637
4646
  filtered_tabs: Array.isArray(extensions.filtered_tabs) ? extensions.filtered_tabs.map(transformFilteredTab) : [],
4638
4647
  processing_time_ms: typeof extensions.processing_time_ms === 'number' ? extensions.processing_time_ms : undefined,
@@ -4692,6 +4701,7 @@ function useQuerySuggestionsEnhanced(options) {
4692
4701
  }
4693
4702
  }, [
4694
4703
  client,
4704
+ minQueryLength,
4695
4705
  maxSuggestions,
4696
4706
  includeDropdownRecommendations,
4697
4707
  includeCategories,
@@ -4768,7 +4778,19 @@ function useQuerySuggestionsEnhanced(options) {
4768
4778
  const relatedSearches = dropdownRecommendations?.related_searches || [];
4769
4779
  const popularBrands = dropdownRecommendations?.popular_brands || [];
4770
4780
  const filteredTabsResult = dropdownRecommendations?.filtered_tabs || [];
4771
- const trendingProducts = dropdownRecommendations?.trending_products || [];
4781
+ // Use trending_products, then item_recommendations, then first filtered_tab's products, then results[1].hits (product_hits) so grid shows for any API shape
4782
+ const trendingProducts = useMemo(() => {
4783
+ const fromTrending = dropdownRecommendations?.trending_products;
4784
+ if (fromTrending && fromTrending.length > 0)
4785
+ return fromTrending;
4786
+ const fromItemRecs = dropdownRecommendations?.item_recommendations;
4787
+ if (fromItemRecs && fromItemRecs.length > 0)
4788
+ return fromItemRecs;
4789
+ const firstTab = dropdownRecommendations?.filtered_tabs?.[0];
4790
+ if (firstTab?.products && firstTab.products.length > 0)
4791
+ return firstTab.products;
4792
+ return dropdownRecommendations?.product_hits ?? [];
4793
+ }, [dropdownRecommendations?.trending_products, dropdownRecommendations?.item_recommendations, dropdownRecommendations?.filtered_tabs, dropdownRecommendations?.product_hits]);
4772
4794
  const hasContent = useMemo(() => {
4773
4795
  return (suggestions.length > 0 ||
4774
4796
  recentSearches.length > 0 ||
@@ -6969,15 +6991,17 @@ const inputStyles = {
6969
6991
  color: 'var(--seekora-text-primary, #111827)',
6970
6992
  fontFamily: 'inherit',
6971
6993
  };
6972
- function SearchInput({ placeholder = 'Search...', autoFocus = false, showClearButton = true, className, style, inputClassName, inputStyle, ariaLabel = 'Search', }) {
6994
+ function SearchInput({ placeholder = 'Search...', autoFocus = false, showClearButton = true, closeOnBlur = true, leftIcon, className, style, inputClassName, inputStyle, ariaLabel = 'Search', }) {
6973
6995
  const { query, setQuery, isOpen, setIsOpen, navigateNext, navigatePrev, selectActive, close, } = useSuggestionsContext();
6974
6996
  const inputRef = useRef(null);
6975
6997
  const handleFocus = useCallback(() => {
6976
6998
  setIsOpen(true);
6977
6999
  }, [setIsOpen]);
6978
7000
  const handleBlur = useCallback(() => {
7001
+ if (!closeOnBlur)
7002
+ return;
6979
7003
  setTimeout(() => close(), 200);
6980
- }, [close]);
7004
+ }, [close, closeOnBlur]);
6981
7005
  const handleChange = useCallback((e) => {
6982
7006
  setQuery(e.target.value);
6983
7007
  }, [setQuery]);
@@ -7010,6 +7034,7 @@ function SearchInput({ placeholder = 'Search...', autoFocus = false, showClearBu
7010
7034
  }, [setQuery]);
7011
7035
  return (React.createElement("div", { className: clsx('seekora-suggestions-search-input-wrapper', className), style: { ...defaultStyles, ...style } },
7012
7036
  React.createElement("div", { className: "seekora-suggestions-input-wrapper", style: inputWrapperStyles },
7037
+ leftIcon ? (React.createElement("span", { className: "seekora-suggestions-input-left-icon", style: { display: 'flex', flexShrink: 0, color: 'var(--seekora-text-secondary, #6b7280)' } }, leftIcon)) : null,
7013
7038
  React.createElement("input", { ref: inputRef, type: "text", value: query, onChange: handleChange, onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown, placeholder: placeholder, autoFocus: autoFocus, autoComplete: "off", autoCorrect: "off", autoCapitalize: "off", spellCheck: false, "aria-label": ariaLabel, "aria-expanded": isOpen, "aria-haspopup": "listbox", "aria-autocomplete": "list", role: "combobox", className: clsx('seekora-suggestions-input', inputClassName), style: { ...inputStyles, ...inputStyle } }),
7014
7039
  showClearButton && query ? (React.createElement("button", { type: "button", onClick: handleClear, className: "seekora-suggestions-input-clear", "aria-label": "Clear search", style: {
7015
7040
  padding: 4,