@seekora-ai/ui-sdk-react 0.2.15 → 0.2.16

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.
Files changed (122) hide show
  1. package/dist/components/FacetDropdown.d.ts +2 -0
  2. package/dist/components/FacetDropdown.d.ts.map +1 -1
  3. package/dist/components/FacetDropdown.js +47 -25
  4. package/dist/components/Facets.d.ts +4 -0
  5. package/dist/components/Facets.d.ts.map +1 -1
  6. package/dist/components/Facets.js +21 -2
  7. package/dist/components/InfiniteHits.d.ts +0 -7
  8. package/dist/components/InfiniteHits.d.ts.map +1 -1
  9. package/dist/components/InfiniteHits.js +2 -13
  10. package/dist/components/Pagination.d.ts.map +1 -1
  11. package/dist/components/Pagination.js +27 -9
  12. package/dist/components/QuerySuggestions.d.ts +0 -4
  13. package/dist/components/QuerySuggestions.d.ts.map +1 -1
  14. package/dist/components/QuerySuggestions.js +1 -17
  15. package/dist/components/QuerySuggestionsDropdown.d.ts +0 -4
  16. package/dist/components/QuerySuggestionsDropdown.d.ts.map +1 -1
  17. package/dist/components/QuerySuggestionsDropdown.js +1 -16
  18. package/dist/components/Recommendations.d.ts +0 -7
  19. package/dist/components/Recommendations.d.ts.map +1 -1
  20. package/dist/components/Recommendations.js +3 -23
  21. package/dist/components/RichQuerySuggestions.d.ts +0 -4
  22. package/dist/components/RichQuerySuggestions.d.ts.map +1 -1
  23. package/dist/components/RichQuerySuggestions.js +1 -10
  24. package/dist/components/SearchBar.d.ts +0 -4
  25. package/dist/components/SearchBar.d.ts.map +1 -1
  26. package/dist/components/SearchBar.js +2 -4
  27. package/dist/components/SearchBarWithSuggestions.js +1 -1
  28. package/dist/components/SearchLayout.d.ts.map +1 -1
  29. package/dist/components/SearchLayout.js +13 -17
  30. package/dist/components/SearchProvider.d.ts.map +1 -1
  31. package/dist/components/SearchProvider.js +1 -3
  32. package/dist/components/SearchResults.d.ts +0 -6
  33. package/dist/components/SearchResults.d.ts.map +1 -1
  34. package/dist/components/SearchResults.js +1 -14
  35. package/dist/components/primitives/ImageDisplay.d.ts.map +1 -1
  36. package/dist/components/primitives/ImageDisplay.js +24 -14
  37. package/dist/components/primitives/ImageZoom.d.ts.map +1 -1
  38. package/dist/components/primitives/ImageZoom.js +59 -4
  39. package/dist/components/primitives/VariantSwatches.d.ts.map +1 -1
  40. package/dist/components/primitives/VariantSwatches.js +25 -10
  41. package/dist/components/section-primitives/SectionItemGrid.d.ts +1 -3
  42. package/dist/components/section-primitives/SectionItemGrid.d.ts.map +1 -1
  43. package/dist/components/section-primitives/SectionItemGrid.js +1 -4
  44. package/dist/components/section-primitives/index.d.ts +0 -1
  45. package/dist/components/section-primitives/index.d.ts.map +1 -1
  46. package/dist/components/section-primitives/index.js +0 -1
  47. package/dist/components/suggestions/AmazonDropdown.d.ts.map +1 -1
  48. package/dist/components/suggestions/AmazonDropdown.js +3 -21
  49. package/dist/components/suggestions/GoogleDropdown.d.ts.map +1 -1
  50. package/dist/components/suggestions/GoogleDropdown.js +3 -20
  51. package/dist/components/suggestions/MinimalDropdown.d.ts.map +1 -1
  52. package/dist/components/suggestions/MinimalDropdown.js +2 -2
  53. package/dist/components/suggestions/MobileSheetDropdown.d.ts.map +1 -1
  54. package/dist/components/suggestions/MobileSheetDropdown.js +60 -60
  55. package/dist/components/suggestions/PinterestDropdown.d.ts.map +1 -1
  56. package/dist/components/suggestions/PinterestDropdown.js +41 -41
  57. package/dist/components/suggestions/ShopifyDropdown.d.ts.map +1 -1
  58. package/dist/components/suggestions/ShopifyDropdown.js +3 -4
  59. package/dist/components/suggestions/SpotlightDropdown.d.ts.map +1 -1
  60. package/dist/components/suggestions/SpotlightDropdown.js +2 -3
  61. package/dist/components/suggestions/SuggestionSearchBar.d.ts.map +1 -1
  62. package/dist/components/suggestions/SuggestionSearchBar.js +2 -15
  63. package/dist/components/suggestions/types.d.ts +0 -6
  64. package/dist/components/suggestions/types.d.ts.map +1 -1
  65. package/dist/components/suggestions-primitives/ItemCard.d.ts.map +1 -1
  66. package/dist/components/suggestions-primitives/ItemCard.js +32 -8
  67. package/dist/components/suggestions-primitives/ItemGrid.d.ts.map +1 -1
  68. package/dist/components/suggestions-primitives/ItemGrid.js +9 -2
  69. package/dist/components/suggestions-primitives/ProductCard.d.ts.map +1 -1
  70. package/dist/components/suggestions-primitives/ProductCard.js +14 -5
  71. package/dist/components/suggestions-primitives/ProductCardLayouts.d.ts.map +1 -1
  72. package/dist/components/suggestions-primitives/ProductCardLayouts.js +32 -12
  73. package/dist/components/suggestions-primitives/ProductGrid.d.ts.map +1 -1
  74. package/dist/components/suggestions-primitives/ProductGrid.js +8 -3
  75. package/dist/components/suggestions-primitives/RecentSearchesList.d.ts.map +1 -1
  76. package/dist/components/suggestions-primitives/RecentSearchesList.js +12 -5
  77. package/dist/components/suggestions-primitives/SearchInput.d.ts.map +1 -1
  78. package/dist/components/suggestions-primitives/SearchInput.js +1 -1
  79. package/dist/components/suggestions-primitives/SuggestionItem.d.ts.map +1 -1
  80. package/dist/components/suggestions-primitives/SuggestionItem.js +5 -3
  81. package/dist/components/suggestions-primitives/SuggestionList.d.ts +1 -8
  82. package/dist/components/suggestions-primitives/SuggestionList.d.ts.map +1 -1
  83. package/dist/components/suggestions-primitives/SuggestionList.js +1 -7
  84. package/dist/components/suggestions-primitives/TrendingList.d.ts.map +1 -1
  85. package/dist/components/suggestions-primitives/TrendingList.js +14 -7
  86. package/dist/components/suggestions-primitives/index.d.ts +1 -3
  87. package/dist/components/suggestions-primitives/index.d.ts.map +1 -1
  88. package/dist/components/suggestions-primitives/index.js +1 -2
  89. package/dist/docsearch/components/DocSearch.d.ts.map +1 -1
  90. package/dist/docsearch/components/DocSearch.js +1 -1
  91. package/dist/docsearch/components/Results.d.ts +1 -3
  92. package/dist/docsearch/components/Results.d.ts.map +1 -1
  93. package/dist/docsearch/components/Results.js +1 -9
  94. package/dist/docsearch/components/SearchBox.d.ts +1 -2
  95. package/dist/docsearch/components/SearchBox.d.ts.map +1 -1
  96. package/dist/docsearch/components/SearchBox.js +4 -6
  97. package/dist/docsearch/hooks/useSeekoraSearch.d.ts.map +1 -1
  98. package/dist/docsearch/hooks/useSeekoraSearch.js +6 -0
  99. package/dist/docsearch/types.d.ts +0 -1
  100. package/dist/docsearch/types.d.ts.map +1 -1
  101. package/dist/docsearch.css +2 -5
  102. package/dist/hooks/useClickTracking.d.ts.map +1 -1
  103. package/dist/hooks/useClickTracking.js +4 -11
  104. package/dist/hooks/useExperiment.d.ts.map +1 -1
  105. package/dist/hooks/useExperiment.js +10 -33
  106. package/dist/hooks/useFilters.d.ts +27 -0
  107. package/dist/hooks/useFilters.d.ts.map +1 -0
  108. package/dist/hooks/useFilters.js +66 -0
  109. package/dist/index.d.ts +7 -4
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.js +3 -2
  112. package/dist/index.umd.js +1 -1
  113. package/dist/src/index.d.ts +39 -79
  114. package/dist/src/index.esm.js +800 -452
  115. package/dist/src/index.esm.js.map +1 -1
  116. package/dist/src/index.js +800 -453
  117. package/dist/src/index.js.map +1 -1
  118. package/dist/utils/responsive.d.ts +130 -0
  119. package/dist/utils/responsive.d.ts.map +1 -0
  120. package/dist/utils/responsive.js +231 -0
  121. package/package.json +5 -5
  122. package/src/docsearch/docsearch.css +2 -5
@@ -580,6 +580,87 @@ class SearchStateManager {
580
580
  this.abVariant = abVariant;
581
581
  log.verbose('SearchStateManager: A/B test updated', { abTestId, abVariant });
582
582
  }
583
+ // =========================================================================
584
+ // Filters API
585
+ // =========================================================================
586
+ /** Build filter_by string from current refinements */
587
+ buildFilterString() {
588
+ if (this.state.refinements.length === 0)
589
+ return '';
590
+ const filtersByField = {};
591
+ this.state.refinements.forEach((refinement) => {
592
+ if (!filtersByField[refinement.field]) {
593
+ filtersByField[refinement.field] = [];
594
+ }
595
+ filtersByField[refinement.field].push(refinement.value);
596
+ });
597
+ const filterParts = [];
598
+ Object.entries(filtersByField).forEach(([field, values]) => {
599
+ values.forEach((value) => {
600
+ filterParts.push(`${field}:${value}`);
601
+ });
602
+ });
603
+ return filterParts.join(' && ');
604
+ }
605
+ /** Fetch filter values independently from search */
606
+ async fetchFilters(options) {
607
+ log.verbose('SearchStateManager: Fetching filters', { options });
608
+ try {
609
+ const filterString = this.buildFilterString();
610
+ const response = await this.client.getFilters({
611
+ q: this.state.query || undefined,
612
+ filter: filterString || undefined,
613
+ ...options,
614
+ });
615
+ log.info('SearchStateManager: Filters fetched', {
616
+ filterCount: response?.filters?.length,
617
+ });
618
+ return response;
619
+ }
620
+ catch (err) {
621
+ const error = err instanceof Error ? err : new Error(String(err));
622
+ log.error('SearchStateManager: Failed to fetch filters', { error: error.message });
623
+ throw error;
624
+ }
625
+ }
626
+ /** Search within a facet's values (for autocomplete in facet search boxes) */
627
+ async searchFacetValues(facetName, query) {
628
+ log.verbose('SearchStateManager: Searching facet values', { facetName, query });
629
+ try {
630
+ const filterString = this.buildFilterString();
631
+ const response = await this.client.searchFacetValues(facetName, {
632
+ q: this.state.query || undefined,
633
+ filter: filterString || undefined,
634
+ facetQuery: query,
635
+ });
636
+ log.info('SearchStateManager: Facet values fetched', {
637
+ facetName,
638
+ valueCount: response?.values?.length,
639
+ });
640
+ return response;
641
+ }
642
+ catch (err) {
643
+ const error = err instanceof Error ? err : new Error(String(err));
644
+ log.error('SearchStateManager: Failed to search facet values', { facetName, error: error.message });
645
+ throw error;
646
+ }
647
+ }
648
+ /** Get filter schema (for UI initialization) */
649
+ async getFiltersSchema() {
650
+ log.verbose('SearchStateManager: Getting filters schema');
651
+ try {
652
+ const response = await this.client.getFiltersSchema();
653
+ log.info('SearchStateManager: Filters schema fetched', {
654
+ fieldCount: response?.fields?.length,
655
+ });
656
+ return response;
657
+ }
658
+ catch (err) {
659
+ const error = err instanceof Error ? err : new Error(String(err));
660
+ log.error('SearchStateManager: Failed to get filters schema', { error: error.message });
661
+ throw error;
662
+ }
663
+ }
583
664
  // Clear all state
584
665
  clear() {
585
666
  this.state = {
@@ -1387,9 +1468,7 @@ const SearchProvider = ({ client, theme: themeConfig, enableAnalytics = true, au
1387
1468
  useEffect(() => {
1388
1469
  if (abTestId !== undefined || abVariant !== undefined) {
1389
1470
  stateManager.setAbTest(abTestId, abVariant);
1390
- if (typeof client.setAbTest === 'function') {
1391
- client.setAbTest(abTestId, abVariant);
1392
- }
1471
+ client.setAbTest(abTestId, abVariant);
1393
1472
  }
1394
1473
  }, [stateManager, client, abTestId, abVariant]);
1395
1474
  const value = useMemo(() => ({
@@ -1606,7 +1685,7 @@ const DefaultSearchIcon = ({ size = 18 }) => (React.createElement("svg", { width
1606
1685
  const DefaultClearIcon = ({ size = 14 }) => (React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
1607
1686
  React.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1608
1687
  React.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })));
1609
- const SearchBar = ({ placeholder = 'Search...', showSuggestions = true, debounceMs = 300, minQueryLength = 2, maxSuggestions = 10, onSearch, onQueryChange, onSuggestionSelect, onSearchStateChange, searchOptions, className, style, theme: customTheme, showLoadingState = false, renderSuggestion, renderLoading, renderSearchIcon, showClearButton = true, renderClearIcon, showSubmitButton = false, renderSubmitButton, size = 'medium', }) => {
1688
+ const SearchBar = ({ placeholder = 'Powered by Seekora', showSuggestions = true, debounceMs = 300, minQueryLength = 2, maxSuggestions = 10, onSearch, onQueryChange, onSuggestionSelect, onSearchStateChange, searchOptions, className, style, theme: customTheme, renderSuggestion, renderSearchIcon, showClearButton = true, renderClearIcon, showSubmitButton = false, renderSubmitButton, size = 'medium', }) => {
1610
1689
  const { client, theme, enableAnalytics, autoTrackSearch } = useSearchContext();
1611
1690
  const { query, setQuery, search: triggerSearch, results, loading: searchLoading, error: searchError } = useSearchState();
1612
1691
  const [isFocused, setIsFocused] = useState(false);
@@ -1736,12 +1815,11 @@ const SearchBar = ({ placeholder = 'Search...', showSuggestions = true, debounce
1736
1815
  }, 200);
1737
1816
  }, []);
1738
1817
  const defaultRenderSuggestion = (suggestion, index) => (React.createElement("div", { key: index }, suggestion));
1739
- const defaultRenderLoading = () => (React.createElement("div", { style: { padding: theme.spacing.medium, textAlign: 'center' } }, "Loading suggestions..."));
1740
1818
  const searchBarTheme = customTheme || {};
1741
1819
  const isLoading = suggestionsLoading || searchLoading;
1742
1820
  // Show list when we have suggestions (including previous while loading) or when loading and showLoadingState
1743
1821
  const hasSuggestions = displayedSuggestions.length > 0;
1744
- const showSuggestionsList = isFocused && showSuggestions && query.length >= minQueryLength && (hasSuggestions || (isLoading && showLoadingState));
1822
+ const showSuggestionsList = isFocused && showSuggestions && query.length >= minQueryLength && hasSuggestions;
1745
1823
  // Get processing time from results
1746
1824
  const res = results;
1747
1825
  const processingTime = res?.processingTimeMS
@@ -1869,7 +1947,6 @@ const SearchBar = ({ placeholder = 'Search...', showSuggestions = true, debounce
1869
1947
  zIndex: Z_INDEX$2.dropdown,
1870
1948
  boxSizing: 'border-box',
1871
1949
  } },
1872
- isLoading && displayedSuggestions.length === 0 && showLoadingState && (renderLoading ? renderLoading() : defaultRenderLoading()),
1873
1950
  displayedSuggestions.length > 0 && (React.createElement(React.Fragment, null, displayedSuggestions.map((suggestion, index) => {
1874
1951
  const isSelected = index === selectedIndex;
1875
1952
  const renderFn = renderSuggestion || defaultRenderSuggestion;
@@ -1912,7 +1989,7 @@ const formatPrice$1 = (value, currency = '₹') => {
1912
1989
  }
1913
1990
  return String(value);
1914
1991
  };
1915
- const SearchResults = ({ results: resultsProp, loading: loadingProp, error: errorProp, onResultClick, renderResult, renderEmpty, showLoadingState = false, renderLoading, renderError, className, style, theme: customTheme, itemsPerPage = 20, showPagination = false, viewMode = 'list', fieldMapping, extractResults, enableKeyboardNavigation = true, autoFocus = false, minHeight = '400px', minWidth = '100%', loadingOpacity = 0.7, }) => {
1992
+ const SearchResults = ({ results: resultsProp, loading: loadingProp, error: errorProp, onResultClick, renderResult, renderEmpty, renderError, className, style, theme: customTheme, itemsPerPage = 20, showPagination = false, viewMode = 'list', fieldMapping, extractResults, enableKeyboardNavigation = true, autoFocus = false, minHeight = '400px', minWidth = '100%', }) => {
1916
1993
  const { theme, client, enableAnalytics } = useSearchContext();
1917
1994
  const { results: stateResults, loading: stateLoading, error: stateError, currentPage, itemsPerPage: stateItemsPerPage } = useSearchState();
1918
1995
  const searchResultsTheme = customTheme || {};
@@ -2182,11 +2259,6 @@ const SearchResults = ({ results: resultsProp, loading: loadingProp, error: erro
2182
2259
  textAlign: 'center',
2183
2260
  color: theme.colors.text,
2184
2261
  } }, "No results found"));
2185
- const defaultRenderLoading = () => (React.createElement("div", { className: searchResultsTheme.loadingState, style: {
2186
- padding: theme.spacing.large,
2187
- textAlign: 'center',
2188
- color: theme.colors.text,
2189
- } }, "Loading results..."));
2190
2262
  const defaultRenderError = (err) => (React.createElement("div", { className: searchResultsTheme.errorState, style: {
2191
2263
  padding: theme.spacing.large,
2192
2264
  textAlign: 'center',
@@ -2295,8 +2367,6 @@ const SearchResults = ({ results: resultsProp, loading: loadingProp, error: erro
2295
2367
  const containerStyle = {
2296
2368
  minHeight: `var(--seekora-results-min-height, ${minHeight})`,
2297
2369
  minWidth: `var(--seekora-results-min-width, ${minWidth})`,
2298
- transition: 'opacity 200ms ease-in-out',
2299
- opacity: loading && resultItems.length > 0 ? loadingOpacity : 1,
2300
2370
  ...style,
2301
2371
  };
2302
2372
  // Determine results list style based on view mode
@@ -2320,12 +2390,6 @@ const SearchResults = ({ results: resultsProp, loading: loadingProp, error: erro
2320
2390
  hasError: !!error,
2321
2391
  isLoading: loading,
2322
2392
  });
2323
- // When loading with no previous results, show loading only if showLoadingState (default: show previous results, no loading screen)
2324
- if (loading && resultItems.length === 0 && showLoadingState) {
2325
- log.verbose('SearchResults: Rendering loading state');
2326
- return (React.createElement("div", { className: clsx(searchResultsTheme.container, className), style: containerStyle }, renderLoading ? renderLoading() : defaultRenderLoading()));
2327
- }
2328
- // When loading with previous results, fall through and render them (with opacity transition)
2329
2393
  if (error) {
2330
2394
  log.error('SearchResults: Rendering error state', {
2331
2395
  error: error.message,
@@ -2549,6 +2613,183 @@ const Stats = ({ results: resultsProp, renderStats, className, style, theme: cus
2549
2613
  return null;
2550
2614
  };
2551
2615
 
2616
+ /**
2617
+ * Responsive utilities for adaptive layouts across all screen sizes
2618
+ *
2619
+ * Provides hooks and utilities for:
2620
+ * - Responsive breakpoints
2621
+ * - Responsive grid columns
2622
+ * - Responsive spacing
2623
+ * - Responsive font sizes
2624
+ * - Touch target sizing
2625
+ */
2626
+ /**
2627
+ * Standard breakpoints (in pixels)
2628
+ */
2629
+ const BREAKPOINTS = {
2630
+ sm: 576, // Small devices (landscape phones, ≥ 576px)
2631
+ md: 768, // Medium devices (tablets, ≥ 768px)
2632
+ lg: 992, // Large devices (desktops, ≥ 992px)
2633
+ xl: 1200, // Extra large devices (large desktops, ≥ 1200px)
2634
+ xxl: 1400, // Extra extra large devices (larger desktops, ≥ 1400px)
2635
+ };
2636
+ /**
2637
+ * Get current breakpoint based on window width
2638
+ */
2639
+ function getCurrentBreakpoint(width) {
2640
+ if (width >= BREAKPOINTS.xxl)
2641
+ return 'xxl';
2642
+ if (width >= BREAKPOINTS.xl)
2643
+ return 'xl';
2644
+ if (width >= BREAKPOINTS.lg)
2645
+ return 'lg';
2646
+ if (width >= BREAKPOINTS.md)
2647
+ return 'md';
2648
+ if (width >= BREAKPOINTS.sm)
2649
+ return 'sm';
2650
+ return 'xs';
2651
+ }
2652
+ /**
2653
+ * Hook to get current breakpoint
2654
+ */
2655
+ function useBreakpoint() {
2656
+ const [breakpoint, setBreakpoint] = useState(() => {
2657
+ if (typeof window === 'undefined')
2658
+ return 'lg';
2659
+ return getCurrentBreakpoint(window.innerWidth);
2660
+ });
2661
+ useEffect(() => {
2662
+ const handleResize = () => {
2663
+ setBreakpoint(getCurrentBreakpoint(window.innerWidth));
2664
+ };
2665
+ handleResize();
2666
+ window.addEventListener('resize', handleResize);
2667
+ return () => window.removeEventListener('resize', handleResize);
2668
+ }, []);
2669
+ return breakpoint;
2670
+ }
2671
+ /**
2672
+ * Hook to check if current viewport matches breakpoint
2673
+ */
2674
+ function useMediaQuery(query) {
2675
+ const [matches, setMatches] = useState(() => {
2676
+ if (typeof window === 'undefined')
2677
+ return false;
2678
+ return window.matchMedia(query).matches;
2679
+ });
2680
+ useEffect(() => {
2681
+ const mediaQuery = window.matchMedia(query);
2682
+ const handleChange = () => setMatches(mediaQuery.matches);
2683
+ handleChange();
2684
+ mediaQuery.addEventListener('change', handleChange);
2685
+ return () => mediaQuery.removeEventListener('change', handleChange);
2686
+ }, [query]);
2687
+ return matches;
2688
+ }
2689
+ /**
2690
+ * Check if mobile viewport (< 768px)
2691
+ */
2692
+ function useIsMobile() {
2693
+ return useMediaQuery(`(max-width: ${BREAKPOINTS.md - 1}px)`);
2694
+ }
2695
+ /**
2696
+ * Get responsive grid columns based on breakpoint
2697
+ */
2698
+ function getResponsiveColumns(breakpoint, defaultColumns = 4) {
2699
+ switch (breakpoint) {
2700
+ case 'xs':
2701
+ return Math.min(defaultColumns, 1); // 1 column on extra small
2702
+ case 'sm':
2703
+ return Math.min(defaultColumns, 2); // 2 columns on small
2704
+ case 'md':
2705
+ return Math.min(defaultColumns, 3); // 3 columns on medium
2706
+ case 'lg':
2707
+ return Math.min(defaultColumns, 4); // 4 columns on large
2708
+ case 'xl':
2709
+ return Math.min(defaultColumns, 5); // 5 columns on extra large
2710
+ case 'xxl':
2711
+ return defaultColumns; // Full columns on extra extra large
2712
+ default:
2713
+ return defaultColumns;
2714
+ }
2715
+ }
2716
+ /**
2717
+ * Hook to get responsive grid columns
2718
+ */
2719
+ function useResponsiveColumns(defaultColumns = 4) {
2720
+ const breakpoint = useBreakpoint();
2721
+ return getResponsiveColumns(breakpoint, defaultColumns);
2722
+ }
2723
+ /**
2724
+ * Minimum touch target size (WCAG AAA)
2725
+ */
2726
+ const MIN_TOUCH_TARGET = 44; // 44x44px minimum for touch
2727
+ /**
2728
+ * Get touch target size based on device type
2729
+ */
2730
+ function getTouchTargetSize(isMobile, baseSize = 32) {
2731
+ return isMobile ? Math.max(MIN_TOUCH_TARGET, baseSize) : baseSize;
2732
+ }
2733
+ /**
2734
+ * Hook to get responsive touch target size
2735
+ */
2736
+ function useTouchTargetSize(baseSize = 32) {
2737
+ const isMobile = useIsMobile();
2738
+ return getTouchTargetSize(isMobile, baseSize);
2739
+ }
2740
+ /**
2741
+ * Get responsive gap/padding for grids
2742
+ */
2743
+ function getResponsiveGap(breakpoint, baseGap = 12) {
2744
+ switch (breakpoint) {
2745
+ case 'xs':
2746
+ return Math.max(4, baseGap * 0.5); // 50% on extra small
2747
+ case 'sm':
2748
+ return Math.max(6, baseGap * 0.75); // 75% on small
2749
+ case 'md':
2750
+ case 'lg':
2751
+ return baseGap; // 100% on medium/large
2752
+ case 'xl':
2753
+ case 'xxl':
2754
+ return baseGap * 1.25; // 125% on extra large
2755
+ default:
2756
+ return baseGap;
2757
+ }
2758
+ }
2759
+ /**
2760
+ * Hook to get responsive gap
2761
+ */
2762
+ function useResponsiveGap(baseGap = 12) {
2763
+ const breakpoint = useBreakpoint();
2764
+ return getResponsiveGap(breakpoint, baseGap);
2765
+ }
2766
+ /**
2767
+ * Get responsive padding for containers
2768
+ */
2769
+ function getResponsivePadding(breakpoint, basePadding = 12) {
2770
+ switch (breakpoint) {
2771
+ case 'xs':
2772
+ return Math.max(8, basePadding * 0.67); // ~67% on extra small
2773
+ case 'sm':
2774
+ return Math.max(10, basePadding * 0.83); // ~83% on small
2775
+ case 'md':
2776
+ case 'lg':
2777
+ return basePadding; // 100% on medium/large
2778
+ case 'xl':
2779
+ case 'xxl':
2780
+ return basePadding * 1.33; // ~133% on extra large
2781
+ default:
2782
+ return basePadding;
2783
+ }
2784
+ }
2785
+ /**
2786
+ * Hook to get responsive padding
2787
+ */
2788
+ function useResponsivePadding(basePadding = 12) {
2789
+ const breakpoint = useBreakpoint();
2790
+ return getResponsivePadding(breakpoint, basePadding);
2791
+ }
2792
+
2552
2793
  /**
2553
2794
  * Pagination Component
2554
2795
  *
@@ -2563,16 +2804,29 @@ const Stats = ({ results: resultsProp, renderStats, className, style, theme: cus
2563
2804
  * --seekora-pagination-border
2564
2805
  * --seekora-pagination-radius
2565
2806
  */
2566
- /** Size-specific style tokens */
2567
- const SIZE_TOKENS = {
2568
- small: { paddingKey: 'small', fontSizeKey: 'small', minWidth: '32px' },
2569
- medium: { paddingKey: 'small', fontSizeKey: 'medium', minWidth: '40px' },
2570
- large: { paddingKey: 'medium', fontSizeKey: 'large', minWidth: '48px' },
2807
+ /** Size-specific style tokens (responsive) */
2808
+ const getSizeTokens = (size, isMobile) => {
2809
+ if (isMobile) {
2810
+ // Larger touch targets on mobile
2811
+ return {
2812
+ small: { paddingKey: 'small', fontSizeKey: 'small', minWidth: '40px', minHeight: '40px' },
2813
+ medium: { paddingKey: 'small', fontSizeKey: 'medium', minWidth: '44px', minHeight: '44px' },
2814
+ large: { paddingKey: 'medium', fontSizeKey: 'large', minWidth: '48px', minHeight: '48px' },
2815
+ }[size];
2816
+ }
2817
+ return {
2818
+ small: { paddingKey: 'small', fontSizeKey: 'small', minWidth: '32px', minHeight: '32px' },
2819
+ medium: { paddingKey: 'small', fontSizeKey: 'medium', minWidth: '40px', minHeight: '40px' },
2820
+ large: { paddingKey: 'medium', fontSizeKey: 'large', minWidth: '48px', minHeight: '48px' },
2821
+ }[size];
2571
2822
  };
2572
- const Pagination = ({ results: resultsProp, currentPage: currentPageProp, itemsPerPage: itemsPerPageProp, totalPages: totalPagesProp, onPageChange, maxPages = 7, showFirstLast = true, showPrevNext = true, renderPageButton, className, style, theme: customTheme, variant = 'numbered', loadMoreText = 'Load More', size = 'medium', showPageInfo, previousLabel = 'Previous', nextLabel = 'Next', }) => {
2823
+ const Pagination = ({ results: resultsProp, currentPage: currentPageProp, itemsPerPage: itemsPerPageProp, totalPages: totalPagesProp, onPageChange, maxPages: maxPagesProp = 7, showFirstLast = true, showPrevNext = true, renderPageButton, className, style, theme: customTheme, variant = 'numbered', loadMoreText = 'Load More', size = 'medium', showPageInfo, previousLabel = 'Previous', nextLabel = 'Next', }) => {
2573
2824
  const { theme } = useSearchContext();
2574
2825
  const { results: stateResults, currentPage: stateCurrentPage, setPage } = useSearchState();
2575
2826
  const paginationTheme = customTheme || {};
2827
+ const isMobile = useIsMobile();
2828
+ // Responsive maxPages - show fewer pages on mobile
2829
+ const maxPages = isMobile ? Math.min(maxPagesProp, 5) : maxPagesProp;
2576
2830
  // Use results from prop if provided, otherwise from state manager
2577
2831
  const results = resultsProp || stateResults;
2578
2832
  // Use currentPage from prop if provided, otherwise from state manager
@@ -2596,8 +2850,8 @@ const Pagination = ({ results: resultsProp, currentPage: currentPageProp, itemsP
2596
2850
  const resolvedShowPageInfo = showPageInfo !== undefined
2597
2851
  ? showPageInfo
2598
2852
  : variant === 'simple';
2599
- // Size tokens
2600
- const sizeTokens = SIZE_TOKENS[size];
2853
+ // Size tokens (responsive)
2854
+ const sizeTokens = getSizeTokens(size, isMobile);
2601
2855
  // CSS variable aware helpers — allow overrides via custom properties
2602
2856
  const cssVarBg = 'var(--seekora-pagination-bg, ' + theme.colors.background + ')';
2603
2857
  const cssVarColor = 'var(--seekora-pagination-color, ' + theme.colors.text + ')';
@@ -2617,7 +2871,7 @@ const Pagination = ({ results: resultsProp, currentPage: currentPageProp, itemsP
2617
2871
  };
2618
2872
  const defaultRenderPageButton = (page, isActive, isDisabled) => (React.createElement("button", { type: "button", disabled: isDisabled, onClick: () => handlePageChange(page), "aria-current": isActive ? 'page' : undefined, "aria-label": `Page ${page}`, className: clsx(paginationTheme.item, isActive && paginationTheme.itemActive, isDisabled), style: {
2619
2873
  padding: theme.spacing[sizeTokens.paddingKey],
2620
- margin: `0 ${theme.spacing.small}`,
2874
+ margin: isMobile ? `0 4px` : `0 ${theme.spacing.small}`,
2621
2875
  border: `1px solid ${cssVarBorder}`,
2622
2876
  borderRadius: cssVarRadius,
2623
2877
  backgroundColor: isActive ? cssVarActiveBg : cssVarBg,
@@ -2626,6 +2880,10 @@ const Pagination = ({ results: resultsProp, currentPage: currentPageProp, itemsP
2626
2880
  opacity: 1,
2627
2881
  fontSize: theme.typography.fontSize[sizeTokens.fontSizeKey],
2628
2882
  minWidth: sizeTokens.minWidth,
2883
+ minHeight: sizeTokens.minHeight,
2884
+ display: 'inline-flex',
2885
+ alignItems: 'center',
2886
+ justifyContent: 'center',
2629
2887
  ...(isActive && {
2630
2888
  fontWeight: 'bold',
2631
2889
  }),
@@ -3009,6 +3267,71 @@ const SortBy = ({ options, value: valueProp, defaultValue, onSortChange, renderS
3009
3267
  return null;
3010
3268
  };
3011
3269
 
3270
+ /**
3271
+ * useFilters Hook
3272
+ *
3273
+ * React hook for fetching filter values, searching facet values,
3274
+ * and retrieving filter schema via the dedicated Filters API.
3275
+ */
3276
+ const useFilters = (options) => {
3277
+ const { stateManager } = useSearchContext();
3278
+ const [filters, setFilters] = useState([]);
3279
+ const [schema, setSchema] = useState(null);
3280
+ const [loading, setLoading] = useState(false);
3281
+ const [error, setError] = useState(null);
3282
+ const mountedRef = useRef(true);
3283
+ const autoFetch = options?.autoFetch !== false;
3284
+ // Extract non-autoFetch options to pass to fetchFilters
3285
+ const fetchFilters = useCallback(async () => {
3286
+ setLoading(true);
3287
+ setError(null);
3288
+ try {
3289
+ const { autoFetch: _, ...filterOptions } = options || {};
3290
+ const response = await stateManager.fetchFilters(filterOptions);
3291
+ if (mountedRef.current) {
3292
+ setFilters(response?.filters || []);
3293
+ setLoading(false);
3294
+ }
3295
+ }
3296
+ catch (err) {
3297
+ if (mountedRef.current) {
3298
+ setError(err instanceof Error ? err : new Error(String(err)));
3299
+ setLoading(false);
3300
+ }
3301
+ }
3302
+ }, [stateManager, options?.facetBy, options?.maxFacetValues, options?.disjunctiveFacets?.join(',')]);
3303
+ // Refetch when query or refinements change
3304
+ useEffect(() => {
3305
+ if (!autoFetch)
3306
+ return;
3307
+ const unsubscribe = stateManager.subscribe((_state) => {
3308
+ fetchFilters();
3309
+ });
3310
+ return unsubscribe;
3311
+ }, [stateManager, autoFetch, fetchFilters]);
3312
+ // Fetch schema once on mount
3313
+ useEffect(() => {
3314
+ stateManager.getFiltersSchema().then((response) => {
3315
+ if (mountedRef.current) {
3316
+ setSchema(response);
3317
+ }
3318
+ }).catch(() => {
3319
+ // Schema fetch failure is non-critical
3320
+ });
3321
+ }, [stateManager]);
3322
+ // Cleanup
3323
+ useEffect(() => {
3324
+ return () => {
3325
+ mountedRef.current = false;
3326
+ };
3327
+ }, []);
3328
+ const searchFacetValues = useCallback((facetName, query) => stateManager.searchFacetValues(facetName, query), [stateManager]);
3329
+ const refetch = useCallback(async () => {
3330
+ await fetchFilters();
3331
+ }, [fetchFilters]);
3332
+ return { filters, schema, loading, error, searchFacetValues, refetch };
3333
+ };
3334
+
3012
3335
  /**
3013
3336
  * RangeSlider Component
3014
3337
  *
@@ -3325,10 +3648,12 @@ const CSS_VAR_DEFAULTS = {
3325
3648
  // ---------------------------------------------------------------------------
3326
3649
  // Component
3327
3650
  // ---------------------------------------------------------------------------
3328
- const Facets = ({ results: resultsProp, facets: facetsProp, onFacetChange, renderFacet, renderFacetItem, maxItems = 10, showMore = true, className, style, theme: customTheme, variant = 'checkbox', searchable = false, showCounts = true, colorMap, defaultCollapsed = false, size = 'medium', facetRanges, }) => {
3651
+ const Facets = ({ results: resultsProp, facets: facetsProp, onFacetChange, renderFacet, renderFacetItem, maxItems = 10, showMore = true, className, style, theme: customTheme, variant = 'checkbox', searchable = false, showCounts = true, colorMap, defaultCollapsed = false, size = 'medium', facetRanges, useFiltersApi = false, disjunctiveFacets, }) => {
3329
3652
  const { theme } = useSearchContext();
3330
3653
  const { results: stateResults, refinements, addRefinement, removeRefinement } = useSearchState();
3331
3654
  const facetsTheme = customTheme || {};
3655
+ // Use dedicated Filters API when useFiltersApi is enabled
3656
+ const { filters: filtersApiData, searchFacetValues } = useFilters(useFiltersApi ? { disjunctiveFacets } : { autoFetch: false });
3332
3657
  // expandedFacets is used for "Show more/less" in checkbox/color-swatch variants
3333
3658
  // AND for collapse/expand in collapsible variant.
3334
3659
  const [expandedFacets, setExpandedFacets] = useState({});
@@ -3336,11 +3661,27 @@ const Facets = ({ results: resultsProp, facets: facetsProp, onFacetChange, rende
3336
3661
  // Use results from prop if provided, otherwise from state manager
3337
3662
  const results = resultsProp || stateResults;
3338
3663
  // -------------------------------------------------------------------
3339
- // Extract facets from results
3664
+ // Extract facets from results (or Filters API)
3340
3665
  // -------------------------------------------------------------------
3341
3666
  const extractFacets = () => {
3342
3667
  if (facetsProp)
3343
3668
  return facetsProp;
3669
+ // When using Filters API, convert FilterField[] to Facet[]
3670
+ if (useFiltersApi && filtersApiData.length > 0) {
3671
+ log.verbose('Facets: Using Filters API data', { filterCount: filtersApiData.length });
3672
+ return filtersApiData.map((filter) => ({
3673
+ field: filter.field || '',
3674
+ label: filter.field || '',
3675
+ items: (filter.values || []).map((v) => ({
3676
+ value: v.value || '',
3677
+ count: v.count || 0,
3678
+ selected: refinements.some(r => r.field === filter.field && r.value === v.value),
3679
+ })),
3680
+ stats: filter.stats?.min != null && filter.stats?.max != null
3681
+ ? { min: filter.stats.min, max: filter.stats.max, avg: filter.stats.avg, sum: filter.stats.sum }
3682
+ : undefined,
3683
+ }));
3684
+ }
3344
3685
  // Try to get facets from various locations in the response
3345
3686
  // Primary location: data.facets (widget_mode response structure)
3346
3687
  const rawFacets = results?.data?.facets
@@ -4064,8 +4405,8 @@ const Z_INDEX$1 = {
4064
4405
  // ---------------------------------------------------------------------------
4065
4406
  // Component
4066
4407
  // ---------------------------------------------------------------------------
4067
- const FacetDropdown = ({ field, placeholder = 'All Categories', className, theme: customTheme = {}, onChange, value: controlledValue, maxOptions = 10, showCounts = true, options: providedOptions, client: providedClient, applyFilter = true, navigateOnSelect = false, searchPageUrl = '/search', }) => {
4068
- const { client: contextClient, theme } = useSearchContext();
4408
+ const FacetDropdown = ({ field, placeholder = 'All Categories', className, theme: customTheme = {}, onChange, value: controlledValue, maxOptions = 10, showCounts = true, options: providedOptions, client: providedClient, applyFilter = true, navigateOnSelect = false, searchPageUrl = '/search', useFiltersApi = false, }) => {
4409
+ const { client: contextClient, theme, stateManager } = useSearchContext();
4069
4410
  const { addRefinement, removeRefinement, refinements, search } = useSearchState();
4070
4411
  const client = providedClient || contextClient;
4071
4412
  const [open, setOpen] = useState(false);
@@ -4083,30 +4424,52 @@ const FacetDropdown = ({ field, placeholder = 'All Categories', className, theme
4083
4424
  if (providedOptions || !client)
4084
4425
  return;
4085
4426
  try {
4086
- log.verbose('FacetDropdown: Fetching facet values', { field });
4087
- // Perform a search with facet_by parameter to get facet counts
4088
- const response = await client.search('*', {
4089
- facet_by: field,
4090
- max_facet_values: 100,
4091
- per_page: 0, // We only need facets, not results
4092
- });
4093
- log.verbose('FacetDropdown: Search response', { response });
4094
- // Extract facet counts from the response
4095
- const facetsData = response?.data?.facets || response?.facets;
4096
- const facets = Array.isArray(facetsData) ? facetsData : [];
4097
- const targetFacet = facets.find((f) => f.field_name === field || f.field === field);
4098
- if (targetFacet && targetFacet.counts) {
4099
- const options = targetFacet.counts.map((count) => ({
4100
- value: count.value,
4101
- count: count.count,
4102
- label: count.value,
4103
- }));
4104
- setFacetOptions(options);
4105
- log.verbose('FacetDropdown: Facet options loaded', { field, count: options.length });
4427
+ log.verbose('FacetDropdown: Fetching facet values', { field, useFiltersApi });
4428
+ if (useFiltersApi) {
4429
+ // Use dedicated Filters API via state manager
4430
+ const response = await stateManager.fetchFilters({
4431
+ facetBy: field,
4432
+ maxFacetValues: 100,
4433
+ });
4434
+ const targetFilter = (response?.filters || []).find((f) => f.field === field);
4435
+ if (targetFilter && targetFilter.values) {
4436
+ const options = targetFilter.values.map((v) => ({
4437
+ value: v.value || '',
4438
+ count: v.count || 0,
4439
+ label: v.value || '',
4440
+ }));
4441
+ setFacetOptions(options);
4442
+ log.verbose('FacetDropdown: Facet options loaded via Filters API', { field, count: options.length });
4443
+ }
4444
+ else {
4445
+ log.verbose('FacetDropdown: No filter data found', { field });
4446
+ setFacetOptions([]);
4447
+ }
4106
4448
  }
4107
4449
  else {
4108
- log.verbose('FacetDropdown: No facet data found', { field, facets });
4109
- setFacetOptions([]);
4450
+ // Fallback: use search API with per_page=0
4451
+ const response = await client.search('*', {
4452
+ facet_by: field,
4453
+ max_facet_values: 100,
4454
+ per_page: 0,
4455
+ });
4456
+ log.verbose('FacetDropdown: Search response', { response });
4457
+ const facetsData = response?.data?.facets || response?.facets;
4458
+ const facets = Array.isArray(facetsData) ? facetsData : [];
4459
+ const targetFacet = facets.find((f) => f.field_name === field || f.field === field);
4460
+ if (targetFacet && targetFacet.counts) {
4461
+ const options = targetFacet.counts.map((count) => ({
4462
+ value: count.value,
4463
+ count: count.count,
4464
+ label: count.value,
4465
+ }));
4466
+ setFacetOptions(options);
4467
+ log.verbose('FacetDropdown: Facet options loaded', { field, count: options.length });
4468
+ }
4469
+ else {
4470
+ log.verbose('FacetDropdown: No facet data found', { field, facets });
4471
+ setFacetOptions([]);
4472
+ }
4110
4473
  }
4111
4474
  }
4112
4475
  catch (error) {
@@ -4117,7 +4480,7 @@ const FacetDropdown = ({ field, placeholder = 'All Categories', className, theme
4117
4480
  });
4118
4481
  setFacetOptions([]);
4119
4482
  }
4120
- }, [field, client, providedOptions]);
4483
+ }, [field, client, providedOptions, useFiltersApi]);
4121
4484
  useEffect(() => {
4122
4485
  if (!providedOptions && client) {
4123
4486
  fetchFacetValues();
@@ -4752,15 +5115,9 @@ const ClearRefinements = ({ clearsQuery = false, resetLabel = 'Clear all filters
4752
5115
  const SearchLayout = ({ sidebar, children, header, footer, sidebarWidth = '300px', className, style, theme: customTheme, showSidebarOnMobile = false, }) => {
4753
5116
  const { theme } = useSearchContext();
4754
5117
  const layoutTheme = customTheme || {};
4755
- const [isMobile, setIsMobile] = useState(false);
4756
- useEffect(() => {
4757
- const checkMobile = () => {
4758
- setIsMobile(window.innerWidth <= 768);
4759
- };
4760
- checkMobile();
4761
- window.addEventListener('resize', checkMobile);
4762
- return () => window.removeEventListener('resize', checkMobile);
4763
- }, []);
5118
+ const isMobile = useIsMobile();
5119
+ const responsivePadding = useResponsivePadding(parseInt(theme.spacing.medium) || 16);
5120
+ const responsiveGap = useResponsiveGap(parseInt(theme.spacing.large) || 24);
4764
5121
  return (React.createElement("div", { className: clsx(layoutTheme.container, className), style: {
4765
5122
  display: 'flex',
4766
5123
  flexDirection: 'column',
@@ -4771,7 +5128,7 @@ const SearchLayout = ({ sidebar, children, header, footer, sidebarWidth = '300px
4771
5128
  ...style,
4772
5129
  } },
4773
5130
  header && (React.createElement("header", { className: layoutTheme.header, style: {
4774
- padding: theme.spacing.medium,
5131
+ padding: responsivePadding,
4775
5132
  borderBottom: `1px solid ${theme.colors.border}`,
4776
5133
  backgroundColor: theme.colors.background,
4777
5134
  } }, header)),
@@ -4780,15 +5137,16 @@ const SearchLayout = ({ sidebar, children, header, footer, sidebarWidth = '300px
4780
5137
  flex: 1,
4781
5138
  width: '100%',
4782
5139
  maxWidth: '100%',
4783
- gap: theme.spacing.large,
4784
- padding: theme.spacing.medium,
5140
+ flexDirection: isMobile ? 'column' : 'row',
5141
+ gap: responsiveGap,
5142
+ padding: responsivePadding,
4785
5143
  backgroundColor: theme.colors.background,
4786
5144
  color: theme.colors.text,
4787
- overflow: 'hidden',
5145
+ overflow: isMobile ? 'visible' : 'hidden',
4788
5146
  } },
4789
5147
  sidebar && (!isMobile || showSidebarOnMobile) && (React.createElement("aside", { className: layoutTheme.sidebar, style: {
4790
- width: sidebarWidth,
4791
- minWidth: sidebarWidth,
5148
+ width: isMobile ? '100%' : sidebarWidth,
5149
+ minWidth: isMobile ? '100%' : sidebarWidth,
4792
5150
  flexShrink: 0,
4793
5151
  } }, sidebar)),
4794
5152
  React.createElement("main", { className: layoutTheme.main, style: {
@@ -4801,7 +5159,7 @@ const SearchLayout = ({ sidebar, children, header, footer, sidebarWidth = '300px
4801
5159
  overflow: 'auto',
4802
5160
  } }, children)),
4803
5161
  footer && (React.createElement("footer", { className: layoutTheme.footer, style: {
4804
- padding: theme.spacing.medium,
5162
+ padding: responsivePadding,
4805
5163
  borderTop: `1px solid ${theme.colors.border}`,
4806
5164
  backgroundColor: theme.colors.background,
4807
5165
  } }, footer))));
@@ -5082,7 +5440,7 @@ const HitsPerPage = ({ items, onHitsPerPageChange, renderSelect, className, styl
5082
5440
  * Displays search results with infinite scroll or "Show More" button
5083
5441
  * Accumulates results as user loads more pages
5084
5442
  */
5085
- const InfiniteHits = ({ renderHit, renderEmpty, showInitialLoading = false, renderLoading, renderShowMore, showMoreButton = true, useInfiniteScroll = false, scrollThreshold = 0.1, fieldMapping, showMoreLabel = 'Show more', loadingLabel = 'Loading...', onHitClick, className, style, theme: customTheme, syncWithState = true, }) => {
5443
+ const InfiniteHits = ({ renderHit, renderEmpty, renderShowMore, showMoreButton = true, useInfiniteScroll = false, scrollThreshold = 0.1, fieldMapping, showMoreLabel = 'Show more', onHitClick, className, style, theme: customTheme, syncWithState = true, }) => {
5086
5444
  const { theme, stateManager } = useSearchContext();
5087
5445
  const { results, loading, currentPage, setPage } = useSearchState();
5088
5446
  const infiniteHitsTheme = customTheme || {};
@@ -5214,12 +5572,6 @@ const InfiniteHits = ({ renderHit, renderEmpty, showInitialLoading = false, rend
5214
5572
  textAlign: 'center',
5215
5573
  color: theme.colors.textSecondary,
5216
5574
  } }, "No results found"));
5217
- // Default loading state
5218
- const defaultRenderLoading = () => (React.createElement("div", { className: infiniteHitsTheme.loading, style: {
5219
- padding: theme.spacing.medium,
5220
- textAlign: 'center',
5221
- color: theme.colors.textSecondary,
5222
- } }, loadingLabel));
5223
5575
  // Default "Show More" button
5224
5576
  const defaultRenderShowMore = () => (React.createElement("button", { type: "button", onClick: handleShowMore, disabled: isLastPage || isLoadingMore, className: clsx(infiniteHitsTheme.loadMore, (isLastPage || isLoadingMore) && infiniteHitsTheme.loadMoreDisabled), style: {
5225
5577
  display: 'block',
@@ -5236,11 +5588,7 @@ const InfiniteHits = ({ renderHit, renderEmpty, showInitialLoading = false, rend
5236
5588
  : theme.borderRadius.medium,
5237
5589
  cursor: isLastPage || isLoadingMore ? 'not-allowed' : 'pointer',
5238
5590
  transition: theme.transitions?.fast || '150ms ease-in-out',
5239
- } }, isLoadingMore ? loadingLabel : isLastPage ? 'No more results' : showMoreLabel));
5240
- // Initial loading state (only when showInitialLoading: default no loading screen)
5241
- if (loading && accumulatedHits.length === 0 && showInitialLoading) {
5242
- return (React.createElement("div", { className: clsx(infiniteHitsTheme.root, className), style: style }, renderLoading ? renderLoading() : defaultRenderLoading()));
5243
- }
5591
+ } }, isLastPage ? 'No more results' : showMoreLabel));
5244
5592
  if (loading && accumulatedHits.length === 0) {
5245
5593
  return React.createElement("div", { className: clsx(infiniteHitsTheme.root, className), style: style });
5246
5594
  }
@@ -5253,7 +5601,6 @@ const InfiniteHits = ({ renderHit, renderEmpty, showInitialLoading = false, rend
5253
5601
  showMoreButton && !useInfiniteScroll && !isLastPage && (renderShowMore
5254
5602
  ? renderShowMore({ isLoading: isLoadingMore, isLastPage, onClick: handleShowMore })
5255
5603
  : defaultRenderShowMore()),
5256
- isLoadingMore && (renderLoading ? renderLoading() : defaultRenderLoading()),
5257
5604
  useInfiniteScroll && !isLastPage && (React.createElement("div", { ref: sentinelRef, className: infiniteHitsTheme.sentinel, style: { height: '1px', visibility: 'hidden' }, "aria-hidden": "true" }))));
5258
5605
  };
5259
5606
 
@@ -6045,16 +6392,11 @@ const MobileFiltersButton = ({ onClick, text = 'Filters', showCount = true, clas
6045
6392
  * - FrequentlyBoughtTogether: Bundle recommendations
6046
6393
  * - RecentlyViewed: User's recently viewed items
6047
6394
  */
6048
- const RelatedProducts = ({ productId, items: itemsProp, loading: loadingProp = false, showLoadingState = false, title = 'Related Products', maxItems = 6, renderItem, onItemClick, className, style, theme: customTheme, layout = 'horizontal', currencySymbol = '$', }) => {
6395
+ const RelatedProducts = ({ productId, items: itemsProp, loading: loadingProp = false, title = 'Related Products', maxItems = 6, renderItem, onItemClick, className, style, theme: customTheme, layout = 'horizontal', currencySymbol = '$', }) => {
6049
6396
  const { theme } = useSearchContext();
6050
- const recommendationTheme = customTheme || {};
6051
6397
  // If items are provided, use them directly
6052
6398
  const items = itemsProp?.slice(0, maxItems) || [];
6053
6399
  const loading = loadingProp;
6054
- if (loading && items.length === 0 && showLoadingState) {
6055
- return (React.createElement("div", { className: clsx(recommendationTheme.root, className), style: style },
6056
- React.createElement("div", { className: recommendationTheme.loading, style: getLoadingStyle(theme) }, "Loading related products...")));
6057
- }
6058
6400
  if (loading && items.length === 0)
6059
6401
  return null;
6060
6402
  if (items.length === 0) {
@@ -6062,15 +6404,10 @@ const RelatedProducts = ({ productId, items: itemsProp, loading: loadingProp = f
6062
6404
  }
6063
6405
  return (React.createElement(RecommendationSection, { title: title, items: items, renderItem: renderItem, onItemClick: onItemClick, className: className, style: style, theme: customTheme, layout: layout, currencySymbol: currencySymbol }));
6064
6406
  };
6065
- const TrendingItems = ({ items: itemsProp, loading: loadingProp = false, showLoadingState = false, title = 'Trending Now', maxItems = 8, renderItem, onItemClick, className, style, theme: customTheme, layout = 'horizontal', currencySymbol = '$', }) => {
6407
+ const TrendingItems = ({ items: itemsProp, loading: loadingProp = false, title = 'Trending Now', maxItems = 8, renderItem, onItemClick, className, style, theme: customTheme, layout = 'horizontal', currencySymbol = '$', }) => {
6066
6408
  const { theme } = useSearchContext();
6067
- const recommendationTheme = customTheme || {};
6068
6409
  const items = itemsProp?.slice(0, maxItems) || [];
6069
6410
  const loading = loadingProp;
6070
- if (loading && items.length === 0 && showLoadingState) {
6071
- return (React.createElement("div", { className: clsx(recommendationTheme.root, className), style: style },
6072
- React.createElement("div", { className: recommendationTheme.loading, style: getLoadingStyle(theme) }, "Loading trending items...")));
6073
- }
6074
6411
  if (loading && items.length === 0)
6075
6412
  return null;
6076
6413
  if (items.length === 0) {
@@ -6078,7 +6415,7 @@ const TrendingItems = ({ items: itemsProp, loading: loadingProp = false, showLoa
6078
6415
  }
6079
6416
  return (React.createElement(RecommendationSection, { title: title, items: items, renderItem: renderItem, onItemClick: onItemClick, className: className, style: style, theme: customTheme, layout: layout, currencySymbol: currencySymbol }));
6080
6417
  };
6081
- const FrequentlyBoughtTogether = ({ productId, items: itemsProp, loading: loadingProp = false, showLoadingState = false, title = 'Frequently Bought Together', maxItems = 4, renderItem, onItemClick, className, style, theme: customTheme, layout = 'horizontal', currencySymbol = '$', showAddAllButton = true, onAddAll, }) => {
6418
+ const FrequentlyBoughtTogether = ({ productId, items: itemsProp, loading: loadingProp = false, title = 'Frequently Bought Together', maxItems = 4, renderItem, onItemClick, className, style, theme: customTheme, layout = 'horizontal', currencySymbol = '$', showAddAllButton = true, onAddAll, }) => {
6082
6419
  const { theme } = useSearchContext();
6083
6420
  const recommendationTheme = customTheme || {};
6084
6421
  const items = itemsProp?.slice(0, maxItems) || [];
@@ -6091,10 +6428,6 @@ const FrequentlyBoughtTogether = ({ productId, items: itemsProp, loading: loadin
6091
6428
  return sum + price;
6092
6429
  }, 0);
6093
6430
  }, [items]);
6094
- if (loading && items.length === 0 && showLoadingState) {
6095
- return (React.createElement("div", { className: clsx(recommendationTheme.root, className), style: style },
6096
- React.createElement("div", { className: recommendationTheme.loading, style: getLoadingStyle(theme) }, "Loading recommendations...")));
6097
- }
6098
6431
  if (loading && items.length === 0)
6099
6432
  return null;
6100
6433
  if (items.length === 0) {
@@ -6301,21 +6634,13 @@ const DefaultRecommendationItem = ({ item, theme: recommendationTheme, currencyS
6301
6634
  currencySymbol,
6302
6635
  typeof price === 'number' ? price.toFixed(2) : price)))));
6303
6636
  };
6304
- // Helper function
6305
- function getLoadingStyle(theme) {
6306
- return {
6307
- padding: theme.spacing.large,
6308
- textAlign: 'center',
6309
- color: theme.colors.textSecondary,
6310
- };
6311
- }
6312
6637
 
6313
6638
  /**
6314
6639
  * QuerySuggestions Component
6315
6640
  *
6316
6641
  * Standalone component for displaying query suggestions
6317
6642
  */
6318
- const QuerySuggestions = ({ query = '', maxSuggestions = 10, debounceMs = 300, minQueryLength = 2, onSuggestionClick, renderSuggestion, showLoadingState = false, renderLoading, renderEmpty, showTitle = false, title = 'Suggestions', className, style, theme: customTheme, }) => {
6643
+ const QuerySuggestions = ({ query = '', maxSuggestions = 10, debounceMs = 300, minQueryLength = 2, onSuggestionClick, renderSuggestion, renderEmpty, showTitle = false, title = 'Suggestions', className, style, theme: customTheme, }) => {
6319
6644
  const { client, theme } = useSearchContext();
6320
6645
  const [selectedIndex, setSelectedIndex] = useState(-1);
6321
6646
  const { suggestions, loading, error } = useQuerySuggestions({
@@ -6333,11 +6658,6 @@ const QuerySuggestions = ({ query = '', maxSuggestions = 10, debounceMs = 300, m
6333
6658
  "(",
6334
6659
  suggestion.count,
6335
6660
  ")"))));
6336
- const defaultRenderLoading = () => (React.createElement("div", { style: {
6337
- padding: theme.spacing.medium,
6338
- textAlign: 'center',
6339
- color: theme.colors.text,
6340
- } }, "Loading suggestions..."));
6341
6661
  const defaultRenderEmpty = () => (React.createElement("div", { style: {
6342
6662
  padding: theme.spacing.medium,
6343
6663
  textAlign: 'center',
@@ -6352,17 +6672,6 @@ const QuerySuggestions = ({ query = '', maxSuggestions = 10, debounceMs = 300, m
6352
6672
  if (query.length < minQueryLength) {
6353
6673
  return null;
6354
6674
  }
6355
- // When loading with no previous results, show loading only if showLoadingState (default: show previous results, no loading screen)
6356
- if (loading && displayedSuggestions.length === 0 && showLoadingState) {
6357
- return (React.createElement("div", { className: clsx(suggestionsTheme.container, className), style: style },
6358
- showTitle && (React.createElement("div", { className: suggestionsTheme.title, style: {
6359
- fontSize: theme.typography.fontSize.large,
6360
- fontWeight: 'bold',
6361
- marginBottom: theme.spacing.medium,
6362
- color: theme.colors.text,
6363
- } }, title)),
6364
- renderLoading ? renderLoading() : defaultRenderLoading()));
6365
- }
6366
6675
  if (error || (!loading && displayedSuggestions.length === 0)) {
6367
6676
  return (React.createElement("div", { className: clsx(suggestionsTheme.container, className), style: style },
6368
6677
  showTitle && (React.createElement("div", { className: suggestionsTheme.title, style: {
@@ -6904,15 +7213,6 @@ const defaultStyles$1 = {
6904
7213
  removeButtonVisible: {
6905
7214
  opacity: 1,
6906
7215
  },
6907
- loadingState: {
6908
- display: 'flex',
6909
- alignItems: 'center',
6910
- justifyContent: 'center',
6911
- padding: '24px 16px',
6912
- color: 'var(--seekora-text-secondary, inherit)',
6913
- fontSize: '14px',
6914
- gap: '8px',
6915
- },
6916
7216
  emptyState: {
6917
7217
  display: 'flex',
6918
7218
  flexDirection: 'column',
@@ -6966,14 +7266,11 @@ const ClockIcon$6 = ({ className, style }) => (React.createElement("svg", { clas
6966
7266
  React.createElement("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z", clipRule: "evenodd" })));
6967
7267
  const CloseIcon$1 = ({ className, style }) => (React.createElement("svg", { className: className, style: style, viewBox: "0 0 20 20", fill: "currentColor", width: "16", height: "16" },
6968
7268
  React.createElement("path", { fillRule: "evenodd", d: "M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z", clipRule: "evenodd" })));
6969
- const LoadingSpinner = ({ style }) => (React.createElement("svg", { style: { animation: 'spin 1s linear infinite', ...style }, viewBox: "0 0 24 24", width: "20", height: "20" },
6970
- React.createElement("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "3", fill: "none", opacity: "0.25" }),
6971
- React.createElement("path", { fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" })));
6972
7269
  // ============================================================================
6973
7270
  // Component
6974
7271
  // ============================================================================
6975
7272
  const QuerySuggestionsDropdown = forwardRef(function QuerySuggestionsDropdown(props, ref) {
6976
- const { query, isOpen = true, maxSuggestions = 8, minQueryLength = 1, debounceMs = 200, showRecentSearches = true, maxRecentSearches = 5, showCounts = true, showLoading = false, showEmptyState = true, highlight = { enabled: true, preTag: '<mark>', postTag: '</mark>' }, keyboardNav = { enabled: true }, animation = { enabled: true, duration: 150, entrance: 'fade' }, classNames = {}, style, renderSuggestion, renderRecentSearch, renderLoading, renderEmpty, footer, position = 'absolute', width = '100%', zIndex = 1000, closeOnClickOutside = true, closeOnEscape = true, ariaLabel = 'Search suggestions', onSuggestionSelect, onRecentSearchClick, onRecentSearchRemove, onOpen, onClose, onNavigate, } = props;
7273
+ const { query, isOpen = true, maxSuggestions = 8, minQueryLength = 1, debounceMs = 200, showRecentSearches = true, maxRecentSearches = 5, showCounts = true, showEmptyState = true, highlight = { enabled: true, preTag: '<mark>', postTag: '</mark>' }, keyboardNav = { enabled: true }, animation = { enabled: true, duration: 150, entrance: 'fade' }, classNames = {}, style, renderSuggestion, renderRecentSearch, renderEmpty, footer, position = 'absolute', width = '100%', zIndex = 1000, closeOnClickOutside = true, closeOnEscape = true, ariaLabel = 'Search suggestions', onSuggestionSelect, onRecentSearchClick, onRecentSearchRemove, onOpen, onClose, onNavigate, } = props;
6977
7274
  const { client, theme } = useSearchContext();
6978
7275
  const containerRef = useRef(null);
6979
7276
  const [activeIndex, setActiveIndex] = useState(-1);
@@ -7155,9 +7452,6 @@ const QuerySuggestionsDropdown = forwardRef(function QuerySuggestionsDropdown(pr
7155
7452
  ...animationStyle,
7156
7453
  ...style,
7157
7454
  } },
7158
- loading && showLoading && (React.createElement("div", { className: classNames.loadingState, style: defaultStyles$1.loadingState }, renderLoading ? renderLoading() : (React.createElement(React.Fragment, null,
7159
- React.createElement(LoadingSpinner, null),
7160
- React.createElement("span", null, "Searching..."))))),
7161
7455
  showRecent && (React.createElement("div", { className: clsx('seekora-suggestions-section', classNames.section, classNames.recentSearches) },
7162
7456
  React.createElement("div", { className: classNames.sectionTitle, style: defaultStyles$1.sectionTitle }, "Recent Searches"),
7163
7457
  recentSearches.slice(0, maxRecentSearches).map((search, index) => {
@@ -7404,14 +7698,6 @@ const styles$2 = {
7404
7698
  fontSize: '12px',
7405
7699
  color: 'var(--seekora-text-secondary, inherit)',
7406
7700
  },
7407
- loadingOverlay: {
7408
- position: 'absolute',
7409
- inset: 0,
7410
- display: 'flex',
7411
- alignItems: 'center',
7412
- justifyContent: 'center',
7413
- backgroundColor: 'rgba(255, 255, 255, 0.1)',
7414
- },
7415
7701
  emptyState: {
7416
7702
  padding: '32px 16px',
7417
7703
  textAlign: 'center',
@@ -7439,7 +7725,7 @@ const CloseIcon = () => (React.createElement("svg", { viewBox: "0 0 20 20", fill
7439
7725
  // Component
7440
7726
  // ============================================================================
7441
7727
  const RichQuerySuggestions = forwardRef(function RichQuerySuggestions(props, ref) {
7442
- const { query, isOpen = true, sections = DEFAULT_SECTIONS, maxSuggestionsPerSection = 8, minQueryLength = 0, debounceMs = 200, includeDropdownRecommendations = true, includeDropdownProductList = true, includeFilteredTabs = true, includeCategories = true, maxCategories = 3, showCounts = true, showCategoryCounts = true, showSectionHeaders = true, classNames = {}, style, renderSuggestion, renderCategory, renderTrendingItem, renderRecentItem, header, footer, width = '100%', maxHeight = '480px', zIndex = 1000, ariaLabel = 'Search suggestions', analyticsTags, onSuggestionSelect, onCategoryClick, onRecentSearchClick, onRecentSearchRemove, onViewAllClick, onOpen, onClose, showLoadingOverlay = false, renderLoading, } = props;
7728
+ const { query, isOpen = true, sections = DEFAULT_SECTIONS, maxSuggestionsPerSection = 8, minQueryLength = 0, debounceMs = 200, includeDropdownRecommendations = true, includeDropdownProductList = true, includeFilteredTabs = true, includeCategories = true, maxCategories = 3, showCounts = true, showCategoryCounts = true, showSectionHeaders = true, classNames = {}, style, renderSuggestion, renderCategory, renderTrendingItem, renderRecentItem, header, footer, width = '100%', maxHeight = '480px', zIndex = 1000, ariaLabel = 'Search suggestions', analyticsTags, onSuggestionSelect, onCategoryClick, onRecentSearchClick, onRecentSearchRemove, onViewAllClick, onOpen, onClose, } = props;
7443
7729
  const { client } = useSearchContext();
7444
7730
  const containerRef = useRef(null);
7445
7731
  const [activeIndex, setActiveIndex] = useState(-1);
@@ -7658,7 +7944,6 @@ const RichQuerySuggestions = forwardRef(function RichQuerySuggestions(props, ref
7658
7944
  } },
7659
7945
  header && React.createElement("div", { style: styles$2.header }, header),
7660
7946
  React.createElement("div", { style: { ...styles$2.content, maxHeight } },
7661
- loading && showLoadingOverlay && (React.createElement("div", { style: styles$2.loadingOverlay }, renderLoading ? renderLoading() : React.createElement("span", null, "Loading..."))),
7662
7947
  enabledSections.map((section, index) => {
7663
7948
  let content = null;
7664
7949
  switch (section.id) {
@@ -8532,7 +8817,7 @@ const ClearIcon$1 = () => (React.createElement("svg", { viewBox: "0 0 20 20", fi
8532
8817
  // Component
8533
8818
  // ============================================================================
8534
8819
  const SearchBarWithSuggestions = forwardRef(function SearchBarWithSuggestions(props, ref) {
8535
- const { variant = 'classic', placeholder = 'Search...', initialQuery = '', value, onQueryChange, onSearch, onSuggestionSelect, onProductClick, showSearchButton = false, searchButtonText = 'Search', showClearButton = true, autoFocus = false, minQueryLength = 1, maxSuggestions = 8, maxProducts = 8, debounceMs = 200, showRecentSearches = true, showTrendingOnEmpty = true, includeDropdownRecommendations = false, filteredTabs, enableAnalytics = true, analyticsTags, includeFacets, includeCategories, dropdownWidth, dropdownMaxHeight, classNames = {}, style, inputStyle, ariaLabel = 'Search', } = props;
8820
+ const { variant = 'classic', placeholder = 'Powered by Seekora', initialQuery = '', value, onQueryChange, onSearch, onSuggestionSelect, onProductClick, showSearchButton = false, searchButtonText = 'Search', showClearButton = true, autoFocus = false, minQueryLength = 1, maxSuggestions = 8, maxProducts = 8, debounceMs = 200, showRecentSearches = true, showTrendingOnEmpty = true, includeDropdownRecommendations = false, filteredTabs, enableAnalytics = true, analyticsTags, includeFacets, includeCategories, dropdownWidth, dropdownMaxHeight, classNames = {}, style, inputStyle, ariaLabel = 'Search', } = props;
8536
8821
  const { client } = useSearchContext();
8537
8822
  const inputRef = useRef(null);
8538
8823
  const dropdownRef = useRef(null);
@@ -8987,7 +9272,7 @@ const inputStyles = {
8987
9272
  color: 'var(--seekora-text-primary, inherit)',
8988
9273
  fontFamily: 'inherit',
8989
9274
  };
8990
- function SearchInput({ placeholder = 'Search...', autoFocus = false, showClearButton = true, closeOnBlur = true, leftIcon, className, style, inputClassName, inputStyle, ariaLabel = 'Search', }) {
9275
+ function SearchInput({ placeholder = 'Powered by Seekora', autoFocus = false, showClearButton = true, closeOnBlur = true, leftIcon, className, style, inputClassName, inputStyle, ariaLabel = 'Search', }) {
8991
9276
  const { query, setQuery, isOpen, setIsOpen, navigateNext, navigatePrev, selectActive, close, } = useSuggestionsContext();
8992
9277
  const inputRef = useRef(null);
8993
9278
  const handleFocus = useCallback(() => {
@@ -9195,17 +9480,19 @@ const defaultItemStyle = {
9195
9480
  whiteSpace: 'nowrap',
9196
9481
  };
9197
9482
  function SuggestionItem({ suggestion, index, isActive, onSelect, className, style, enableHighlightMarkup = true, highlightMarkupOptions, renderHighlight, }) {
9483
+ const [isHovered, setIsHovered] = React.useState(false);
9198
9484
  const displayText = suggestion.highlightedQuery ?? suggestion.query;
9199
9485
  const content = renderHighlight != null
9200
9486
  ? renderHighlight(displayText)
9201
9487
  : enableHighlightMarkup
9202
9488
  ? parseHighlightMarkup(displayText ?? '', highlightMarkupOptions)
9203
9489
  : (suggestion.query ?? displayText ?? '');
9204
- return (React.createElement("li", { role: "option", "aria-selected": isActive, id: `seekora-suggestion-${index}`, className: clsx('seekora-suggestions-item', isActive && 'seekora-suggestions-item--active', className), style: {
9490
+ const showHighlight = isActive || isHovered;
9491
+ return (React.createElement("li", { role: "option", "aria-selected": isActive, id: `seekora-suggestion-${index}`, className: clsx('seekora-suggestions-item', isActive && 'seekora-suggestions-item--active', isHovered && 'seekora-suggestions-item--hover', className), style: {
9205
9492
  ...defaultItemStyle,
9206
- ...(isActive ? { backgroundColor: 'var(--seekora-bg-hover, #f3f4f6)' } : {}),
9493
+ ...(showHighlight ? { backgroundColor: 'var(--seekora-bg-hover, #f3f4f6)' } : {}),
9207
9494
  ...style,
9208
- }, onMouseDown: (e) => {
9495
+ }, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), onMouseDown: (e) => {
9209
9496
  e.preventDefault();
9210
9497
  onSelect();
9211
9498
  } },
@@ -9224,18 +9511,12 @@ const listStyle = {
9224
9511
  margin: 0,
9225
9512
  padding: '4px 0',
9226
9513
  };
9227
- function SuggestionList({ maxItems = 10, className, style, listClassName, showLoadingState = false, renderLoading, enableHighlightMarkup = true, highlightMarkupOptions, renderItem, }) {
9514
+ function SuggestionList({ maxItems = 10, className, style, listClassName, enableHighlightMarkup = true, highlightMarkupOptions, renderItem, }) {
9228
9515
  const { suggestions, activeIndex, loading, selectSuggestion, getAllNavigableItems, } = useSuggestionsContext();
9229
9516
  const items = suggestions.slice(0, maxItems);
9230
9517
  const navigableItems = getAllNavigableItems();
9231
9518
  const suggestionStartIndex = navigableItems.findIndex((n) => n.type === 'suggestion');
9232
9519
  const activeIsInSuggestions = suggestionStartIndex >= 0 && activeIndex >= suggestionStartIndex && activeIndex < suggestionStartIndex + items.length;
9233
- // When loading with no previous results, show loading only if showLoadingState (default: don't show loading screen)
9234
- if (loading && items.length === 0 && showLoadingState) {
9235
- if (renderLoading)
9236
- return React.createElement(React.Fragment, null, renderLoading());
9237
- return (React.createElement("div", { className: clsx('seekora-suggestions-loading', className), style: { padding: 16, color: 'var(--seekora-text-secondary, #6b7280)', fontSize: '0.875rem', ...style } }, "Loading..."));
9238
- }
9239
9520
  // When loading with previous results, show previous results (no loading UI)
9240
9521
  if (items.length === 0)
9241
9522
  return null;
@@ -9365,10 +9646,66 @@ function ImageZoom({ src, alt = '', mode = 'both', zoomLevel = 2.5, className, s
9365
9646
  const rect = containerRef.current.getBoundingClientRect();
9366
9647
  const bgPosX = (cursorPos.x / rect.width) * 100;
9367
9648
  const bgPosY = (cursorPos.y / rect.height) * 100;
9649
+ // Calculate available space in all directions
9650
+ const viewportWidth = window.innerWidth;
9651
+ const viewportHeight = window.innerHeight;
9652
+ const gap = 16;
9653
+ const mouseBuffer = 50; // Minimum distance from mouse cursor
9654
+ // Calculate mouse position in viewport
9655
+ const mouseX = rect.left + cursorPos.x;
9656
+ const mouseY = rect.top + cursorPos.y;
9657
+ // Calculate space available in each direction
9658
+ const spaceRight = viewportWidth - rect.right - gap;
9659
+ const spaceLeft = rect.left - gap;
9660
+ // Determine optimal horizontal position
9661
+ let left;
9662
+ let top = rect.top;
9663
+ // Try right side first (default Amazon-style)
9664
+ if (spaceRight >= zoomPanelSize.width) {
9665
+ left = rect.right + gap;
9666
+ }
9667
+ // Try left side
9668
+ else if (spaceLeft >= zoomPanelSize.width) {
9669
+ left = rect.left - zoomPanelSize.width - gap;
9670
+ }
9671
+ // Not enough space on either side - center on the side with more space
9672
+ else if (spaceRight > spaceLeft) {
9673
+ left = rect.right + gap;
9674
+ }
9675
+ else {
9676
+ left = Math.max(gap, rect.left - zoomPanelSize.width - gap);
9677
+ }
9678
+ // Adjust vertical position to keep within viewport
9679
+ // Try to align with image top by default
9680
+ if (rect.top + zoomPanelSize.height > viewportHeight - gap) {
9681
+ // Panel would overflow bottom - adjust upward
9682
+ top = Math.max(gap, viewportHeight - zoomPanelSize.height - gap);
9683
+ }
9684
+ // Ensure panel doesn't overlap with mouse cursor
9685
+ const panelRight = left + zoomPanelSize.width;
9686
+ const panelBottom = top + zoomPanelSize.height;
9687
+ // Check if mouse is inside the panel area
9688
+ if (mouseX >= left - mouseBuffer &&
9689
+ mouseX <= panelRight + mouseBuffer &&
9690
+ mouseY >= top - mouseBuffer &&
9691
+ mouseY <= panelBottom + mouseBuffer) {
9692
+ // Mouse is too close - try to reposition
9693
+ // If on right side and there's space on left, flip to left
9694
+ if (left > rect.right && spaceLeft >= zoomPanelSize.width) {
9695
+ left = rect.left - zoomPanelSize.width - gap;
9696
+ }
9697
+ // If on left side and there's space on right, flip to right
9698
+ else if (left < rect.left && spaceRight >= zoomPanelSize.width) {
9699
+ left = rect.right + gap;
9700
+ }
9701
+ }
9702
+ // Clamp to viewport boundaries
9703
+ left = Math.max(gap, Math.min(left, viewportWidth - zoomPanelSize.width - gap));
9704
+ top = Math.max(gap, Math.min(top, viewportHeight - zoomPanelSize.height - gap));
9368
9705
  return {
9369
- position: 'fixed', // Changed from absolute to fixed for better positioning
9370
- top: rect.top,
9371
- left: rect.right + 16, // 16px gap from the image
9706
+ position: 'fixed',
9707
+ top,
9708
+ left,
9372
9709
  width: zoomPanelSize.width,
9373
9710
  height: zoomPanelSize.height,
9374
9711
  backgroundImage: `url(${src})`,
@@ -9405,7 +9742,6 @@ function ImageZoom({ src, alt = '', mode = 'both', zoomLevel = 2.5, className, s
9405
9742
  if (!containerRef.current || !imageRef.current)
9406
9743
  return {};
9407
9744
  const rect = containerRef.current.getBoundingClientRect();
9408
- imageRef.current.getBoundingClientRect();
9409
9745
  return {
9410
9746
  position: 'absolute',
9411
9747
  width: rect.width * zoomLevel,
@@ -9628,6 +9964,11 @@ function ImageDisplay({ images, variant = 'single', alt = '', className, style,
9628
9964
  const [hovering, setHovering] = useState(false);
9629
9965
  const safeImages = Array.isArray(images) ? images.filter(Boolean) : [];
9630
9966
  const current = safeImages[index] ?? safeImages[0];
9967
+ // Responsive values
9968
+ const isMobile = useIsMobile();
9969
+ const arrowSize = useTouchTargetSize(32); // 44px on mobile, 32px on desktop
9970
+ const thumbSize = isMobile ? 56 : 48; // Larger thumbnails on mobile
9971
+ const responsiveGap = useResponsiveGap(8);
9631
9972
  if (safeImages.length === 0) {
9632
9973
  return React.createElement("div", { className: clsx('seekora-img-display', 'seekora-img-placeholder', className), style: { ...imgBaseStyle, ...style }, "aria-hidden": true });
9633
9974
  }
@@ -9664,22 +10005,24 @@ function ImageDisplay({ images, variant = 'single', alt = '', className, style,
9664
10005
  return (React.createElement("div", { className: clsx('seekora-img-display', 'seekora-img-carousel', className), style: { position: 'relative', ...style } },
9665
10006
  mainImage,
9666
10007
  safeImages.length > 1 && (React.createElement(React.Fragment, null,
9667
- React.createElement("button", { type: "button", "aria-label": "Previous", className: "seekora-img-carousel-prev", style: arrowStyle(true), onMouseDown: (e) => { e.stopPropagation(); e.preventDefault(); go(-1); }, onClick: (e) => { e.stopPropagation(); e.preventDefault(); }, onMouseEnter: (e) => { e.currentTarget.style.backgroundColor = 'var(--seekora-carousel-btn-bg-hover, rgba(255,255,255,1))'; }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = 'var(--seekora-carousel-btn-bg, rgba(255,255,255,0.9))'; } }, "\u2039"),
9668
- React.createElement("button", { type: "button", "aria-label": "Next", className: "seekora-img-carousel-next", style: arrowStyle(false), onMouseDown: (e) => { e.stopPropagation(); e.preventDefault(); go(1); }, onClick: (e) => { e.stopPropagation(); e.preventDefault(); }, onMouseEnter: (e) => { e.currentTarget.style.backgroundColor = 'var(--seekora-carousel-btn-bg-hover, rgba(255,255,255,1))'; }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = 'var(--seekora-carousel-btn-bg, rgba(255,255,255,0.9))'; } }, "\u203A"),
10008
+ React.createElement("button", { type: "button", "aria-label": "Previous", className: "seekora-img-carousel-prev", style: arrowStyle(true, arrowSize), onMouseDown: (e) => { e.stopPropagation(); e.preventDefault(); go(-1); }, onClick: (e) => { e.stopPropagation(); e.preventDefault(); }, onMouseEnter: (e) => { e.currentTarget.style.backgroundColor = 'var(--seekora-carousel-btn-bg-hover, rgba(255,255,255,1))'; }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = 'var(--seekora-carousel-btn-bg, rgba(255,255,255,0.9))'; } }, "\u2039"),
10009
+ React.createElement("button", { type: "button", "aria-label": "Next", className: "seekora-img-carousel-next", style: arrowStyle(false, arrowSize), onMouseDown: (e) => { e.stopPropagation(); e.preventDefault(); go(1); }, onClick: (e) => { e.stopPropagation(); e.preventDefault(); }, onMouseEnter: (e) => { e.currentTarget.style.backgroundColor = 'var(--seekora-carousel-btn-bg-hover, rgba(255,255,255,1))'; }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = 'var(--seekora-carousel-btn-bg, rgba(255,255,255,0.9))'; } }, "\u203A"),
9669
10010
  showDots && (React.createElement("div", { className: "seekora-img-carousel-dots", style: {
9670
10011
  position: 'absolute',
9671
- bottom: 8,
10012
+ bottom: isMobile ? 12 : 8,
9672
10013
  left: '50%',
9673
10014
  transform: 'translateX(-50%)',
9674
10015
  display: 'flex',
9675
- gap: 6,
9676
- padding: '6px 12px',
10016
+ gap: isMobile ? 8 : 6,
10017
+ padding: isMobile ? '8px 16px' : '6px 12px',
9677
10018
  backgroundColor: 'var(--seekora-carousel-dots-bg, rgba(0,0,0,0.5))',
9678
10019
  borderRadius: 12,
9679
10020
  zIndex: 10,
9680
10021
  } }, safeImages.map((_, i) => (React.createElement("button", { key: i, type: "button", "aria-label": `Go to image ${i + 1}`, onMouseDown: (e) => { e.stopPropagation(); e.preventDefault(); }, onClick: (e) => { e.stopPropagation(); e.preventDefault(); setIndex(i); }, style: {
9681
- width: 8,
9682
- height: 8,
10022
+ width: isMobile ? 10 : 8,
10023
+ height: isMobile ? 10 : 8,
10024
+ minWidth: isMobile ? 44 : 32, // Ensure touch target
10025
+ minHeight: isMobile ? 44 : 32,
9683
10026
  borderRadius: '50%',
9684
10027
  border: 'none',
9685
10028
  padding: 0,
@@ -9691,26 +10034,28 @@ function ImageDisplay({ images, variant = 'single', alt = '', className, style,
9691
10034
  if (variant === 'thumbStrip' || variant === 'thumbList') {
9692
10035
  const thumbMainStyle = style?.aspectRatio ? { ...imgBaseStyle, aspectRatio: style.aspectRatio } : imgBaseStyle;
9693
10036
  const mainImage = enableZoom ? (React.createElement(ImageZoom, { src: current, alt: alt, mode: zoomMode, zoomLevel: zoomLevel, images: safeImages, currentIndex: index, className: "seekora-img-thumb-main", style: thumbMainStyle })) : (React.createElement("img", { src: current, alt: alt, className: "seekora-img-thumb-main", style: thumbMainStyle, loading: "lazy" }));
9694
- return (React.createElement("div", { className: clsx('seekora-img-display', 'seekora-img-thumbstrip', className), style: { display: 'flex', flexDirection: 'column', gap: 8, ...style } },
10037
+ return (React.createElement("div", { className: clsx('seekora-img-display', 'seekora-img-thumbstrip', className), style: { display: 'flex', flexDirection: 'column', gap: responsiveGap, ...style } },
9695
10038
  mainImage,
9696
- React.createElement("div", { className: "seekora-img-thumbs", style: { display: 'flex', gap: 4, overflowX: 'auto', paddingBottom: 4 } }, safeImages.map((src, i) => (React.createElement("button", { type: "button", key: i, className: clsx('seekora-img-thumb', i === index && 'seekora-img-thumb--active'), style: { flexShrink: 0, width: 48, height: 48, padding: 0, border: i === index ? '2px solid var(--seekora-primary)' : '1px solid transparent', borderRadius: 4, overflow: 'hidden', cursor: 'pointer', background: 'none' }, onMouseDown: (e) => { e.stopPropagation(); e.preventDefault(); setIndex(i); }, onClick: (e) => { e.stopPropagation(); e.preventDefault(); } },
10039
+ React.createElement("div", { className: "seekora-img-thumbs", style: { display: 'flex', gap: isMobile ? 6 : 4, overflowX: 'auto', paddingBottom: isMobile ? 6 : 4 } }, safeImages.map((src, i) => (React.createElement("button", { type: "button", key: i, className: clsx('seekora-img-thumb', i === index && 'seekora-img-thumb--active'), style: { flexShrink: 0, width: thumbSize, height: thumbSize, minWidth: thumbSize, minHeight: thumbSize, padding: 0, border: i === index ? '2px solid var(--seekora-primary)' : '1px solid transparent', borderRadius: 4, overflow: 'hidden', cursor: 'pointer', background: 'none' }, onMouseDown: (e) => { e.stopPropagation(); e.preventDefault(); setIndex(i); }, onClick: (e) => { e.stopPropagation(); e.preventDefault(); } },
9697
10040
  React.createElement("img", { src: src, alt: "", style: { width: '100%', height: '100%', objectFit: 'cover' } })))))));
9698
10041
  }
9699
10042
  return React.createElement("img", { src: current, alt: alt, className: clsx('seekora-img-display', className), style: { ...imgBaseStyle, ...style }, loading: "lazy" });
9700
10043
  }
9701
- function arrowStyle(left) {
10044
+ function arrowStyle(left, size = 32) {
9702
10045
  return {
9703
10046
  position: 'absolute',
9704
10047
  top: '50%',
9705
- [left ? 'left' : 'right']: 8,
10048
+ [left ? 'left' : 'right']: size >= 44 ? 4 : 8, // Less offset on mobile for larger buttons
9706
10049
  transform: 'translateY(-50%)',
9707
- width: 32,
9708
- height: 32,
10050
+ width: size,
10051
+ height: size,
10052
+ minWidth: size,
10053
+ minHeight: size,
9709
10054
  borderRadius: '50%',
9710
10055
  border: 'none',
9711
10056
  backgroundColor: 'var(--seekora-carousel-btn-bg, rgba(255, 255, 255, 0.9))',
9712
10057
  color: 'var(--seekora-carousel-btn-text, #111)',
9713
- fontSize: '1.25rem',
10058
+ fontSize: size >= 44 ? '1.5rem' : '1.25rem', // Larger font on mobile
9714
10059
  fontWeight: 'bold',
9715
10060
  cursor: 'pointer',
9716
10061
  display: 'flex',
@@ -9831,7 +10176,7 @@ const cardStyle$1 = {
9831
10176
  textAlign: 'left',
9832
10177
  fontSize: 'inherit',
9833
10178
  fontFamily: 'inherit',
9834
- transition: `background-color ${TRANSITIONS$1.fast}`,
10179
+ transition: `background-color ${TRANSITIONS$1.fast}, box-shadow ${TRANSITIONS$1.fast}`,
9835
10180
  };
9836
10181
  const imgStyle$1 = {
9837
10182
  width: '100%',
@@ -9847,28 +10192,51 @@ function ItemCard({ item, position, onSelect, className, style, asLink = true, i
9847
10192
  const title = item.title ?? item.primaryText ?? '';
9848
10193
  const description = item.description ?? item.secondaryText;
9849
10194
  const href = item.url;
10195
+ const isMobile = useIsMobile();
10196
+ const horizontalGap = useResponsiveGap(12);
9850
10197
  const isHorizontal = layout === 'horizontal';
9851
- const imageBlock = images.length > 0 ? (React.createElement("div", { style: { position: 'relative', overflow: 'hidden', borderRadius: 4, ...(isHorizontal ? { minWidth: 80, flexBasis: '20%', maxWidth: 120, flexShrink: 0 } : {}) } },
10198
+ const [isHovered, setIsHovered] = React.useState(false);
10199
+ // Responsive dimensions for horizontal layout
10200
+ const imageMinWidth = isMobile ? 60 : 80;
10201
+ const imageMaxWidth = isMobile ? 100 : 120;
10202
+ const imageFlex = isMobile ? '20%' : '20%';
10203
+ const imageHeight = isMobile ? 60 : 80;
10204
+ // Responsive font sizes
10205
+ const titleFontSize = isMobile ? '0.9375rem' : '0.875rem';
10206
+ const descriptionFontSize = isMobile ? '0.875rem' : '0.8125rem';
10207
+ const textGap = isMobile ? 6 : 4;
10208
+ const lineClamp = isMobile ? 3 : 2; // More lines on mobile
10209
+ // Hover style
10210
+ const cardHoverStyle = isHovered
10211
+ ? {
10212
+ backgroundColor: 'var(--seekora-card-hover-bg, var(--seekora-bg-hover, #f9fafb))',
10213
+ boxShadow: 'var(--seekora-card-hover-shadow, 0 2px 8px rgba(0, 0, 0, 0.08))',
10214
+ }
10215
+ : {};
10216
+ const imageBlock = images.length > 0 ? (React.createElement("div", { style: { position: 'relative', overflow: 'hidden', borderRadius: 4, ...(isHorizontal ? { minWidth: imageMinWidth, flexBasis: imageFlex, maxWidth: imageMaxWidth, flexShrink: 0 } : {}) } },
9852
10217
  React.createElement(ImageDisplay, { images: images, variant: imageVariant, alt: String(title), className: "seekora-item-card-image" }),
9853
- actionButtons && actionButtons.length > 0 && actionButtonsPosition?.startsWith('overlay') && (React.createElement(ActionButtons, { buttons: actionButtons, position: actionButtonsPosition === 'overlay-top-right' ? 'top-right' : 'bottom-center', showLabels: showActionLabels, size: "small" })))) : (React.createElement("div", { className: "seekora-item-card-placeholder", style: { ...imgStyle$1, ...(isHorizontal ? { minWidth: 80, flexBasis: '20%', maxWidth: 120, height: 80, flexShrink: 0 } : {}) }, "aria-hidden": true }));
9854
- const textBlock = (React.createElement("div", { style: isHorizontal ? { display: 'flex', flexDirection: 'column', gap: 4, flex: 1, minWidth: 0 } : undefined },
9855
- React.createElement("span", { className: "seekora-item-card-title", style: { fontSize: '0.875rem', fontWeight: 500, lineHeight: 1.4, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, String(title)),
9856
- description ? (React.createElement("span", { className: "seekora-item-card-description", style: { fontSize: '0.8125rem', color: 'var(--seekora-text-secondary, #6b7280)', lineHeight: 1.3, display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden' } }, String(description))) : null,
10218
+ actionButtons && actionButtons.length > 0 && actionButtonsPosition?.startsWith('overlay') && (React.createElement(ActionButtons, { buttons: actionButtons, position: actionButtonsPosition === 'overlay-top-right' ? 'top-right' : 'bottom-center', showLabels: showActionLabels, size: "small" })))) : (React.createElement("div", { className: "seekora-item-card-placeholder", style: { ...imgStyle$1, ...(isHorizontal ? { minWidth: imageMinWidth, flexBasis: imageFlex, maxWidth: imageMaxWidth, height: imageHeight, flexShrink: 0 } : {}) }, "aria-hidden": true }));
10219
+ const textBlock = (React.createElement("div", { style: isHorizontal ? { display: 'flex', flexDirection: 'column', gap: textGap, flex: 1, minWidth: 0 } : undefined },
10220
+ React.createElement("span", { className: "seekora-item-card-title", style: { fontSize: titleFontSize, fontWeight: 500, lineHeight: 1.4, overflow: 'hidden', textOverflow: 'ellipsis', display: '-webkit-box', WebkitLineClamp: isHorizontal ? 2 : 3, WebkitBoxOrient: 'vertical' } }, String(title)),
10221
+ description ? (React.createElement("span", { className: "seekora-item-card-description", style: { fontSize: descriptionFontSize, color: 'var(--seekora-text-secondary, #6b7280)', lineHeight: 1.3, display: '-webkit-box', WebkitLineClamp: lineClamp, WebkitBoxOrient: 'vertical', overflow: 'hidden' } }, String(description))) : null,
9857
10222
  actionButtons && actionButtons.length > 0 && actionButtonsPosition === 'inline' && (React.createElement(ActionButtons, { buttons: actionButtons, position: "inline", showLabels: showActionLabels, size: "small", layout: "horizontal" }))));
9858
- const content = isHorizontal ? (React.createElement("div", { style: { display: 'flex', gap: 12, alignItems: 'flex-start' } },
10223
+ const content = isHorizontal ? (React.createElement("div", { style: { display: 'flex', gap: horizontalGap, alignItems: 'flex-start' } },
9859
10224
  imageBlock,
9860
10225
  textBlock)) : (React.createElement(React.Fragment, null,
9861
10226
  imageBlock,
9862
10227
  textBlock));
9863
10228
  const commonProps = {
9864
- className: clsx('seekora-item-card', isHorizontal && 'seekora-item-card--horizontal', className),
10229
+ className: clsx('seekora-item-card', isHorizontal && 'seekora-item-card--horizontal', isHovered && 'seekora-item-card--hover', className),
9865
10230
  style: {
9866
10231
  ...cardStyle$1,
9867
10232
  ...(isHorizontal ? { flexDirection: 'row' } : {}),
10233
+ ...cardHoverStyle,
9868
10234
  ...style,
9869
10235
  },
9870
10236
  'data-position': position,
9871
10237
  onClick: onSelect,
10238
+ onMouseEnter: () => setIsHovered(true),
10239
+ onMouseLeave: () => setIsHovered(false),
9872
10240
  onMouseDown: onSelect ? (e) => { e.preventDefault(); onSelect(); } : undefined,
9873
10241
  };
9874
10242
  if (asLink && href) {
@@ -9886,11 +10254,6 @@ function ItemCard({ item, position, onSelect, className, style, asLink = true, i
9886
10254
  */
9887
10255
  const SPACING$1 = {
9888
10256
  md: 12};
9889
- const defaultGridStyle = {
9890
- display: 'grid',
9891
- gap: SPACING$1.md,
9892
- padding: SPACING$1.md,
9893
- };
9894
10257
  function toGenericItem(item, getItemId, getItemTitle, getItemImage, getItemDescription, getItemUrl) {
9895
10258
  return {
9896
10259
  id: getItemId(item),
@@ -9902,12 +10265,18 @@ function toGenericItem(item, getItemId, getItemTitle, getItemImage, getItemDescr
9902
10265
  };
9903
10266
  }
9904
10267
  function ItemGrid({ items, onItemClick, getItemId = (i) => i.id, getItemTitle = (i) => i.title, getItemImage = (i) => i.image, getItemDescription = (i) => i.description, getItemUrl = (i) => i.url, renderItem, maxItems = 24, columns = 4, className, style, gridClassName, }) {
10268
+ // Responsive columns and spacing
10269
+ const responsiveColumns = useResponsiveColumns(columns);
10270
+ const responsiveGap = useResponsiveGap(SPACING$1.md);
10271
+ const responsivePadding = useResponsivePadding(SPACING$1.md);
9905
10272
  const slice = items.slice(0, maxItems);
9906
10273
  if (slice.length === 0)
9907
10274
  return null;
9908
10275
  const gridStyle = {
9909
- ...defaultGridStyle,
9910
- gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
10276
+ display: 'grid',
10277
+ gap: responsiveGap,
10278
+ padding: responsivePadding,
10279
+ gridTemplateColumns: `repeat(${responsiveColumns}, minmax(0, 1fr))`,
9911
10280
  };
9912
10281
  return (React.createElement("div", { className: clsx('seekora-item-grid', className), style: style },
9913
10282
  React.createElement("div", { className: clsx('seekora-item-grid-inner', gridClassName), style: gridStyle }, slice.map((item, i) => {
@@ -10706,6 +11075,12 @@ const getAvailability$1 = (optionName, value, options, variants, selectedValues)
10706
11075
  };
10707
11076
  function VariantSwatches({ options, visibleOptions, maxValues = 5, colorMap, selectedValues, variants, showAvailability = true, onSwatchHover, onSwatchClick, className, style, }) {
10708
11077
  const [expandedOptions, setExpandedOptions] = useState(new Set());
11078
+ const isMobile = useIsMobile();
11079
+ // Responsive sizing
11080
+ const colorSwatchSize = isMobile ? 20 : 14; // Larger on mobile for touch
11081
+ const textSwatchPadding = isMobile ? '4px 10px' : '1px 6px';
11082
+ const textSwatchFontSize = isMobile ? '0.75rem' : '0.6875rem';
11083
+ const gap = isMobile ? 6 : 4;
10709
11084
  if (!options || options.length === 0)
10710
11085
  return null;
10711
11086
  const filtered = visibleOptions
@@ -10726,14 +11101,14 @@ function VariantSwatches({ options, visibleOptions, maxValues = 5, colorMap, sel
10726
11101
  return next;
10727
11102
  });
10728
11103
  };
10729
- return (React.createElement("div", { className: clsx('seekora-variant-swatches', className), style: { display: 'flex', flexDirection: 'column', gap: 4, ...style } }, filtered.map((option) => {
11104
+ return (React.createElement("div", { className: clsx('seekora-variant-swatches', className), style: { display: 'flex', flexDirection: 'column', gap, ...style } }, filtered.map((option) => {
10730
11105
  const isColor = isColorOption$1(option.name);
10731
11106
  const isExpanded = expandedOptions.has(option.name);
10732
11107
  const visible = isExpanded ? option.values : option.values.slice(0, maxValues);
10733
11108
  const overflow = option.values.length - maxValues;
10734
11109
  const hasOverflow = overflow > 0;
10735
11110
  const selectedValue = selectedValues?.[option.name];
10736
- return (React.createElement("div", { key: option.name, className: "seekora-variant-swatch-group", style: { display: 'flex', alignItems: 'center', gap: 4, flexWrap: 'wrap' } },
11111
+ return (React.createElement("div", { key: option.name, className: "seekora-variant-swatch-group", style: { display: 'flex', alignItems: 'center', gap, flexWrap: 'wrap' } },
10737
11112
  visible.map((value) => {
10738
11113
  const color = isColor ? resolveColor$1(value, colorMap) : null;
10739
11114
  const isSelected = selectedValue === value;
@@ -10742,9 +11117,13 @@ function VariantSwatches({ options, visibleOptions, maxValues = 5, colorMap, sel
10742
11117
  : true;
10743
11118
  if (color) {
10744
11119
  return (React.createElement("span", { key: value, className: clsx('seekora-variant-swatch', 'seekora-variant-swatch--color', isSelected && 'seekora-variant-swatch--selected', !isAvailable && 'seekora-variant-swatch--unavailable'), title: `${value}${!isAvailable ? ' (Unavailable)' : ''}`, style: {
10745
- display: 'inline-block',
10746
- width: 14,
10747
- height: 14,
11120
+ display: 'inline-flex',
11121
+ alignItems: 'center',
11122
+ justifyContent: 'center',
11123
+ width: colorSwatchSize,
11124
+ height: colorSwatchSize,
11125
+ minWidth: colorSwatchSize,
11126
+ minHeight: colorSwatchSize,
10748
11127
  borderRadius: '50%',
10749
11128
  backgroundColor: color,
10750
11129
  border: isSelected
@@ -10776,9 +11155,12 @@ function VariantSwatches({ options, visibleOptions, maxValues = 5, colorMap, sel
10776
11155
  } }))));
10777
11156
  }
10778
11157
  return (React.createElement("span", { key: value, className: clsx('seekora-variant-swatch', 'seekora-variant-swatch--text', isSelected && 'seekora-variant-swatch--selected', !isAvailable && 'seekora-variant-swatch--unavailable'), style: {
10779
- display: 'inline-block',
10780
- padding: '1px 6px',
10781
- fontSize: '0.6875rem',
11158
+ display: 'inline-flex',
11159
+ alignItems: 'center',
11160
+ justifyContent: 'center',
11161
+ padding: textSwatchPadding,
11162
+ minHeight: isMobile ? 32 : 24, // Minimum touch target on mobile
11163
+ fontSize: textSwatchFontSize,
10782
11164
  borderRadius: 3,
10783
11165
  border: isSelected
10784
11166
  ? '1px solid var(--seekora-primary, #111827)'
@@ -10805,11 +11187,12 @@ function VariantSwatches({ options, visibleOptions, maxValues = 5, colorMap, sel
10805
11187
  } }, value));
10806
11188
  }),
10807
11189
  hasOverflow && (React.createElement("button", { type: "button", className: "seekora-variant-swatch--overflow", onClick: (e) => toggleExpanded(option.name, e), style: {
10808
- fontSize: '0.6875rem',
11190
+ fontSize: textSwatchFontSize,
10809
11191
  color: 'var(--seekora-primary, #2563eb)',
10810
11192
  background: 'none',
10811
11193
  border: 'none',
10812
- padding: 0,
11194
+ padding: isMobile ? '4px 8px' : 0,
11195
+ minHeight: isMobile ? 32 : 'auto', // Touch target on mobile
10813
11196
  cursor: 'pointer',
10814
11197
  textDecoration: 'underline',
10815
11198
  fontWeight: 500,
@@ -10844,16 +11227,24 @@ function ImageBlock({ images, title, imageVariant, aspectRatio, enableZoom, zoom
10844
11227
  }
10845
11228
  /** minimal: image, title, price (current default behavior) */
10846
11229
  function MinimalLayout({ images, title, price, product, imageVariant, displayConfig, enableImageZoom, imageZoomMode, imageZoomLevel }) {
11230
+ const isMobile = useIsMobile();
11231
+ const titleFontSize = isMobile ? '0.9375rem' : '0.875rem'; // Slightly larger on mobile
11232
+ const priceFontSize = isMobile ? '0.9375rem' : '0.875rem';
10847
11233
  return (React.createElement(React.Fragment, null,
10848
11234
  React.createElement(ImageBlock, { images: images, title: title, imageVariant: imageVariant, aspectRatio: displayConfig.imageAspectRatio, enableZoom: enableImageZoom, zoomMode: imageZoomMode, zoomLevel: imageZoomLevel }),
10849
- React.createElement("span", { className: "seekora-product-card__title", style: { fontSize: '0.875rem', fontWeight: 500, lineHeight: 1.4, overflow: 'hidden', textOverflow: 'ellipsis' } }, title),
10850
- price != null && !Number.isNaN(price) && (React.createElement("span", { className: "seekora-product-card__price seekora-suggestions-product-card-price", style: { fontSize: '0.875rem', color: 'var(--seekora-text-secondary, #6b7280)' } },
11235
+ React.createElement("span", { className: "seekora-product-card__title", style: { fontSize: titleFontSize, fontWeight: 500, lineHeight: 1.4, overflow: 'hidden', textOverflow: 'ellipsis', display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical' } }, title),
11236
+ price != null && !Number.isNaN(price) && (React.createElement("span", { className: "seekora-product-card__price seekora-suggestions-product-card-price", style: { fontSize: priceFontSize, color: 'var(--seekora-text-secondary, #6b7280)' } },
10851
11237
  product.currency ?? '$',
10852
11238
  price.toFixed(2)))));
10853
11239
  }
10854
11240
  /** standard: image, badges, brand, title, price + compare price, color swatches */
10855
11241
  function StandardLayout({ images, title, price, comparePrice, brand, badges, options, variants, product, imageVariant, displayConfig, onVariantHover, onVariantClick, selectedVariants, actionButtons, actionButtonsPosition, showActionLabels, enableImageZoom, imageZoomMode, imageZoomLevel, }) {
10856
11242
  const cfg = displayConfig;
11243
+ const isMobile = useIsMobile();
11244
+ const bodyGap = isMobile ? 6 : 4; // More gap on mobile
11245
+ const titleFontSize = isMobile ? '0.9375rem' : '0.875rem';
11246
+ const brandFontSize = isMobile ? '0.8125rem' : '0.75rem';
11247
+ const priceFontSize = isMobile ? '0.9375rem' : '0.875rem';
10857
11248
  // Normalize position: 'overlay-*' positions render over image, everything else renders inline
10858
11249
  const isOverlayPosition = actionButtonsPosition?.startsWith('overlay');
10859
11250
  return (React.createElement(React.Fragment, null,
@@ -10861,11 +11252,11 @@ function StandardLayout({ images, title, price, comparePrice, brand, badges, opt
10861
11252
  React.createElement(ImageBlock, { images: images, title: title, imageVariant: imageVariant, aspectRatio: cfg.imageAspectRatio, enableZoom: enableImageZoom, zoomMode: imageZoomMode, zoomLevel: imageZoomLevel }),
10862
11253
  cfg.showBadges !== false && badges.length > 0 && (React.createElement(BadgeList, { badges: badges, position: "top-left", maxBadges: 2 })),
10863
11254
  actionButtons && actionButtons.length > 0 && isOverlayPosition && (React.createElement(ActionButtons, { buttons: actionButtons, position: actionButtonsPosition === 'overlay-top-right' ? 'top-right' : 'bottom-center', showLabels: showActionLabels, size: "small" }))),
10864
- React.createElement("div", { className: "seekora-product-card__body", style: { display: 'flex', flexDirection: 'column', gap: 4 } },
10865
- cfg.showBrand !== false && brand && (React.createElement("span", { className: "seekora-product-card__brand", style: { fontSize: '0.75rem', color: 'var(--seekora-text-secondary, #6b7280)', textTransform: 'uppercase', letterSpacing: '0.02em' } }, brand)),
10866
- React.createElement("span", { className: "seekora-product-card__title", style: { fontSize: '0.875rem', fontWeight: 500, lineHeight: 1.4, overflow: 'hidden', textOverflow: 'ellipsis' } }, title),
11255
+ React.createElement("div", { className: "seekora-product-card__body", style: { display: 'flex', flexDirection: 'column', gap: bodyGap } },
11256
+ cfg.showBrand !== false && brand && (React.createElement("span", { className: "seekora-product-card__brand", style: { fontSize: brandFontSize, color: 'var(--seekora-text-secondary, #6b7280)', textTransform: 'uppercase', letterSpacing: '0.02em' } }, brand)),
11257
+ React.createElement("span", { className: "seekora-product-card__title", style: { fontSize: titleFontSize, fontWeight: 500, lineHeight: 1.4, overflow: 'hidden', textOverflow: 'ellipsis', display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical' } }, title),
10867
11258
  React.createElement("div", { className: "seekora-product-card__price" },
10868
- React.createElement(PriceDisplay, { price: price ?? undefined, comparePrice: comparePrice ?? undefined, currency: cfg.currency ?? product.currency, currencyPosition: cfg.currencyPosition, priceDecimals: cfg.priceDecimals, showDiscount: cfg.showDiscount, style: { fontSize: '0.875rem' } })),
11259
+ React.createElement(PriceDisplay, { price: price ?? undefined, comparePrice: comparePrice ?? undefined, currency: cfg.currency ?? product.currency, currencyPosition: cfg.currencyPosition, priceDecimals: cfg.priceDecimals, showDiscount: cfg.showDiscount, style: { fontSize: priceFontSize } })),
10869
11260
  cfg.showVariants !== false && options.length > 0 && (React.createElement(VariantSwatches, { options: options, visibleOptions: cfg.variantOptionsToShow, maxValues: cfg.maxVariantValues, selectedValues: selectedVariants, variants: variants, onSwatchHover: onVariantHover, onSwatchClick: onVariantClick })),
10870
11261
  actionButtons && actionButtons.length > 0 && !isOverlayPosition && (React.createElement(ActionButtons, { buttons: actionButtons, position: "inline", showLabels: showActionLabels, size: "small", layout: "horizontal" })))));
10871
11262
  }
@@ -10910,16 +11301,27 @@ function CompactLayout({ images, title, price, product, imageVariant, displayCon
10910
11301
  /** horizontal: image left + content right (title, brand, price, swatches) */
10911
11302
  function HorizontalLayout({ images, title, price, comparePrice, brand, badges, options, variants, product, imageVariant, displayConfig, onVariantHover, onVariantClick, selectedVariants, actionButtons, actionButtonsPosition, showActionLabels, enableImageZoom, imageZoomMode, imageZoomLevel, }) {
10912
11303
  const cfg = displayConfig;
10913
- return (React.createElement("div", { style: { display: 'flex', gap: 12, alignItems: 'flex-start' } },
10914
- React.createElement("div", { style: { position: 'relative', minWidth: 80, flexBasis: '25%', maxWidth: 120, flexShrink: 0 } },
11304
+ const isMobile = useIsMobile();
11305
+ const containerGap = useResponsiveGap(12);
11306
+ const bodyGap = isMobile ? 6 : 4;
11307
+ // Responsive image dimensions
11308
+ const imageMinWidth = isMobile ? 60 : 80;
11309
+ const imageMaxWidth = isMobile ? 100 : 120;
11310
+ const imageFlex = isMobile ? '20%' : '25%';
11311
+ // Responsive font sizes
11312
+ const brandFontSize = isMobile ? '0.6875rem' : '0.75rem';
11313
+ const titleFontSize = isMobile ? '0.875rem' : '0.875rem';
11314
+ const priceFontSize = isMobile ? '0.875rem' : '0.875rem';
11315
+ return (React.createElement("div", { style: { display: 'flex', gap: containerGap, alignItems: 'flex-start' } },
11316
+ React.createElement("div", { style: { position: 'relative', minWidth: imageMinWidth, flexBasis: imageFlex, maxWidth: imageMaxWidth, flexShrink: 0 } },
10915
11317
  React.createElement(ImageBlock, { images: images, title: title, imageVariant: imageVariant, aspectRatio: "1:1", enableZoom: enableImageZoom, zoomMode: imageZoomMode, zoomLevel: imageZoomLevel }),
10916
11318
  cfg.showBadges !== false && badges.length > 0 && (React.createElement(BadgeList, { badges: badges, position: "top-left", maxBadges: 1 })),
10917
11319
  actionButtons && actionButtons.length > 0 && actionButtonsPosition?.startsWith('overlay') && (React.createElement(ActionButtons, { buttons: actionButtons, position: actionButtonsPosition === 'overlay-top-right' ? 'top-right' : 'bottom-center', showLabels: showActionLabels, size: "small" }))),
10918
- React.createElement("div", { className: "seekora-product-card__body", style: { display: 'flex', flexDirection: 'column', gap: 4, flex: 1, minWidth: 0 } },
10919
- cfg.showBrand !== false && brand && (React.createElement("span", { className: "seekora-product-card__brand", style: { fontSize: '0.75rem', color: 'var(--seekora-text-secondary, #6b7280)', textTransform: 'uppercase' } }, brand)),
10920
- React.createElement("span", { className: "seekora-product-card__title", style: { fontSize: '0.875rem', fontWeight: 500, lineHeight: 1.4, overflow: 'hidden', textOverflow: 'ellipsis' } }, title),
11320
+ React.createElement("div", { className: "seekora-product-card__body", style: { display: 'flex', flexDirection: 'column', gap: bodyGap, flex: 1, minWidth: 0 } },
11321
+ cfg.showBrand !== false && brand && (React.createElement("span", { className: "seekora-product-card__brand", style: { fontSize: brandFontSize, color: 'var(--seekora-text-secondary, #6b7280)', textTransform: 'uppercase' } }, brand)),
11322
+ React.createElement("span", { className: "seekora-product-card__title", style: { fontSize: titleFontSize, fontWeight: 500, lineHeight: 1.4, overflow: 'hidden', textOverflow: 'ellipsis', display: '-webkit-box', WebkitLineClamp: isMobile ? 3 : 2, WebkitBoxOrient: 'vertical' } }, title),
10921
11323
  React.createElement("div", { className: "seekora-product-card__price" },
10922
- React.createElement(PriceDisplay, { price: price ?? undefined, comparePrice: comparePrice ?? undefined, currency: cfg.currency ?? product.currency, currencyPosition: cfg.currencyPosition, priceDecimals: cfg.priceDecimals, showDiscount: cfg.showDiscount, style: { fontSize: '0.875rem' } })),
11324
+ React.createElement(PriceDisplay, { price: price ?? undefined, comparePrice: comparePrice ?? undefined, currency: cfg.currency ?? product.currency, currencyPosition: cfg.currencyPosition, priceDecimals: cfg.priceDecimals, showDiscount: cfg.showDiscount, style: { fontSize: priceFontSize } })),
10923
11325
  cfg.showVariants !== false && options.length > 0 && (React.createElement(VariantSwatches, { options: options, visibleOptions: cfg.variantOptionsToShow, maxValues: cfg.maxVariantValues ?? 3, selectedValues: selectedVariants, variants: variants, onSwatchHover: onVariantHover, onSwatchClick: onVariantClick })),
10924
11326
  actionButtons && actionButtons.length > 0 && actionButtonsPosition === 'inline' && (React.createElement(ActionButtons, { buttons: actionButtons, position: "inline", showLabels: showActionLabels, size: "small", layout: "horizontal" })))));
10925
11327
  }
@@ -10945,7 +11347,7 @@ const cardStyle = {
10945
11347
  borderRadius: `var(--seekora-border-radius, ${BORDER_RADIUS.md}px)`,
10946
11348
  backgroundColor: 'transparent',
10947
11349
  textAlign: 'left',
10948
- transition: `background-color ${TRANSITIONS.fast}`,
11350
+ transition: `background-color ${TRANSITIONS.fast}, box-shadow ${TRANSITIONS.fast}`,
10949
11351
  };
10950
11352
  const imgStyle = {
10951
11353
  width: '100%',
@@ -10997,9 +11399,17 @@ function ProductCard({ product, position, section, tabId, onSelect, className, s
10997
11399
  const images = effectiveImages;
10998
11400
  const title = effectiveTitle;
10999
11401
  const price = effectivePrice;
11402
+ // Hover state for card
11403
+ const [isHovered, setIsHovered] = React.useState(false);
11404
+ const cardHoverStyle = isHovered
11405
+ ? {
11406
+ backgroundColor: 'var(--seekora-card-hover-bg, var(--seekora-bg-hover, #f9fafb))',
11407
+ boxShadow: 'var(--seekora-card-hover-shadow, 0 2px 8px rgba(0, 0, 0, 0.08))',
11408
+ }
11409
+ : {};
11000
11410
  // If no displayConfig, render original minimal layout (backwards compat)
11001
11411
  if (!displayConfig) {
11002
- return (React.createElement("div", { role: "button", tabIndex: 0, className: clsx('seekora-suggestions-product-card', className), style: { ...cardStyle, ...style }, onClick: () => onSelect(), onKeyDown: (e) => {
11412
+ return (React.createElement("div", { role: "button", tabIndex: 0, className: clsx('seekora-suggestions-product-card', isHovered && 'seekora-suggestions-product-card--hover', className), style: { ...cardStyle, ...cardHoverStyle, ...style }, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), onClick: () => onSelect(), onKeyDown: (e) => {
11003
11413
  if (e.key === 'Enter' || e.key === ' ') {
11004
11414
  e.preventDefault();
11005
11415
  onSelect();
@@ -11060,20 +11470,21 @@ function ProductCard({ product, position, section, tabId, onSelect, className, s
11060
11470
  horizontal: HorizontalLayout,
11061
11471
  };
11062
11472
  const LayoutComponent = layoutMap[layoutStyle] ?? MinimalLayout;
11063
- const rootClassName = clsx('seekora-product-card', `seekora-product-card--${layoutStyle}`, 'seekora-suggestions-product-card', className);
11473
+ const rootClassName = clsx('seekora-product-card', `seekora-product-card--${layoutStyle}`, 'seekora-suggestions-product-card', isHovered && 'seekora-product-card--hover', className);
11064
11474
  const rootStyle = {
11065
11475
  ...cardStyle,
11066
11476
  ...(layoutStyle === 'horizontal' ? { flexDirection: 'row' } : {}),
11477
+ ...cardHoverStyle,
11067
11478
  ...style,
11068
11479
  };
11069
11480
  if (asLink && product.url) {
11070
- return (React.createElement("a", { href: product.url, className: rootClassName, style: { ...rootStyle, textDecoration: 'none', color: 'inherit' }, onClick: (e) => {
11481
+ return (React.createElement("a", { href: product.url, className: rootClassName, style: { ...rootStyle, textDecoration: 'none', color: 'inherit' }, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), onClick: (e) => {
11071
11482
  e.preventDefault();
11072
11483
  onSelect();
11073
11484
  }, "data-position": position, "data-section": section, "data-tab-id": tabId },
11074
11485
  React.createElement(LayoutComponent, { ...layoutProps })));
11075
11486
  }
11076
- return (React.createElement("div", { role: "button", tabIndex: 0, className: rootClassName, style: rootStyle, onClick: () => onSelect(), onKeyDown: (e) => {
11487
+ return (React.createElement("div", { role: "button", tabIndex: 0, className: rootClassName, style: rootStyle, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), onClick: () => onSelect(), onKeyDown: (e) => {
11077
11488
  if (e.key === 'Enter' || e.key === ' ') {
11078
11489
  e.preventDefault();
11079
11490
  onSelect();
@@ -11089,6 +11500,10 @@ function ProductCard({ product, position, section, tabId, onSelect, className, s
11089
11500
  */
11090
11501
  function ProductGrid({ maxItems = 8, source = 'trending', columns = 4, className, style, gridClassName, displayConfig, onVariantHover, }) {
11091
11502
  const { trendingProducts, filteredTabs, activeTabId, selectProduct, getAllNavigableItems, } = useSuggestionsContext();
11503
+ // Responsive columns and spacing
11504
+ const responsiveColumns = useResponsiveColumns(columns);
11505
+ const responsiveGap = useResponsiveGap(12);
11506
+ const responsivePadding = useResponsivePadding(12);
11092
11507
  const products = useMemo(() => {
11093
11508
  if (source === 'trending')
11094
11509
  return trendingProducts;
@@ -11102,9 +11517,9 @@ function ProductGrid({ maxItems = 8, source = 'trending', columns = 4, className
11102
11517
  return null;
11103
11518
  const gridStyle = {
11104
11519
  display: 'grid',
11105
- gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
11106
- gap: 12,
11107
- padding: 12,
11520
+ gridTemplateColumns: `repeat(${responsiveColumns}, minmax(0, 1fr))`,
11521
+ gap: responsiveGap,
11522
+ padding: responsivePadding,
11108
11523
  };
11109
11524
  return (React.createElement("div", { className: clsx('seekora-suggestions-product-grid', className), style: style },
11110
11525
  React.createElement("div", { className: clsx('seekora-suggestions-product-grid-inner', gridClassName), style: gridStyle }, items.map((product, i) => {
@@ -11165,6 +11580,17 @@ const itemStyle$1 = {
11165
11580
  color: 'var(--seekora-text-primary, #111827)',
11166
11581
  transition: 'background-color 120ms ease',
11167
11582
  };
11583
+ function RecentSearchItem({ search, onSelect }) {
11584
+ const [isHovered, setIsHovered] = React.useState(false);
11585
+ return (React.createElement("li", null,
11586
+ React.createElement("button", { type: "button", className: clsx('seekora-suggestions-recent-item', isHovered && 'seekora-suggestions-recent-item--hover'), style: {
11587
+ ...itemStyle$1,
11588
+ ...(isHovered ? { backgroundColor: 'var(--seekora-bg-hover, #f3f4f6)' } : {}),
11589
+ }, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), onMouseDown: (e) => {
11590
+ e.preventDefault();
11591
+ onSelect();
11592
+ } }, search.query)));
11593
+ }
11168
11594
  function RecentSearchesList({ title = 'Recent', maxItems = 8, className, style, listClassName, renderItem, }) {
11169
11595
  const { recentSearches, query, selectRecentSearch } = useSuggestionsContext();
11170
11596
  const items = recentSearches.slice(0, maxItems);
@@ -11177,11 +11603,7 @@ function RecentSearchesList({ title = 'Recent', maxItems = 8, className, style,
11177
11603
  if (renderItem) {
11178
11604
  return React.createElement("li", { key: `${search.query}-${search.timestamp}` }, renderItem(search, i, onSelect));
11179
11605
  }
11180
- return (React.createElement("li", { key: `${search.query}-${search.timestamp}` },
11181
- React.createElement("button", { type: "button", className: "seekora-suggestions-recent-item", style: itemStyle$1, onMouseDown: (e) => {
11182
- e.preventDefault();
11183
- onSelect();
11184
- } }, search.query)));
11606
+ return (React.createElement(RecentSearchItem, { key: `${search.query}-${search.timestamp}`, search: search, onSelect: onSelect }));
11185
11607
  }))));
11186
11608
  }
11187
11609
 
@@ -11202,6 +11624,19 @@ const itemStyle = {
11202
11624
  color: 'var(--seekora-text-primary, #111827)',
11203
11625
  transition: 'background-color 120ms ease',
11204
11626
  };
11627
+ function TrendingItem({ trending, onSelect }) {
11628
+ const [isHovered, setIsHovered] = React.useState(false);
11629
+ return (React.createElement("li", null,
11630
+ React.createElement("button", { type: "button", className: clsx('seekora-suggestions-trending-item', isHovered && 'seekora-suggestions-trending-item--hover'), style: {
11631
+ ...itemStyle,
11632
+ ...(isHovered ? { backgroundColor: 'var(--seekora-bg-hover, #f3f4f6)' } : {}),
11633
+ }, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), onMouseDown: (e) => {
11634
+ e.preventDefault();
11635
+ onSelect();
11636
+ } },
11637
+ trending.query,
11638
+ trending.count != null ? (React.createElement("span", { style: { marginLeft: 8, color: 'var(--seekora-text-secondary, #6b7280)', fontSize: '0.875em' } }, trending.count)) : null)));
11639
+ }
11205
11640
  function TrendingList({ title = 'Trending', maxItems = 8, className, style, listClassName, renderItem, }) {
11206
11641
  const { trendingSearches, selectTrendingSearch } = useSuggestionsContext();
11207
11642
  const items = trendingSearches.slice(0, maxItems);
@@ -11214,31 +11649,10 @@ function TrendingList({ title = 'Trending', maxItems = 8, className, style, list
11214
11649
  if (renderItem) {
11215
11650
  return React.createElement("li", { key: `${trending.query}-${i}` }, renderItem(trending, i, onSelect));
11216
11651
  }
11217
- return (React.createElement("li", { key: `${trending.query}-${i}` },
11218
- React.createElement("button", { type: "button", className: "seekora-suggestions-trending-item", style: itemStyle, onMouseDown: (e) => {
11219
- e.preventDefault();
11220
- onSelect();
11221
- } },
11222
- trending.query,
11223
- trending.count != null ? (React.createElement("span", { style: { marginLeft: 8, color: 'var(--seekora-text-secondary, #6b7280)', fontSize: '0.875em' } }, trending.count)) : null)));
11652
+ return (React.createElement(TrendingItem, { key: `${trending.query}-${i}`, trending: trending, onSelect: onSelect }));
11224
11653
  }))));
11225
11654
  }
11226
11655
 
11227
- /**
11228
- * SuggestionsLoading – loading indicator (primitive)
11229
- */
11230
- function SuggestionsLoading({ className, style, text = 'Loading...' }) {
11231
- const { loading } = useSuggestionsContext();
11232
- if (!loading)
11233
- return null;
11234
- return (React.createElement("div", { className: clsx('seekora-suggestions-loading', className), style: {
11235
- padding: 16,
11236
- color: 'var(--seekora-text-secondary, #6b7280)',
11237
- fontSize: '0.875rem',
11238
- ...style,
11239
- } }, text));
11240
- }
11241
-
11242
11656
  /**
11243
11657
  * SuggestionsError – error message (primitive)
11244
11658
  */
@@ -11716,16 +12130,6 @@ function SectionSearchProvider({ children, query, refinements = [], maxItems = 1
11716
12130
  return React.createElement(SectionSearchContext.Provider, { value: value }, children);
11717
12131
  }
11718
12132
 
11719
- /**
11720
- * SectionLoading – loading state for section (primitive)
11721
- */
11722
- function SectionLoading({ className, style, text = 'Loading...' }) {
11723
- const { loading } = useSectionSearchContext();
11724
- if (!loading)
11725
- return null;
11726
- return (React.createElement("div", { className: className, style: { padding: 16, color: 'var(--seekora-text-secondary)', fontSize: '0.875rem', ...style } }, text));
11727
- }
11728
-
11729
12133
  /**
11730
12134
  * SectionError – error state for section (primitive)
11731
12135
  */
@@ -11741,10 +12145,8 @@ function SectionError({ className, style, render }) {
11741
12145
  /**
11742
12146
  * SectionItemGrid – generic grid of items from SectionSearchProvider (primitive)
11743
12147
  */
11744
- function SectionItemGrid({ columns = 4, maxItems = 12, className, style, showLoadingState = false, getItemId = (i) => i.id ?? String(i?.objectID ?? ''), getItemTitle = (i) => i.title ?? i?.title ?? '', getItemImage = (i) => i.image ?? i?.image, getItemDescription = (i) => i.description ?? i?.description, getItemUrl = (i) => i.url ?? i?.url, renderItem, }) {
12148
+ function SectionItemGrid({ columns = 4, maxItems = 12, className, style, getItemId = (i) => i.id ?? String(i?.objectID ?? ''), getItemTitle = (i) => i.title ?? i?.title ?? '', getItemImage = (i) => i.image ?? i?.image, getItemDescription = (i) => i.description ?? i?.description, getItemUrl = (i) => i.url ?? i?.url, renderItem, }) {
11745
12149
  const { items, loading, error, trackClick } = useSectionSearchContext();
11746
- if (loading && items.length === 0 && showLoadingState)
11747
- return React.createElement(SectionLoading, { className: className, style: style });
11748
12150
  if (error)
11749
12151
  return React.createElement(SectionError, { className: className, style: style });
11750
12152
  // When loading with previous items, show them (no loading screen)
@@ -12405,22 +12807,6 @@ const createStyles$7 = (theme = {}, isMobile = false) => ({
12405
12807
  fontWeight: 700,
12406
12808
  color: 'var(--seekora-text-primary, #0f1111)',
12407
12809
  },
12408
- loading: {
12409
- display: 'flex',
12410
- alignItems: 'center',
12411
- justifyContent: 'center',
12412
- padding: '40px',
12413
- color: 'var(--seekora-text-secondary, #565959)',
12414
- gap: '10px',
12415
- },
12416
- spinner: {
12417
- width: '20px',
12418
- height: '20px',
12419
- border: '2px solid var(--seekora-border-color, #ddd)',
12420
- borderTopColor: 'var(--seekora-primary, #f90)',
12421
- borderRadius: '50%',
12422
- animation: 'seekora-spin 0.8s linear infinite',
12423
- },
12424
12810
  empty: {
12425
12811
  padding: '40px 20px',
12426
12812
  textAlign: 'center',
@@ -12484,7 +12870,7 @@ const RatingStars = ({ rating, count }) => {
12484
12870
  ")"))));
12485
12871
  };
12486
12872
  const AmazonDropdown = forwardRef(function AmazonDropdown(props, ref) {
12487
- const { query, isOpen = true, loading = false, suggestions = [], products = [], categories = [], recentSearches = [], trendingSearches = [], filteredTabs = [], activeTab = 'all', suggestionFields = { query: 'query' }, productFields = { id: 'id', title: 'title' }, categoryFields = { id: 'id', label: 'label' }, productDisplay = {}, theme = {}, showDepartments = true, currentDepartment, departments = [], showPrime = false, width = '700px', maxHeight = '500px', zIndex = 1000, className, style, classNames = {}, onSuggestionSelect, onProductClick, onCategoryClick, onTabChange, onRecentClick, onRecentRemove, onRecentClearAll, onViewAll, onClose, header, footer, showLoadingState = false, renderLoading, renderEmpty, } = props;
12873
+ const { query, isOpen = true, loading = false, suggestions = [], products = [], categories = [], recentSearches = [], trendingSearches = [], filteredTabs = [], activeTab = 'all', suggestionFields = { query: 'query' }, productFields = { id: 'id', title: 'title' }, categoryFields = { id: 'id', label: 'label' }, productDisplay = {}, theme = {}, showDepartments = true, currentDepartment, departments = [], showPrime = false, width = '700px', maxHeight = '500px', zIndex = 1000, className, style, classNames = {}, onSuggestionSelect, onProductClick, onCategoryClick, onTabChange, onRecentClick, onRecentRemove, onRecentClearAll, onViewAll, onClose, header, footer, renderEmpty, } = props;
12488
12874
  // Inject global responsive styles
12489
12875
  useInjectResponsiveStyles();
12490
12876
  // Responsive state
@@ -12581,9 +12967,7 @@ const AmazonDropdown = forwardRef(function AmazonDropdown(props, ref) {
12581
12967
  }
12582
12968
  `),
12583
12969
  header,
12584
- (loading && showLoadingState) ? (renderLoading ? renderLoading() : (React.createElement("div", { style: styles.loading },
12585
- React.createElement("div", { style: styles.spinner }),
12586
- React.createElement("span", null, "Searching...")))) : (React.createElement(React.Fragment, null,
12970
+ React.createElement(React.Fragment, null,
12587
12971
  filteredTabs.length > 0 && (React.createElement("div", { style: {
12588
12972
  display: 'flex',
12589
12973
  gap: '8px',
@@ -12688,7 +13072,7 @@ const AmazonDropdown = forwardRef(function AmazonDropdown(props, ref) {
12688
13072
  currency: '',
12689
13073
  priceDecimals: productDisplay.priceDecimals
12690
13074
  }))),
12691
- product.rating && (React.createElement(RatingStars, { rating: parseFloat(String(product.rating)), count: product.reviewCount ? parseInt(String(product.reviewCount)) : undefined }))))))))))),
13075
+ product.rating && (React.createElement(RatingStars, { rating: parseFloat(String(product.rating)), count: product.reviewCount ? parseInt(String(product.reviewCount)) : undefined })))))))))),
12692
13076
  footer !== undefined ? footer : (React.createElement("div", { style: styles.footer },
12693
13077
  React.createElement("div", { style: styles.keyboard },
12694
13078
  React.createElement("span", { style: styles.key }, "\u2191"),
@@ -12878,22 +13262,6 @@ const createStyles$6 = (isMobile = false) => ({
12878
13262
  backgroundColor: 'var(--seekora-bg-hover, #f1f3f4)',
12879
13263
  color: 'var(--seekora-text-primary, #202124)',
12880
13264
  },
12881
- loading: {
12882
- display: 'flex',
12883
- alignItems: 'center',
12884
- justifyContent: 'center',
12885
- padding: '24px',
12886
- color: 'var(--seekora-text-secondary, #5f6368)',
12887
- gap: '12px',
12888
- },
12889
- spinner: {
12890
- width: '24px',
12891
- height: '24px',
12892
- border: '3px solid var(--seekora-border-color, #e8eaed)',
12893
- borderTopColor: 'var(--seekora-primary, #4285f4)',
12894
- borderRadius: '50%',
12895
- animation: 'seekora-spin 0.8s linear infinite',
12896
- },
12897
13265
  empty: {
12898
13266
  padding: '24px',
12899
13267
  textAlign: 'center',
@@ -12913,7 +13281,7 @@ const TrendingIcon$2 = () => (React.createElement("svg", { width: "20", height:
12913
13281
  const ArrowIcon$1 = () => (React.createElement("svg", { viewBox: "0 0 24 24", fill: "none", width: "20", height: "20" },
12914
13282
  React.createElement("path", { d: "M9 5l-4 4 4 4", stroke: "currentColor", strokeWidth: "2", fill: "none", transform: "rotate(-90 12 12)" })));
12915
13283
  const GoogleDropdown = forwardRef(function GoogleDropdown(props, ref) {
12916
- const { query, isOpen = true, loading = false, suggestions = [], recentSearches = [], trendingSearches = [], suggestionFields = { query: 'query' }, theme = {}, showFeelingLucky = true, feelingLuckyText = "I'm Feeling Lucky", showRemoveRecent = true, showVoiceSearch = false, showTrendingIndicator = true, width = '100%', maxHeight = '400px', zIndex = 1000, className, style, classNames = {}, onSuggestionSelect, onRecentClick, onRecentRemove, onVoiceSearch, onFeelingLucky, onClose, onSearchSubmit, header, footer, showLoadingState = false, renderLoading, renderEmpty, } = props;
13284
+ const { query, isOpen = true, loading = false, suggestions = [], recentSearches = [], trendingSearches = [], suggestionFields = { query: 'query' }, theme = {}, showFeelingLucky = true, feelingLuckyText = "I'm Feeling Lucky", showRemoveRecent = true, showVoiceSearch = false, showTrendingIndicator = true, width = '100%', maxHeight = '400px', zIndex = 1000, className, style, classNames = {}, onSuggestionSelect, onRecentClick, onRecentRemove, onVoiceSearch, onFeelingLucky, onClose, onSearchSubmit, header, footer, renderEmpty, } = props;
12917
13285
  // Inject global responsive styles
12918
13286
  useInjectResponsiveStyles();
12919
13287
  // Responsive state
@@ -13028,8 +13396,7 @@ const GoogleDropdown = forwardRef(function GoogleDropdown(props, ref) {
13028
13396
  }
13029
13397
  `),
13030
13398
  header,
13031
- (loading && showLoadingState) ? (renderLoading ? renderLoading() : (React.createElement("div", { style: styles.loading },
13032
- React.createElement("div", { style: styles.spinner })))) : (React.createElement(React.Fragment, null,
13399
+ React.createElement(React.Fragment, null,
13033
13400
  React.createElement("ul", { style: styles.list },
13034
13401
  showRecent && (React.createElement(React.Fragment, null,
13035
13402
  recentQueries.map((q, idx) => {
@@ -13059,7 +13426,7 @@ const GoogleDropdown = forwardRef(function GoogleDropdown(props, ref) {
13059
13426
  "\"")))),
13060
13427
  (showFeelingLucky || showVoiceSearch) && (React.createElement("div", { style: styles.footer }, showFeelingLucky && (React.createElement(React.Fragment, null,
13061
13428
  React.createElement("button", { style: mergeStyles(styles.footerButton, hoveredBtn === 'search' ? styles.footerButtonHover : undefined), onMouseEnter: () => setHoveredBtn('search'), onMouseLeave: () => setHoveredBtn(null), onClick: () => onSearchSubmit?.(query || (activeIndex >= 0 ? allItems[activeIndex]?.query : '')) }, "Search"),
13062
- React.createElement("button", { style: mergeStyles(styles.footerButton, hoveredBtn === 'lucky' ? styles.footerButtonHover : undefined), onMouseEnter: () => setHoveredBtn('lucky'), onMouseLeave: () => setHoveredBtn(null), onClick: () => onFeelingLucky?.(query || (activeIndex >= 0 ? allItems[activeIndex]?.query : '')) }, feelingLuckyText))))))),
13429
+ React.createElement("button", { style: mergeStyles(styles.footerButton, hoveredBtn === 'lucky' ? styles.footerButtonHover : undefined), onMouseEnter: () => setHoveredBtn('lucky'), onMouseLeave: () => setHoveredBtn(null), onClick: () => onFeelingLucky?.(query || (activeIndex >= 0 ? allItems[activeIndex]?.query : '')) }, feelingLuckyText)))))),
13063
13430
  footer));
13064
13431
  });
13065
13432
 
@@ -13363,7 +13730,7 @@ const ImageIcon = () => (React.createElement("svg", { viewBox: "0 0 24 24", fill
13363
13730
  React.createElement("polyline", { points: "21 15 16 10 5 21" })));
13364
13731
  const PinterestDropdown = forwardRef(function PinterestDropdown(props, ref) {
13365
13732
  const { query, isOpen = true, loading = false, suggestions = [], products = [], categories = [], suggestionFields = { query: 'query' }, productFields = { id: 'id', title: 'title' }, categoryFields = { id: 'id', label: 'label' }, productDisplay = {}, theme = {}, showSaveButton = true, activeCategory: initialActiveCategory, activeTab, // From BaseDropdownProps
13366
- showPriceOverlay = true, gridColumns = 4, width = '800px', maxHeight = '600px', zIndex = 1000, className, style, classNames = {}, onSuggestionSelect, onProductClick, onCategoryClick, onTabChange, onSaveProduct, onViewAll, onClose, header, footer, showLoadingState = false, renderLoading, renderEmpty, } = props;
13733
+ showPriceOverlay = true, gridColumns = 4, width = '800px', maxHeight = '600px', zIndex = 1000, className, style, classNames = {}, onSuggestionSelect, onProductClick, onCategoryClick, onTabChange, onSaveProduct, onViewAll, onClose, header, footer, renderEmpty, } = props;
13367
13734
  // Inject global responsive styles
13368
13735
  useInjectResponsiveStyles();
13369
13736
  // Responsive state
@@ -13457,47 +13824,47 @@ const PinterestDropdown = forwardRef(function PinterestDropdown(props, ref) {
13457
13824
  "(",
13458
13825
  cat.count,
13459
13826
  ")"))))))))),
13460
- React.createElement("div", { style: { ...styles.content, maxHeight } }, (loading && showLoadingState) ? (renderLoading ? renderLoading() : (React.createElement("div", { style: styles.loading },
13461
- React.createElement("div", { style: styles.spinner })))) : (React.createElement(React.Fragment, null,
13462
- processedSuggestions.length > 0 && (React.createElement("div", { style: styles.suggestionsRow }, processedSuggestions.slice(0, 8).map((s, idx) => (React.createElement("button", { key: s.id || idx, "data-index": idx, style: mergeStyles(styles.suggestionPill, activeIndex === idx ? styles.suggestionPillActive : undefined, hoveredSuggestion === idx && activeIndex !== idx ? styles.suggestionPillHover : undefined), onClick: () => onSuggestionSelect?.(s._raw, idx), onMouseEnter: () => {
13463
- setHoveredSuggestion(idx);
13464
- setActiveIndex(idx);
13465
- }, onMouseLeave: () => setHoveredSuggestion(-1) },
13466
- React.createElement("span", { style: styles.suggestionIcon }, query ? highlightTextReact(s.query, query, { tag: 'strong' }) : s.query)))))),
13467
- displayProducts.length > 0 && (React.createElement(React.Fragment, null,
13468
- React.createElement("div", { style: styles.sectionTitle },
13469
- React.createElement(TrendingIcon$1, null),
13470
- "Ideas for you"),
13471
- React.createElement("div", { style: {
13472
- ...styles.productsGrid,
13473
- gridTemplateColumns: `repeat(${gridColumns}, 1fr)`,
13474
- } }, displayProducts.slice(0, 12).map((product, idx) => (React.createElement("div", { key: product.id, style: mergeStyles(styles.productCard, hoveredProduct === product.id ? styles.productCardHover : undefined), onClick: () => onProductClick?.(product._raw, idx), onMouseEnter: () => setHoveredProduct(product.id), onMouseLeave: () => setHoveredProduct(null) },
13475
- React.createElement("div", { style: styles.productImageContainer },
13476
- product.image ? (React.createElement("img", { src: product.image, alt: product.title, style: styles.productImage, loading: "lazy" })) : (React.createElement("div", { style: styles.productImagePlaceholder },
13477
- React.createElement(ImageIcon, null))),
13478
- showSaveButton && (React.createElement("button", { style: mergeStyles(styles.productSaveBtn, hoveredProduct === product.id ? styles.productSaveBtnVisible : undefined), onClick: (e) => {
13479
- e.stopPropagation();
13480
- onSaveProduct?.(product._raw);
13481
- }, "aria-label": "Save" },
13482
- React.createElement(HeartIcon, null))),
13483
- showPriceOverlay && product.price !== undefined && (React.createElement("div", { style: mergeStyles(styles.productOverlay, hoveredProduct === product.id ? styles.productOverlayVisible : undefined) },
13484
- React.createElement("div", { style: styles.productTitleOverlay }, product.title),
13485
- React.createElement("div", { style: styles.productPriceOverlay }, formatPrice(product.price, {
13827
+ React.createElement("div", { style: { ...styles.content, maxHeight } },
13828
+ React.createElement(React.Fragment, null,
13829
+ processedSuggestions.length > 0 && (React.createElement("div", { style: styles.suggestionsRow }, processedSuggestions.slice(0, 8).map((s, idx) => (React.createElement("button", { key: s.id || idx, "data-index": idx, style: mergeStyles(styles.suggestionPill, activeIndex === idx ? styles.suggestionPillActive : undefined, hoveredSuggestion === idx && activeIndex !== idx ? styles.suggestionPillHover : undefined), onClick: () => onSuggestionSelect?.(s._raw, idx), onMouseEnter: () => {
13830
+ setHoveredSuggestion(idx);
13831
+ setActiveIndex(idx);
13832
+ }, onMouseLeave: () => setHoveredSuggestion(-1) },
13833
+ React.createElement("span", { style: styles.suggestionIcon }, query ? highlightTextReact(s.query, query, { tag: 'strong' }) : s.query)))))),
13834
+ displayProducts.length > 0 && (React.createElement(React.Fragment, null,
13835
+ React.createElement("div", { style: styles.sectionTitle },
13836
+ React.createElement(TrendingIcon$1, null),
13837
+ "Ideas for you"),
13838
+ React.createElement("div", { style: {
13839
+ ...styles.productsGrid,
13840
+ gridTemplateColumns: `repeat(${gridColumns}, 1fr)`,
13841
+ } }, displayProducts.slice(0, 12).map((product, idx) => (React.createElement("div", { key: product.id, style: mergeStyles(styles.productCard, hoveredProduct === product.id ? styles.productCardHover : undefined), onClick: () => onProductClick?.(product._raw, idx), onMouseEnter: () => setHoveredProduct(product.id), onMouseLeave: () => setHoveredProduct(null) },
13842
+ React.createElement("div", { style: styles.productImageContainer },
13843
+ product.image ? (React.createElement("img", { src: product.image, alt: product.title, style: styles.productImage, loading: "lazy" })) : (React.createElement("div", { style: styles.productImagePlaceholder },
13844
+ React.createElement(ImageIcon, null))),
13845
+ showSaveButton && (React.createElement("button", { style: mergeStyles(styles.productSaveBtn, hoveredProduct === product.id ? styles.productSaveBtnVisible : undefined), onClick: (e) => {
13846
+ e.stopPropagation();
13847
+ onSaveProduct?.(product._raw);
13848
+ }, "aria-label": "Save" },
13849
+ React.createElement(HeartIcon, null))),
13850
+ showPriceOverlay && product.price !== undefined && (React.createElement("div", { style: mergeStyles(styles.productOverlay, hoveredProduct === product.id ? styles.productOverlayVisible : undefined) },
13851
+ React.createElement("div", { style: styles.productTitleOverlay }, product.title),
13852
+ React.createElement("div", { style: styles.productPriceOverlay }, formatPrice(product.price, {
13853
+ currency: productDisplay.currency || product.currency || '$'
13854
+ }))))),
13855
+ !showPriceOverlay && (React.createElement("div", { style: styles.productInfo },
13856
+ React.createElement("div", { style: styles.productTitle }, product.title),
13857
+ product.price !== undefined && (React.createElement("div", { style: styles.productPrice }, formatPrice(product.price, {
13486
13858
  currency: productDisplay.currency || product.currency || '$'
13487
- }))))),
13488
- !showPriceOverlay && (React.createElement("div", { style: styles.productInfo },
13489
- React.createElement("div", { style: styles.productTitle }, product.title),
13490
- product.price !== undefined && (React.createElement("div", { style: styles.productPrice }, formatPrice(product.price, {
13491
- currency: productDisplay.currency || product.currency || '$'
13492
- }))))))))))),
13493
- !loading && displayProducts.length === 0 && processedSuggestions.length === 0 && (renderEmpty ? renderEmpty(query) : (React.createElement("div", { style: styles.empty },
13494
- React.createElement("div", { style: styles.emptyIcon },
13495
- React.createElement(SearchIcon$3, null)),
13496
- React.createElement("p", null,
13497
- "No ideas found for \"",
13498
- query,
13499
- "\""),
13500
- React.createElement("p", { style: { fontSize: '14px', marginTop: '8px' } }, "Try searching for something else"))))))),
13859
+ }))))))))))),
13860
+ !loading && displayProducts.length === 0 && processedSuggestions.length === 0 && (renderEmpty ? renderEmpty(query) : (React.createElement("div", { style: styles.empty },
13861
+ React.createElement("div", { style: styles.emptyIcon },
13862
+ React.createElement(SearchIcon$3, null)),
13863
+ React.createElement("p", null,
13864
+ "No ideas found for \"",
13865
+ query,
13866
+ "\""),
13867
+ React.createElement("p", { style: { fontSize: '14px', marginTop: '8px' } }, "Try searching for something else")))))),
13501
13868
  footer !== undefined ? footer : (displayProducts.length > 0 && (React.createElement("div", { style: styles.footer },
13502
13869
  React.createElement("span", { style: { color: 'var(--seekora-text-secondary, #767676)', fontSize: '14px' } },
13503
13870
  displayProducts.length,
@@ -13797,7 +14164,7 @@ const ShoppingIcon = () => (React.createElement("svg", { viewBox: "0 0 20 20", f
13797
14164
  const ClockIcon$1 = () => (React.createElement("svg", { viewBox: "0 0 20 20", fill: "currentColor", width: "16", height: "16" },
13798
14165
  React.createElement("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z", clipRule: "evenodd" })));
13799
14166
  const SpotlightDropdown = forwardRef(function SpotlightDropdown(props, ref) {
13800
- const { query, isOpen = true, loading = false, suggestions = [], products = [], recentSearches = [], suggestionFields = { query: 'query' }, productFields = { id: 'id', title: 'title' }, productDisplay = {}, theme = {}, showPreview = true, asOverlay = true, placeholder = 'Search...', showShortcuts = true, actions = [], width = '680px', maxHeight = '500px', zIndex = 9999, className, style, classNames = {}, inputRef, onSuggestionSelect, onProductClick, onRecentClick, onClose, onSearchSubmit, header, footer, showLoadingState = false, renderLoading, renderEmpty, } = props;
14167
+ const { query, isOpen = true, loading = false, suggestions = [], products = [], recentSearches = [], suggestionFields = { query: 'query' }, productFields = { id: 'id', title: 'title' }, productDisplay = {}, theme = {}, showPreview = true, asOverlay = true, placeholder = 'Powered by Seekora', showShortcuts = true, actions = [], width = '680px', maxHeight = '500px', zIndex = 9999, className, style, classNames = {}, inputRef, onSuggestionSelect, onProductClick, onRecentClick, onClose, onSearchSubmit, header, footer, renderEmpty, } = props;
13801
14168
  // Inject global responsive styles
13802
14169
  useInjectResponsiveStyles();
13803
14170
  // Responsive state
@@ -13951,8 +14318,7 @@ const SpotlightDropdown = forwardRef(function SpotlightDropdown(props, ref) {
13951
14318
  React.createElement("span", { style: styles.kbd }, "K")))),
13952
14319
  header,
13953
14320
  React.createElement("div", { style: styles.content },
13954
- React.createElement("div", { ref: listRef, style: { ...styles.resultsColumn, maxHeight } }, (loading && allItems.length === 0 && showLoadingState) ? (renderLoading ? renderLoading() : (React.createElement("div", { style: styles.loading },
13955
- React.createElement("div", { style: styles.spinner })))) : allItems.length === 0 ? (renderEmpty ? renderEmpty(query) : (React.createElement("div", { style: styles.empty },
14321
+ React.createElement("div", { ref: listRef, style: { ...styles.resultsColumn, maxHeight } }, allItems.length === 0 ? (renderEmpty ? renderEmpty(query) : (React.createElement("div", { style: styles.empty },
13956
14322
  "No results for \"",
13957
14323
  query,
13958
14324
  "\""))) : (React.createElement(React.Fragment, null,
@@ -14348,7 +14714,7 @@ const SearchIcon$1 = () => (React.createElement("svg", { width: "18", height: "1
14348
14714
  const ArrowIcon = () => (React.createElement("svg", { width: "16", height: "16", viewBox: "0 0 20 20", fill: "currentColor" },
14349
14715
  React.createElement("path", { fillRule: "evenodd", d: "M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z", clipRule: "evenodd" })));
14350
14716
  const ShopifyDropdown = forwardRef(function ShopifyDropdown(props, ref) {
14351
- const { query, isOpen = true, loading = false, suggestions = [], products = [], categories = [], suggestionFields = { query: 'query' }, productFields = { id: 'id', title: 'title' }, categoryFields = { id: 'id', label: 'label' }, productDisplay = {}, theme = {}, showHeroProduct = true, showCollections = true, showAddToCart = true, addToCartText = 'Quick Add', showComparePrice = true, width = '100%', maxHeight = '500px', zIndex = 1000, className, style, classNames = {}, onSuggestionSelect, onProductClick, onCategoryClick, onTabChange, onAddToCart, onViewAll, onClose, header, footer, showLoadingState = false, renderLoading, renderEmpty, } = props;
14717
+ const { query, isOpen = true, loading = false, suggestions = [], products = [], categories = [], suggestionFields = { query: 'query' }, productFields = { id: 'id', title: 'title' }, categoryFields = { id: 'id', label: 'label' }, productDisplay = {}, theme = {}, showHeroProduct = true, showCollections = true, showAddToCart = true, addToCartText = 'Quick Add', showComparePrice = true, width = '100%', maxHeight = '500px', zIndex = 1000, className, style, classNames = {}, onSuggestionSelect, onProductClick, onCategoryClick, onTabChange, onAddToCart, onViewAll, onClose, header, footer, renderEmpty, } = props;
14352
14718
  // Inject global responsive styles
14353
14719
  useInjectResponsiveStyles();
14354
14720
  // Responsive state
@@ -14420,8 +14786,7 @@ const ShopifyDropdown = forwardRef(function ShopifyDropdown(props, ref) {
14420
14786
  }
14421
14787
  `),
14422
14788
  header,
14423
- (loading && showLoadingState) ? (renderLoading ? renderLoading() : (React.createElement("div", { style: styles.loading },
14424
- React.createElement("div", { style: styles.spinner })))) : (React.createElement("div", { style: styles.layout },
14789
+ React.createElement("div", { style: styles.layout },
14425
14790
  React.createElement("div", { style: styles.leftPanel },
14426
14791
  React.createElement("div", { style: styles.section },
14427
14792
  React.createElement("div", { style: styles.sectionHeader },
@@ -14476,7 +14841,7 @@ const ShopifyDropdown = forwardRef(function ShopifyDropdown(props, ref) {
14476
14841
  React.createElement("div", { style: styles.productTitle }, product.title),
14477
14842
  product.price !== undefined && (React.createElement("div", { style: styles.productPrice }, formatPrice(product.price, {
14478
14843
  currency: productDisplay.currency || product.currency || '$'
14479
- })))))))))))),
14844
+ }))))))))))),
14480
14845
  footer !== undefined ? footer : (React.createElement("div", { style: styles.footer },
14481
14846
  React.createElement("span", null,
14482
14847
  "Press ",
@@ -14753,7 +15118,7 @@ const ChevronIcon = () => (React.createElement("svg", { viewBox: "0 0 24 24", fi
14753
15118
  const ArrowUpIcon = () => (React.createElement("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", width: "20", height: "20" },
14754
15119
  React.createElement("path", { d: "M7 17l5-5-5-5M13 17l5-5-5-5" })));
14755
15120
  const MobileSheetDropdown = forwardRef(function MobileSheetDropdown(props, ref) {
14756
- const { query, isOpen = true, loading = false, suggestions = [], products = [], recentSearches = [], trendingSearches = [], suggestionFields = { query: 'query' }, productFields = { id: 'id', title: 'title' }, productDisplay = {}, theme = {}, showSearchInput = true, showCancel = true, cancelText = 'Cancel', showDragHandle = true, swipeToDismiss = true, footerButtonText = 'Search', showFooterButton = true, width = '100%', maxHeight = '85vh', zIndex = 9999, className, style, classNames = {}, inputRef, onSuggestionSelect, onProductClick, onRecentClick, onRecentClearAll, onSearchSubmit, onClose, header, footer, showLoadingState = false, renderLoading, renderEmpty, } = props;
15121
+ const { query, isOpen = true, loading = false, suggestions = [], products = [], recentSearches = [], trendingSearches = [], suggestionFields = { query: 'query' }, productFields = { id: 'id', title: 'title' }, productDisplay = {}, theme = {}, showSearchInput = true, showCancel = true, cancelText = 'Cancel', showDragHandle = true, swipeToDismiss = true, footerButtonText = 'Search', showFooterButton = true, width = '100%', maxHeight = '85vh', zIndex = 9999, className, style, classNames = {}, inputRef, onSuggestionSelect, onProductClick, onRecentClick, onRecentClearAll, onSearchSubmit, onClose, header, footer, renderEmpty, } = props;
14757
15122
  // Inject global responsive styles
14758
15123
  useInjectResponsiveStyles();
14759
15124
  const styles = useMemo(() => createStyles$2(), []);
@@ -14872,7 +15237,7 @@ const MobileSheetDropdown = forwardRef(function MobileSheetDropdown(props, ref)
14872
15237
  React.createElement("div", { style: styles.searchContainer },
14873
15238
  React.createElement("div", { style: styles.searchIcon },
14874
15239
  React.createElement(SearchIcon, null)),
14875
- React.createElement("input", { ref: inputRef, type: "text", placeholder: "Search...", value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyDown: (e) => {
15240
+ React.createElement("input", { ref: inputRef, type: "text", placeholder: "Powered by Seekora", value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyDown: (e) => {
14876
15241
  if (e.key === 'Enter') {
14877
15242
  e.preventDefault();
14878
15243
  if (activeIndex >= 0) {
@@ -14895,70 +15260,70 @@ const MobileSheetDropdown = forwardRef(function MobileSheetDropdown(props, ref)
14895
15260
  }
14896
15261
  }, style: styles.searchInput, autoFocus: true })),
14897
15262
  showCancel && (React.createElement("button", { style: styles.cancelBtn, onClick: onClose }, cancelText)))))),
14898
- React.createElement("div", { style: styles.content }, (loading && showLoadingState) ? (renderLoading ? renderLoading() : (React.createElement("div", { style: styles.loading },
14899
- React.createElement("div", { style: styles.spinner })))) : (React.createElement(React.Fragment, null,
14900
- showRecent && (React.createElement("div", { style: styles.section },
14901
- React.createElement("div", { style: styles.sectionHeader },
14902
- React.createElement("span", { style: styles.sectionTitle }, "Recent"),
14903
- React.createElement("button", { style: styles.clearBtn, onClick: onRecentClearAll }, "Clear")),
14904
- recentQueries.map((q, idx) => (React.createElement("div", { key: `recent-${q}`, "data-index": idx, style: mergeStyles(styles.item, activeIndex === idx ? styles.itemActive : undefined), onClick: () => {
14905
- onRecentClick?.(q);
14906
- setInputValue(q);
14907
- }, onMouseEnter: () => setActiveIndex(idx) },
14908
- React.createElement("div", { style: styles.itemIcon },
14909
- React.createElement(ClockIcon, null)),
14910
- React.createElement("div", { style: styles.itemContent },
14911
- React.createElement("div", { style: styles.itemTitle }, q)),
14912
- React.createElement("div", { style: styles.itemAction },
14913
- React.createElement(ArrowUpIcon, null))))))),
14914
- showTrending && (React.createElement("div", { style: styles.section },
14915
- React.createElement("div", { style: styles.sectionHeader },
14916
- React.createElement("span", { style: styles.sectionTitle }, "Trending")),
14917
- trendingQueries.map((q, idx) => {
14918
- const globalIdx = recentQueries.length + idx;
14919
- return (React.createElement("div", { key: `trending-${q}`, "data-index": globalIdx, style: mergeStyles(styles.item, activeIndex === globalIdx ? styles.itemActive : undefined), onClick: () => {
15263
+ React.createElement("div", { style: styles.content },
15264
+ React.createElement(React.Fragment, null,
15265
+ showRecent && (React.createElement("div", { style: styles.section },
15266
+ React.createElement("div", { style: styles.sectionHeader },
15267
+ React.createElement("span", { style: styles.sectionTitle }, "Recent"),
15268
+ React.createElement("button", { style: styles.clearBtn, onClick: onRecentClearAll }, "Clear")),
15269
+ recentQueries.map((q, idx) => (React.createElement("div", { key: `recent-${q}`, "data-index": idx, style: mergeStyles(styles.item, activeIndex === idx ? styles.itemActive : undefined), onClick: () => {
14920
15270
  onRecentClick?.(q);
14921
15271
  setInputValue(q);
14922
- }, onMouseEnter: () => setActiveIndex(globalIdx) },
15272
+ }, onMouseEnter: () => setActiveIndex(idx) },
14923
15273
  React.createElement("div", { style: styles.itemIcon },
14924
- React.createElement(TrendingIcon, null)),
15274
+ React.createElement(ClockIcon, null)),
14925
15275
  React.createElement("div", { style: styles.itemContent },
14926
15276
  React.createElement("div", { style: styles.itemTitle }, q)),
14927
15277
  React.createElement("div", { style: styles.itemAction },
14928
- React.createElement(ChevronIcon, null))));
14929
- }))),
14930
- inputValue && processedSuggestions.length > 0 && (React.createElement("div", { style: styles.section },
14931
- React.createElement("div", { style: styles.sectionHeader },
14932
- React.createElement("span", { style: styles.sectionTitle }, "Suggestions")),
14933
- processedSuggestions.map((s, idx) => {
14934
- const globalIdx = recentQueries.length + trendingQueries.length + idx;
14935
- return (React.createElement("div", { key: s.id || idx, "data-index": globalIdx, style: mergeStyles(styles.item, activeIndex === globalIdx ? styles.itemActive : undefined), onClick: () => onSuggestionSelect?.(s._raw, idx), onMouseEnter: () => setActiveIndex(globalIdx) },
14936
- React.createElement("div", { style: styles.itemIcon },
14937
- React.createElement(SearchIcon, null)),
14938
- React.createElement("div", { style: styles.itemContent },
14939
- React.createElement("div", { style: styles.itemTitle }, highlightTextReact(s.query, inputValue, { tag: 'strong' })),
14940
- s.count && (React.createElement("div", { style: styles.itemSubtitle },
14941
- s.count,
14942
- " results"))),
14943
- React.createElement("div", { style: styles.itemAction },
14944
- React.createElement(ChevronIcon, null))));
14945
- }))),
14946
- showProducts && (React.createElement("div", { style: styles.section },
14947
- React.createElement("div", { style: styles.sectionHeader },
14948
- React.createElement("span", { style: styles.sectionTitle }, "Products")),
14949
- React.createElement("div", { style: styles.productsScroll }, processedProducts.slice(0, 8).map((p, idx) => (React.createElement("div", { key: p.id, style: styles.productCard, onClick: () => onProductClick?.(p._raw, idx) },
14950
- p.image ? (React.createElement("img", { src: p.image, alt: p.title, style: styles.productImage, loading: "lazy" })) : (React.createElement("div", { style: styles.productImage })),
14951
- React.createElement("div", { style: styles.productTitle }, p.title),
14952
- p.price !== undefined && (React.createElement("div", { style: styles.productPrice }, formatPrice(p.price, {
14953
- currency: productDisplay.currency || p.currency || '$'
14954
- }))))))))),
14955
- inputValue && processedSuggestions.length === 0 && !loading && (renderEmpty ? renderEmpty(inputValue) : (React.createElement("div", { style: styles.empty },
14956
- React.createElement("div", { style: styles.emptyIcon },
14957
- React.createElement(SearchIcon, null)),
14958
- React.createElement("p", null,
14959
- "No results for \"",
14960
- inputValue,
14961
- "\""))))))),
15278
+ React.createElement(ArrowUpIcon, null))))))),
15279
+ showTrending && (React.createElement("div", { style: styles.section },
15280
+ React.createElement("div", { style: styles.sectionHeader },
15281
+ React.createElement("span", { style: styles.sectionTitle }, "Trending")),
15282
+ trendingQueries.map((q, idx) => {
15283
+ const globalIdx = recentQueries.length + idx;
15284
+ return (React.createElement("div", { key: `trending-${q}`, "data-index": globalIdx, style: mergeStyles(styles.item, activeIndex === globalIdx ? styles.itemActive : undefined), onClick: () => {
15285
+ onRecentClick?.(q);
15286
+ setInputValue(q);
15287
+ }, onMouseEnter: () => setActiveIndex(globalIdx) },
15288
+ React.createElement("div", { style: styles.itemIcon },
15289
+ React.createElement(TrendingIcon, null)),
15290
+ React.createElement("div", { style: styles.itemContent },
15291
+ React.createElement("div", { style: styles.itemTitle }, q)),
15292
+ React.createElement("div", { style: styles.itemAction },
15293
+ React.createElement(ChevronIcon, null))));
15294
+ }))),
15295
+ inputValue && processedSuggestions.length > 0 && (React.createElement("div", { style: styles.section },
15296
+ React.createElement("div", { style: styles.sectionHeader },
15297
+ React.createElement("span", { style: styles.sectionTitle }, "Suggestions")),
15298
+ processedSuggestions.map((s, idx) => {
15299
+ const globalIdx = recentQueries.length + trendingQueries.length + idx;
15300
+ return (React.createElement("div", { key: s.id || idx, "data-index": globalIdx, style: mergeStyles(styles.item, activeIndex === globalIdx ? styles.itemActive : undefined), onClick: () => onSuggestionSelect?.(s._raw, idx), onMouseEnter: () => setActiveIndex(globalIdx) },
15301
+ React.createElement("div", { style: styles.itemIcon },
15302
+ React.createElement(SearchIcon, null)),
15303
+ React.createElement("div", { style: styles.itemContent },
15304
+ React.createElement("div", { style: styles.itemTitle }, highlightTextReact(s.query, inputValue, { tag: 'strong' })),
15305
+ s.count && (React.createElement("div", { style: styles.itemSubtitle },
15306
+ s.count,
15307
+ " results"))),
15308
+ React.createElement("div", { style: styles.itemAction },
15309
+ React.createElement(ChevronIcon, null))));
15310
+ }))),
15311
+ showProducts && (React.createElement("div", { style: styles.section },
15312
+ React.createElement("div", { style: styles.sectionHeader },
15313
+ React.createElement("span", { style: styles.sectionTitle }, "Products")),
15314
+ React.createElement("div", { style: styles.productsScroll }, processedProducts.slice(0, 8).map((p, idx) => (React.createElement("div", { key: p.id, style: styles.productCard, onClick: () => onProductClick?.(p._raw, idx) },
15315
+ p.image ? (React.createElement("img", { src: p.image, alt: p.title, style: styles.productImage, loading: "lazy" })) : (React.createElement("div", { style: styles.productImage })),
15316
+ React.createElement("div", { style: styles.productTitle }, p.title),
15317
+ p.price !== undefined && (React.createElement("div", { style: styles.productPrice }, formatPrice(p.price, {
15318
+ currency: productDisplay.currency || p.currency || '$'
15319
+ }))))))))),
15320
+ inputValue && processedSuggestions.length === 0 && !loading && (renderEmpty ? renderEmpty(inputValue) : (React.createElement("div", { style: styles.empty },
15321
+ React.createElement("div", { style: styles.emptyIcon },
15322
+ React.createElement(SearchIcon, null)),
15323
+ React.createElement("p", null,
15324
+ "No results for \"",
15325
+ inputValue,
15326
+ "\"")))))),
14962
15327
  footer !== undefined ? footer : (showFooterButton && inputValue && (React.createElement("div", { style: styles.footer },
14963
15328
  React.createElement("button", { style: styles.footerBtn, onClick: () => onSearchSubmit?.(inputValue) },
14964
15329
  footerButtonText,
@@ -15129,7 +15494,7 @@ const createStyles$1 = () => ({
15129
15494
  },
15130
15495
  });
15131
15496
  const MinimalDropdown = forwardRef(function MinimalDropdown(props, ref) {
15132
- const { query, isOpen = true, loading = false, suggestions = [], products = [], recentSearches = [], suggestionFields = { query: 'query' }, productFields = { id: 'id', title: 'title' }, productDisplay = {}, theme = {}, showIndices = false, showTypeLabels = false, showSectionDividers = true, width = '100%', maxHeight = '400px', zIndex = 1000, className, style, classNames = {}, onSuggestionSelect, onProductClick, onRecentClick, onClose, onSearchSubmit, header, footer, showLoadingState = false, renderLoading, renderEmpty, } = props;
15497
+ const { query, isOpen = true, loading = false, suggestions = [], products = [], recentSearches = [], suggestionFields = { query: 'query' }, productFields = { id: 'id', title: 'title' }, productDisplay = {}, theme = {}, showIndices = false, showTypeLabels = false, showSectionDividers = true, width = '100%', maxHeight = '400px', zIndex = 1000, className, style, classNames = {}, onSuggestionSelect, onProductClick, onRecentClick, onClose, onSearchSubmit, header, footer, renderEmpty, } = props;
15133
15498
  // Inject global responsive styles
15134
15499
  useInjectResponsiveStyles();
15135
15500
  // Responsive state
@@ -15215,7 +15580,7 @@ const MinimalDropdown = forwardRef(function MinimalDropdown(props, ref) {
15215
15580
  }
15216
15581
  `),
15217
15582
  header,
15218
- React.createElement("div", { ref: listRef, style: { maxHeight, overflowY: 'auto' } }, (loading && allItems.length === 0 && showLoadingState) ? (renderLoading ? renderLoading() : (React.createElement("div", { style: styles.loading }, "Loading..."))) : allItems.length === 0 ? (renderEmpty ? renderEmpty(query) : (React.createElement("div", { style: styles.empty }, query ? `No results for "${query}"` : 'Start typing to search'))) : (React.createElement(React.Fragment, null,
15583
+ React.createElement("div", { ref: listRef, style: { maxHeight, overflowY: 'auto' } }, allItems.length === 0 ? (renderEmpty ? renderEmpty(query) : (React.createElement("div", { style: styles.empty }, query ? `No results for "${query}"` : 'Start typing to search'))) : (React.createElement(React.Fragment, null,
15219
15584
  showRecent && (React.createElement(React.Fragment, null,
15220
15585
  showSectionDividers && (React.createElement("div", { style: styles.divider }, "Recent")),
15221
15586
  recentQueries.map((q, idx) => {
@@ -15423,24 +15788,12 @@ const createStyles = (isMobile) => ({
15423
15788
  flexShrink: 0,
15424
15789
  transition: 'color 150ms',
15425
15790
  },
15426
- loadingSpinner: {
15427
- width: '18px',
15428
- height: '18px',
15429
- borderWidth: '2px',
15430
- borderStyle: 'solid',
15431
- borderColor: '#e5e7eb',
15432
- borderTopColor: '#f90',
15433
- borderRadius: '50%',
15434
- animation: 'seekora-spin 0.8s linear infinite',
15435
- marginRight: '12px',
15436
- flexShrink: 0,
15437
- },
15438
15791
  });
15439
15792
  // ============================================================================
15440
15793
  // Component
15441
15794
  // ============================================================================
15442
15795
  const SuggestionSearchBar = forwardRef(function SuggestionSearchBar(props, ref) {
15443
- const { client, variant = 'amazon', autoMobileVariant = true, placeholder = 'Search...', defaultQuery = '', value, minQueryLength = 1, maxSuggestions = 8, debounceMs = 200, includeDropdownRecommendations = true, includeDropdownProductList = true, includeFilteredTabs = true, includeCategories = true, filteredTabs, analyticsTags, enableRecentSearches = true, maxRecentSearches = 10, showProducts = true, showTrendingOnEmpty = true, enableAnalytics = true, analyticsConfig, suggestionFields, productFields, theme, onSearch, onQueryChange, onSuggestionSelect, onProductClick, onCategoryClick, onTabChange, className, style, inputClassName, dropdownWidth, dropdownMaxHeight = '500px', zIndex = 1000, enableCache = true, cacheTtlMs = 30000, cacheMaxSize = 100, } = props;
15796
+ const { client, variant = 'amazon', autoMobileVariant = true, placeholder = 'Powered by Seekora', defaultQuery = '', value, minQueryLength = 1, maxSuggestions = 8, debounceMs = 200, includeDropdownRecommendations = true, includeDropdownProductList = true, includeFilteredTabs = true, includeCategories = true, filteredTabs, analyticsTags, enableRecentSearches = true, maxRecentSearches = 10, showProducts = true, showTrendingOnEmpty = true, enableAnalytics = true, analyticsConfig, suggestionFields, productFields, theme, onSearch, onQueryChange, onSuggestionSelect, onProductClick, onCategoryClick, onTabChange, className, style, inputClassName, dropdownWidth, dropdownMaxHeight = '500px', zIndex = 1000, enableCache = true, cacheTtlMs = 30000, cacheMaxSize = 100, } = props;
15444
15797
  // Theme: prop overrides context (SearchProvider theme)
15445
15798
  const searchContext = useSearchContext();
15446
15799
  const effectiveTheme = theme ?? searchContext.theme;
@@ -15904,8 +16257,7 @@ const SuggestionSearchBar = forwardRef(function SuggestionSearchBar(props, ref)
15904
16257
  React.createElement("svg", { viewBox: "0 0 20 20", fill: "currentColor", width: "20", height: "20" },
15905
16258
  React.createElement("path", { fillRule: "evenodd", d: "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z", clipRule: "evenodd" }))),
15906
16259
  React.createElement("input", { ref: inputRef, type: "text", value: currentQuery, onChange: handleInputChange, onFocus: handleInputFocus, onBlur: handleInputBlur, onKeyDown: handleKeyDown, placeholder: placeholder, className: inputClassName, style: styles.input, autoComplete: "off", autoCorrect: "off", autoCapitalize: "off", spellCheck: false, "aria-label": "Search", "aria-expanded": isOpen, "aria-haspopup": "listbox" }),
15907
- loading && React.createElement("div", { style: styles.loadingSpinner }),
15908
- currentQuery && !loading && (React.createElement("button", { type: "button", onClick: handleClear, style: styles.clearButton, "aria-label": "Clear search" }, "\u00D7"))),
16260
+ currentQuery && (React.createElement("button", { type: "button", onClick: handleClear, style: styles.clearButton, "aria-label": "Clear search" }, "\u00D7"))),
15909
16261
  isOpen && (React.createElement(DropdownComponent, { query: currentQuery, isOpen: isOpen, loading: loading, suggestions: suggestions, products: currentTabProducts, categories: tabs.map(t => ({ id: t.id, label: t.label, count: t.nb_hits })), recentSearches: recentSearches.map(r => typeof r === 'string' ? r : r.query), trendingSearches: trendingSearches, filteredTabs: tabs, activeTab: activeTab, suggestionFields: suggestionFields, productFields: productFields, theme: themeToDropdownConfig(effectiveTheme) ?? effectiveTheme, width: dropdownWidth || '100%', maxHeight: dropdownMaxHeight, zIndex: zIndex, analytics: {
15910
16262
  enabled: enableAnalytics,
15911
16263
  trackSuggestionClicks: analyticsConfig?.trackSuggestionClicks ?? true,
@@ -15990,18 +16342,16 @@ function Modal({ isOpen, onClose, children }) {
15990
16342
  return createPortal(modalContent, document.body);
15991
16343
  }
15992
16344
 
15993
- function SearchBox({ value, onChange, onKeyDown, placeholder = 'Search documentation...', isLoading = false, onClear, }) {
16345
+ function SearchBox({ value, onChange, onKeyDown, placeholder = 'Search documentation...', onClear, }) {
15994
16346
  const inputRef = useRef(null);
15995
16347
  useEffect(() => { if (inputRef.current)
15996
16348
  inputRef.current.focus(); }, []);
15997
16349
  const handleChange = (event) => onChange(event.target.value);
15998
16350
  const handleClear = () => { onChange(''); onClear?.(); inputRef.current?.focus(); };
15999
16351
  return (React.createElement("div", { className: "seekora-docsearch-searchbox" },
16000
- React.createElement("label", { className: "seekora-docsearch-searchbox-icon", htmlFor: "seekora-docsearch-input" }, isLoading ? (React.createElement("span", { className: "seekora-docsearch-spinner", "aria-hidden": "true" },
16001
- React.createElement("svg", { width: "20", height: "20", viewBox: "0 0 20 20" },
16002
- React.createElement("circle", { cx: "10", cy: "10", r: "8", stroke: "currentColor", strokeWidth: "2", fill: "none", strokeDasharray: "40", strokeDashoffset: "10" },
16003
- React.createElement("animateTransform", { attributeName: "transform", type: "rotate", from: "0 10 10", to: "360 10 10", dur: "0.8s", repeatCount: "indefinite" }))))) : (React.createElement("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true" },
16004
- React.createElement("path", { d: "M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z", fill: "currentColor" })))),
16352
+ React.createElement("label", { className: "seekora-docsearch-searchbox-icon", htmlFor: "seekora-docsearch-input" },
16353
+ React.createElement("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true" },
16354
+ React.createElement("path", { d: "M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z", fill: "currentColor" }))),
16005
16355
  React.createElement("input", { ref: inputRef, id: "seekora-docsearch-input", className: "seekora-docsearch-input", type: "text", value: value, onChange: handleChange, onKeyDown: onKeyDown, placeholder: placeholder, autoComplete: "off", autoCorrect: "off", autoCapitalize: "off", spellCheck: false, "aria-autocomplete": "list", "aria-controls": "seekora-docsearch-results" }),
16006
16356
  value && (React.createElement("button", { type: "button", className: "seekora-docsearch-clear", onClick: handleClear, "aria-label": "Clear search" },
16007
16357
  React.createElement("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true" },
@@ -16230,7 +16580,7 @@ function getHitKey(hit, index) {
16230
16580
  return hit.objectID;
16231
16581
  return `suggestion-${hit.url}-${index}`;
16232
16582
  }
16233
- function Results({ hits, groupedHits, selectedIndex, onSelect, onHover, scrollSelectionIntoViewRef, query, isLoading, showLoadingState = false, error, translations = {}, sources: _sources = [], }) {
16583
+ function Results({ hits, groupedHits, selectedIndex, onSelect, onHover, scrollSelectionIntoViewRef, query, isLoading, error, translations = {}, sources: _sources = [], }) {
16234
16584
  const listRef = useRef(null);
16235
16585
  useEffect(() => {
16236
16586
  if (!listRef.current || hits.length === 0)
@@ -16264,14 +16614,6 @@ function Results({ hits, groupedHits, selectedIndex, onSelect, onHover, scrollSe
16264
16614
  return (React.createElement("div", { className: "seekora-docsearch-empty" },
16265
16615
  React.createElement("p", { className: "seekora-docsearch-empty-text" }, translations.searchPlaceholder || 'Type to start searching...')));
16266
16616
  }
16267
- if (isLoading && hits.length === 0 && showLoadingState) {
16268
- return (React.createElement("div", { className: "seekora-docsearch-loading" },
16269
- React.createElement("div", { className: "seekora-docsearch-loading-spinner" },
16270
- React.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", "aria-hidden": "true" },
16271
- React.createElement("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2", fill: "none", strokeDasharray: "50", strokeDashoffset: "15" },
16272
- React.createElement("animateTransform", { attributeName: "transform", type: "rotate", from: "0 12 12", to: "360 12 12", dur: "0.8s", repeatCount: "indefinite" })))),
16273
- React.createElement("p", { className: "seekora-docsearch-loading-text" }, translations.loadingText || 'Searching...')));
16274
- }
16275
16617
  if (isLoading && hits.length === 0) {
16276
16618
  return React.createElement("div", { className: "seekora-docsearch-empty" });
16277
16619
  }
@@ -16684,6 +17026,12 @@ function useSeekoraSearch$1(options) {
16684
17026
  readSecret: storeSecret,
16685
17027
  logLevel: 'warn',
16686
17028
  enableContextCollection: true,
17029
+ enableEventQueue: true, // Enable batching for better performance
17030
+ eventQueue: {
17031
+ batchSize: 10,
17032
+ flushInterval: 5000, // 5 seconds
17033
+ enablePersistence: true,
17034
+ },
16687
17035
  };
16688
17036
  if (apiEndpoint) {
16689
17037
  if (['local', 'stage', 'production'].includes(apiEndpoint)) {
@@ -16895,7 +17243,7 @@ function DocSearch({ storeId, storeSecret, seekoraApiEndpoint, apiEndpoint, apiK
16895
17243
  React.createElement(Modal, { isOpen: isOpen, onClose: handleClose },
16896
17244
  React.createElement("div", { className: "seekora-docsearch-modal", onKeyDown: handleKeyDown },
16897
17245
  React.createElement("header", { className: "seekora-docsearch-header" },
16898
- React.createElement(SearchBox, { value: query, onChange: setQuery, onKeyDown: handleKeyDown, placeholder: placeholder, isLoading: isLoading, onClear: reset }),
17246
+ React.createElement(SearchBox, { value: query, onChange: setQuery, onKeyDown: handleKeyDown, placeholder: placeholder, onClear: reset }),
16899
17247
  React.createElement("button", { type: "button", className: "seekora-docsearch-close", onClick: handleClose, "aria-label": "Close search" },
16900
17248
  React.createElement("span", { className: "seekora-docsearch-close-text" }, "esc"))),
16901
17249
  React.createElement("div", { className: "seekora-docsearch-body" },
@@ -18180,5 +18528,5 @@ function updateSuggestionsStyles(theme) {
18180
18528
  injectSuggestionsStyles(theme, true);
18181
18529
  }
18182
18530
 
18183
- export { ActionButtons, AmazonDropdown, AnalyticsProvider, BadgeList, Breadcrumb, CategoriesTabs, ClearRefinements, CurrentRefinements, DocSearch, DocSearchButton, DropdownPanel, FacetDropdown, Facets, FederatedDropdown, Fingerprint, FrequentlyBoughtTogether, GoogleDropdown, HierarchicalMenu, Highlight$1 as Highlight, HitsPerPage, ImageDisplay, ImageZoom, InfiniteHits, ItemCard, ItemGrid, MinimalDropdown, MobileFilters, MobileFiltersButton, MobileSheetDropdown, Pagination, PinterestDropdown, PriceDisplay, ProductCard, ProductGallery, ProductGrid, ProductInfo, ProductRecommendations, QuerySuggestions, QuerySuggestionsDropdown, RangeInput, RangeSlider, RatingDisplay, RecentSearchesList, RecentlyViewed, RelatedProducts, RichQuerySuggestions, SearchBar, SearchBarWithSuggestions, SearchInput, SearchLayout, SearchProvider, SearchResults, SectionError, SectionItemGrid, SectionLoading, SectionSearchProvider, ShopifyDropdown, Snippet, SortBy, SpotlightDropdown, Stats, SuggestionDropdownVariants, SuggestionItem, SuggestionList, SuggestionSearchBar, SuggestionsDropdownComposition, SuggestionsError, SuggestionsLoading, SuggestionsProvider, TrendingItems, TrendingList, VariantSelector, VariantSwatches, addRecentSearch, addToRecentlyViewed, brandPresets, breakpoints, clearRecentSearches, clearSuggestionsCache, createSuggestionsCache, createSuggestionsTheme, createTheme, darkTheme, darkThemeVariables, defaultTheme, extractBadges, extractBrand, extractCategory, extractProduct, extractSuggestion, findVariantBySelections, formatParsedFilters, formatPriceRange, formatPrice as formatSuggestionPrice, generateSuggestionsStylesheet, getAvailableValuesForOption, getFingerprint, getPriceRange, getRecentSearches, getShortcutText, getSuggestionsCache, highlightText, injectGlobalResponsiveStyles, injectSuggestionsStyles, lightThemeVariables, mediaQueries, mergeThemes, minimalTheme, minimalThemeVariables, parseHighlightMarkup, removeRecentSearch, touchTargets, updateSuggestionsStyles, useAnalytics, useAnalyticsProvider, useDocSearch, useSeekoraSearch$1 as useDocSearchSeekoraSearch, useInjectResponsiveStyles, useKeyboard, useNaturalLanguageFilters, useProductAnalytics, useQuerySuggestions, useQuerySuggestionsEnhanced, useResponsive, useSearchContext, useSearchState, useSectionSearchContext, useSeekoraSearch, useSmartSuggestions, useSuggestionsAnalytics, useSuggestionsContext, useVariantSelection, withAnalytics };
18531
+ export { ActionButtons, AmazonDropdown, AnalyticsProvider, BadgeList, Breadcrumb, CategoriesTabs, ClearRefinements, CurrentRefinements, DocSearch, DocSearchButton, DropdownPanel, FacetDropdown, Facets, FederatedDropdown, Fingerprint, FrequentlyBoughtTogether, GoogleDropdown, HierarchicalMenu, Highlight$1 as Highlight, HitsPerPage, ImageDisplay, ImageZoom, InfiniteHits, ItemCard, ItemGrid, MinimalDropdown, MobileFilters, MobileFiltersButton, MobileSheetDropdown, Pagination, PinterestDropdown, PriceDisplay, ProductCard, ProductGallery, ProductGrid, ProductInfo, ProductRecommendations, QuerySuggestions, QuerySuggestionsDropdown, RangeInput, RangeSlider, RatingDisplay, RecentSearchesList, RecentlyViewed, RelatedProducts, RichQuerySuggestions, SearchBar, SearchBarWithSuggestions, SearchInput, SearchLayout, SearchProvider, SearchResults, SectionError, SectionItemGrid, SectionSearchProvider, ShopifyDropdown, Snippet, SortBy, SpotlightDropdown, Stats, SuggestionDropdownVariants, SuggestionItem, SuggestionList, SuggestionSearchBar, SuggestionsDropdownComposition, SuggestionsError, SuggestionsProvider, TrendingItems, TrendingList, VariantSelector, VariantSwatches, addRecentSearch, addToRecentlyViewed, brandPresets, breakpoints, clearRecentSearches, clearSuggestionsCache, createSuggestionsCache, createSuggestionsTheme, createTheme, darkTheme, darkThemeVariables, defaultTheme, extractBadges, extractBrand, extractCategory, extractProduct, extractSuggestion, findVariantBySelections, formatParsedFilters, formatPriceRange, formatPrice as formatSuggestionPrice, generateSuggestionsStylesheet, getAvailableValuesForOption, getFingerprint, getPriceRange, getRecentSearches, getShortcutText, getSuggestionsCache, highlightText, injectGlobalResponsiveStyles, injectSuggestionsStyles, lightThemeVariables, mediaQueries, mergeThemes, minimalTheme, minimalThemeVariables, parseHighlightMarkup, removeRecentSearch, touchTargets, updateSuggestionsStyles, useAnalytics, useAnalyticsProvider, useDocSearch, useSeekoraSearch$1 as useDocSearchSeekoraSearch, useFilters, useInjectResponsiveStyles, useKeyboard, useNaturalLanguageFilters, useProductAnalytics, useQuerySuggestions, useQuerySuggestionsEnhanced, useResponsive, useSearchContext, useSearchState, useSectionSearchContext, useSeekoraSearch, useSmartSuggestions, useSuggestionsAnalytics, useSuggestionsContext, useVariantSelection, withAnalytics };
18184
18532
  //# sourceMappingURL=index.esm.js.map