@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
package/dist/src/index.js CHANGED
@@ -582,6 +582,87 @@ class SearchStateManager {
582
582
  this.abVariant = abVariant;
583
583
  log.verbose('SearchStateManager: A/B test updated', { abTestId, abVariant });
584
584
  }
585
+ // =========================================================================
586
+ // Filters API
587
+ // =========================================================================
588
+ /** Build filter_by string from current refinements */
589
+ buildFilterString() {
590
+ if (this.state.refinements.length === 0)
591
+ return '';
592
+ const filtersByField = {};
593
+ this.state.refinements.forEach((refinement) => {
594
+ if (!filtersByField[refinement.field]) {
595
+ filtersByField[refinement.field] = [];
596
+ }
597
+ filtersByField[refinement.field].push(refinement.value);
598
+ });
599
+ const filterParts = [];
600
+ Object.entries(filtersByField).forEach(([field, values]) => {
601
+ values.forEach((value) => {
602
+ filterParts.push(`${field}:${value}`);
603
+ });
604
+ });
605
+ return filterParts.join(' && ');
606
+ }
607
+ /** Fetch filter values independently from search */
608
+ async fetchFilters(options) {
609
+ log.verbose('SearchStateManager: Fetching filters', { options });
610
+ try {
611
+ const filterString = this.buildFilterString();
612
+ const response = await this.client.getFilters({
613
+ q: this.state.query || undefined,
614
+ filter: filterString || undefined,
615
+ ...options,
616
+ });
617
+ log.info('SearchStateManager: Filters fetched', {
618
+ filterCount: response?.filters?.length,
619
+ });
620
+ return response;
621
+ }
622
+ catch (err) {
623
+ const error = err instanceof Error ? err : new Error(String(err));
624
+ log.error('SearchStateManager: Failed to fetch filters', { error: error.message });
625
+ throw error;
626
+ }
627
+ }
628
+ /** Search within a facet's values (for autocomplete in facet search boxes) */
629
+ async searchFacetValues(facetName, query) {
630
+ log.verbose('SearchStateManager: Searching facet values', { facetName, query });
631
+ try {
632
+ const filterString = this.buildFilterString();
633
+ const response = await this.client.searchFacetValues(facetName, {
634
+ q: this.state.query || undefined,
635
+ filter: filterString || undefined,
636
+ facetQuery: query,
637
+ });
638
+ log.info('SearchStateManager: Facet values fetched', {
639
+ facetName,
640
+ valueCount: response?.values?.length,
641
+ });
642
+ return response;
643
+ }
644
+ catch (err) {
645
+ const error = err instanceof Error ? err : new Error(String(err));
646
+ log.error('SearchStateManager: Failed to search facet values', { facetName, error: error.message });
647
+ throw error;
648
+ }
649
+ }
650
+ /** Get filter schema (for UI initialization) */
651
+ async getFiltersSchema() {
652
+ log.verbose('SearchStateManager: Getting filters schema');
653
+ try {
654
+ const response = await this.client.getFiltersSchema();
655
+ log.info('SearchStateManager: Filters schema fetched', {
656
+ fieldCount: response?.fields?.length,
657
+ });
658
+ return response;
659
+ }
660
+ catch (err) {
661
+ const error = err instanceof Error ? err : new Error(String(err));
662
+ log.error('SearchStateManager: Failed to get filters schema', { error: error.message });
663
+ throw error;
664
+ }
665
+ }
585
666
  // Clear all state
586
667
  clear() {
587
668
  this.state = {
@@ -1389,9 +1470,7 @@ const SearchProvider = ({ client, theme: themeConfig, enableAnalytics = true, au
1389
1470
  React.useEffect(() => {
1390
1471
  if (abTestId !== undefined || abVariant !== undefined) {
1391
1472
  stateManager.setAbTest(abTestId, abVariant);
1392
- if (typeof client.setAbTest === 'function') {
1393
- client.setAbTest(abTestId, abVariant);
1394
- }
1473
+ client.setAbTest(abTestId, abVariant);
1395
1474
  }
1396
1475
  }, [stateManager, client, abTestId, abVariant]);
1397
1476
  const value = React.useMemo(() => ({
@@ -1608,7 +1687,7 @@ const DefaultSearchIcon = ({ size = 18 }) => (React.createElement("svg", { width
1608
1687
  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" },
1609
1688
  React.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1610
1689
  React.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })));
1611
- 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', }) => {
1690
+ 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', }) => {
1612
1691
  const { client, theme, enableAnalytics, autoTrackSearch } = useSearchContext();
1613
1692
  const { query, setQuery, search: triggerSearch, results, loading: searchLoading, error: searchError } = useSearchState();
1614
1693
  const [isFocused, setIsFocused] = React.useState(false);
@@ -1738,12 +1817,11 @@ const SearchBar = ({ placeholder = 'Search...', showSuggestions = true, debounce
1738
1817
  }, 200);
1739
1818
  }, []);
1740
1819
  const defaultRenderSuggestion = (suggestion, index) => (React.createElement("div", { key: index }, suggestion));
1741
- const defaultRenderLoading = () => (React.createElement("div", { style: { padding: theme.spacing.medium, textAlign: 'center' } }, "Loading suggestions..."));
1742
1820
  const searchBarTheme = customTheme || {};
1743
1821
  const isLoading = suggestionsLoading || searchLoading;
1744
1822
  // Show list when we have suggestions (including previous while loading) or when loading and showLoadingState
1745
1823
  const hasSuggestions = displayedSuggestions.length > 0;
1746
- const showSuggestionsList = isFocused && showSuggestions && query.length >= minQueryLength && (hasSuggestions || (isLoading && showLoadingState));
1824
+ const showSuggestionsList = isFocused && showSuggestions && query.length >= minQueryLength && hasSuggestions;
1747
1825
  // Get processing time from results
1748
1826
  const res = results;
1749
1827
  const processingTime = res?.processingTimeMS
@@ -1871,7 +1949,6 @@ const SearchBar = ({ placeholder = 'Search...', showSuggestions = true, debounce
1871
1949
  zIndex: Z_INDEX$2.dropdown,
1872
1950
  boxSizing: 'border-box',
1873
1951
  } },
1874
- isLoading && displayedSuggestions.length === 0 && showLoadingState && (renderLoading ? renderLoading() : defaultRenderLoading()),
1875
1952
  displayedSuggestions.length > 0 && (React.createElement(React.Fragment, null, displayedSuggestions.map((suggestion, index) => {
1876
1953
  const isSelected = index === selectedIndex;
1877
1954
  const renderFn = renderSuggestion || defaultRenderSuggestion;
@@ -1914,7 +1991,7 @@ const formatPrice$1 = (value, currency = '₹') => {
1914
1991
  }
1915
1992
  return String(value);
1916
1993
  };
1917
- 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, }) => {
1994
+ 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%', }) => {
1918
1995
  const { theme, client, enableAnalytics } = useSearchContext();
1919
1996
  const { results: stateResults, loading: stateLoading, error: stateError, currentPage, itemsPerPage: stateItemsPerPage } = useSearchState();
1920
1997
  const searchResultsTheme = customTheme || {};
@@ -2184,11 +2261,6 @@ const SearchResults = ({ results: resultsProp, loading: loadingProp, error: erro
2184
2261
  textAlign: 'center',
2185
2262
  color: theme.colors.text,
2186
2263
  } }, "No results found"));
2187
- const defaultRenderLoading = () => (React.createElement("div", { className: searchResultsTheme.loadingState, style: {
2188
- padding: theme.spacing.large,
2189
- textAlign: 'center',
2190
- color: theme.colors.text,
2191
- } }, "Loading results..."));
2192
2264
  const defaultRenderError = (err) => (React.createElement("div", { className: searchResultsTheme.errorState, style: {
2193
2265
  padding: theme.spacing.large,
2194
2266
  textAlign: 'center',
@@ -2297,8 +2369,6 @@ const SearchResults = ({ results: resultsProp, loading: loadingProp, error: erro
2297
2369
  const containerStyle = {
2298
2370
  minHeight: `var(--seekora-results-min-height, ${minHeight})`,
2299
2371
  minWidth: `var(--seekora-results-min-width, ${minWidth})`,
2300
- transition: 'opacity 200ms ease-in-out',
2301
- opacity: loading && resultItems.length > 0 ? loadingOpacity : 1,
2302
2372
  ...style,
2303
2373
  };
2304
2374
  // Determine results list style based on view mode
@@ -2322,12 +2392,6 @@ const SearchResults = ({ results: resultsProp, loading: loadingProp, error: erro
2322
2392
  hasError: !!error,
2323
2393
  isLoading: loading,
2324
2394
  });
2325
- // When loading with no previous results, show loading only if showLoadingState (default: show previous results, no loading screen)
2326
- if (loading && resultItems.length === 0 && showLoadingState) {
2327
- log.verbose('SearchResults: Rendering loading state');
2328
- return (React.createElement("div", { className: clsx(searchResultsTheme.container, className), style: containerStyle }, renderLoading ? renderLoading() : defaultRenderLoading()));
2329
- }
2330
- // When loading with previous results, fall through and render them (with opacity transition)
2331
2395
  if (error) {
2332
2396
  log.error('SearchResults: Rendering error state', {
2333
2397
  error: error.message,
@@ -2551,6 +2615,183 @@ const Stats = ({ results: resultsProp, renderStats, className, style, theme: cus
2551
2615
  return null;
2552
2616
  };
2553
2617
 
2618
+ /**
2619
+ * Responsive utilities for adaptive layouts across all screen sizes
2620
+ *
2621
+ * Provides hooks and utilities for:
2622
+ * - Responsive breakpoints
2623
+ * - Responsive grid columns
2624
+ * - Responsive spacing
2625
+ * - Responsive font sizes
2626
+ * - Touch target sizing
2627
+ */
2628
+ /**
2629
+ * Standard breakpoints (in pixels)
2630
+ */
2631
+ const BREAKPOINTS = {
2632
+ sm: 576, // Small devices (landscape phones, ≥ 576px)
2633
+ md: 768, // Medium devices (tablets, ≥ 768px)
2634
+ lg: 992, // Large devices (desktops, ≥ 992px)
2635
+ xl: 1200, // Extra large devices (large desktops, ≥ 1200px)
2636
+ xxl: 1400, // Extra extra large devices (larger desktops, ≥ 1400px)
2637
+ };
2638
+ /**
2639
+ * Get current breakpoint based on window width
2640
+ */
2641
+ function getCurrentBreakpoint(width) {
2642
+ if (width >= BREAKPOINTS.xxl)
2643
+ return 'xxl';
2644
+ if (width >= BREAKPOINTS.xl)
2645
+ return 'xl';
2646
+ if (width >= BREAKPOINTS.lg)
2647
+ return 'lg';
2648
+ if (width >= BREAKPOINTS.md)
2649
+ return 'md';
2650
+ if (width >= BREAKPOINTS.sm)
2651
+ return 'sm';
2652
+ return 'xs';
2653
+ }
2654
+ /**
2655
+ * Hook to get current breakpoint
2656
+ */
2657
+ function useBreakpoint() {
2658
+ const [breakpoint, setBreakpoint] = React.useState(() => {
2659
+ if (typeof window === 'undefined')
2660
+ return 'lg';
2661
+ return getCurrentBreakpoint(window.innerWidth);
2662
+ });
2663
+ React.useEffect(() => {
2664
+ const handleResize = () => {
2665
+ setBreakpoint(getCurrentBreakpoint(window.innerWidth));
2666
+ };
2667
+ handleResize();
2668
+ window.addEventListener('resize', handleResize);
2669
+ return () => window.removeEventListener('resize', handleResize);
2670
+ }, []);
2671
+ return breakpoint;
2672
+ }
2673
+ /**
2674
+ * Hook to check if current viewport matches breakpoint
2675
+ */
2676
+ function useMediaQuery(query) {
2677
+ const [matches, setMatches] = React.useState(() => {
2678
+ if (typeof window === 'undefined')
2679
+ return false;
2680
+ return window.matchMedia(query).matches;
2681
+ });
2682
+ React.useEffect(() => {
2683
+ const mediaQuery = window.matchMedia(query);
2684
+ const handleChange = () => setMatches(mediaQuery.matches);
2685
+ handleChange();
2686
+ mediaQuery.addEventListener('change', handleChange);
2687
+ return () => mediaQuery.removeEventListener('change', handleChange);
2688
+ }, [query]);
2689
+ return matches;
2690
+ }
2691
+ /**
2692
+ * Check if mobile viewport (< 768px)
2693
+ */
2694
+ function useIsMobile() {
2695
+ return useMediaQuery(`(max-width: ${BREAKPOINTS.md - 1}px)`);
2696
+ }
2697
+ /**
2698
+ * Get responsive grid columns based on breakpoint
2699
+ */
2700
+ function getResponsiveColumns(breakpoint, defaultColumns = 4) {
2701
+ switch (breakpoint) {
2702
+ case 'xs':
2703
+ return Math.min(defaultColumns, 1); // 1 column on extra small
2704
+ case 'sm':
2705
+ return Math.min(defaultColumns, 2); // 2 columns on small
2706
+ case 'md':
2707
+ return Math.min(defaultColumns, 3); // 3 columns on medium
2708
+ case 'lg':
2709
+ return Math.min(defaultColumns, 4); // 4 columns on large
2710
+ case 'xl':
2711
+ return Math.min(defaultColumns, 5); // 5 columns on extra large
2712
+ case 'xxl':
2713
+ return defaultColumns; // Full columns on extra extra large
2714
+ default:
2715
+ return defaultColumns;
2716
+ }
2717
+ }
2718
+ /**
2719
+ * Hook to get responsive grid columns
2720
+ */
2721
+ function useResponsiveColumns(defaultColumns = 4) {
2722
+ const breakpoint = useBreakpoint();
2723
+ return getResponsiveColumns(breakpoint, defaultColumns);
2724
+ }
2725
+ /**
2726
+ * Minimum touch target size (WCAG AAA)
2727
+ */
2728
+ const MIN_TOUCH_TARGET = 44; // 44x44px minimum for touch
2729
+ /**
2730
+ * Get touch target size based on device type
2731
+ */
2732
+ function getTouchTargetSize(isMobile, baseSize = 32) {
2733
+ return isMobile ? Math.max(MIN_TOUCH_TARGET, baseSize) : baseSize;
2734
+ }
2735
+ /**
2736
+ * Hook to get responsive touch target size
2737
+ */
2738
+ function useTouchTargetSize(baseSize = 32) {
2739
+ const isMobile = useIsMobile();
2740
+ return getTouchTargetSize(isMobile, baseSize);
2741
+ }
2742
+ /**
2743
+ * Get responsive gap/padding for grids
2744
+ */
2745
+ function getResponsiveGap(breakpoint, baseGap = 12) {
2746
+ switch (breakpoint) {
2747
+ case 'xs':
2748
+ return Math.max(4, baseGap * 0.5); // 50% on extra small
2749
+ case 'sm':
2750
+ return Math.max(6, baseGap * 0.75); // 75% on small
2751
+ case 'md':
2752
+ case 'lg':
2753
+ return baseGap; // 100% on medium/large
2754
+ case 'xl':
2755
+ case 'xxl':
2756
+ return baseGap * 1.25; // 125% on extra large
2757
+ default:
2758
+ return baseGap;
2759
+ }
2760
+ }
2761
+ /**
2762
+ * Hook to get responsive gap
2763
+ */
2764
+ function useResponsiveGap(baseGap = 12) {
2765
+ const breakpoint = useBreakpoint();
2766
+ return getResponsiveGap(breakpoint, baseGap);
2767
+ }
2768
+ /**
2769
+ * Get responsive padding for containers
2770
+ */
2771
+ function getResponsivePadding(breakpoint, basePadding = 12) {
2772
+ switch (breakpoint) {
2773
+ case 'xs':
2774
+ return Math.max(8, basePadding * 0.67); // ~67% on extra small
2775
+ case 'sm':
2776
+ return Math.max(10, basePadding * 0.83); // ~83% on small
2777
+ case 'md':
2778
+ case 'lg':
2779
+ return basePadding; // 100% on medium/large
2780
+ case 'xl':
2781
+ case 'xxl':
2782
+ return basePadding * 1.33; // ~133% on extra large
2783
+ default:
2784
+ return basePadding;
2785
+ }
2786
+ }
2787
+ /**
2788
+ * Hook to get responsive padding
2789
+ */
2790
+ function useResponsivePadding(basePadding = 12) {
2791
+ const breakpoint = useBreakpoint();
2792
+ return getResponsivePadding(breakpoint, basePadding);
2793
+ }
2794
+
2554
2795
  /**
2555
2796
  * Pagination Component
2556
2797
  *
@@ -2565,16 +2806,29 @@ const Stats = ({ results: resultsProp, renderStats, className, style, theme: cus
2565
2806
  * --seekora-pagination-border
2566
2807
  * --seekora-pagination-radius
2567
2808
  */
2568
- /** Size-specific style tokens */
2569
- const SIZE_TOKENS = {
2570
- small: { paddingKey: 'small', fontSizeKey: 'small', minWidth: '32px' },
2571
- medium: { paddingKey: 'small', fontSizeKey: 'medium', minWidth: '40px' },
2572
- large: { paddingKey: 'medium', fontSizeKey: 'large', minWidth: '48px' },
2809
+ /** Size-specific style tokens (responsive) */
2810
+ const getSizeTokens = (size, isMobile) => {
2811
+ if (isMobile) {
2812
+ // Larger touch targets on mobile
2813
+ return {
2814
+ small: { paddingKey: 'small', fontSizeKey: 'small', minWidth: '40px', minHeight: '40px' },
2815
+ medium: { paddingKey: 'small', fontSizeKey: 'medium', minWidth: '44px', minHeight: '44px' },
2816
+ large: { paddingKey: 'medium', fontSizeKey: 'large', minWidth: '48px', minHeight: '48px' },
2817
+ }[size];
2818
+ }
2819
+ return {
2820
+ small: { paddingKey: 'small', fontSizeKey: 'small', minWidth: '32px', minHeight: '32px' },
2821
+ medium: { paddingKey: 'small', fontSizeKey: 'medium', minWidth: '40px', minHeight: '40px' },
2822
+ large: { paddingKey: 'medium', fontSizeKey: 'large', minWidth: '48px', minHeight: '48px' },
2823
+ }[size];
2573
2824
  };
2574
- 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', }) => {
2825
+ 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', }) => {
2575
2826
  const { theme } = useSearchContext();
2576
2827
  const { results: stateResults, currentPage: stateCurrentPage, setPage } = useSearchState();
2577
2828
  const paginationTheme = customTheme || {};
2829
+ const isMobile = useIsMobile();
2830
+ // Responsive maxPages - show fewer pages on mobile
2831
+ const maxPages = isMobile ? Math.min(maxPagesProp, 5) : maxPagesProp;
2578
2832
  // Use results from prop if provided, otherwise from state manager
2579
2833
  const results = resultsProp || stateResults;
2580
2834
  // Use currentPage from prop if provided, otherwise from state manager
@@ -2598,8 +2852,8 @@ const Pagination = ({ results: resultsProp, currentPage: currentPageProp, itemsP
2598
2852
  const resolvedShowPageInfo = showPageInfo !== undefined
2599
2853
  ? showPageInfo
2600
2854
  : variant === 'simple';
2601
- // Size tokens
2602
- const sizeTokens = SIZE_TOKENS[size];
2855
+ // Size tokens (responsive)
2856
+ const sizeTokens = getSizeTokens(size, isMobile);
2603
2857
  // CSS variable aware helpers — allow overrides via custom properties
2604
2858
  const cssVarBg = 'var(--seekora-pagination-bg, ' + theme.colors.background + ')';
2605
2859
  const cssVarColor = 'var(--seekora-pagination-color, ' + theme.colors.text + ')';
@@ -2619,7 +2873,7 @@ const Pagination = ({ results: resultsProp, currentPage: currentPageProp, itemsP
2619
2873
  };
2620
2874
  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: {
2621
2875
  padding: theme.spacing[sizeTokens.paddingKey],
2622
- margin: `0 ${theme.spacing.small}`,
2876
+ margin: isMobile ? `0 4px` : `0 ${theme.spacing.small}`,
2623
2877
  border: `1px solid ${cssVarBorder}`,
2624
2878
  borderRadius: cssVarRadius,
2625
2879
  backgroundColor: isActive ? cssVarActiveBg : cssVarBg,
@@ -2628,6 +2882,10 @@ const Pagination = ({ results: resultsProp, currentPage: currentPageProp, itemsP
2628
2882
  opacity: 1,
2629
2883
  fontSize: theme.typography.fontSize[sizeTokens.fontSizeKey],
2630
2884
  minWidth: sizeTokens.minWidth,
2885
+ minHeight: sizeTokens.minHeight,
2886
+ display: 'inline-flex',
2887
+ alignItems: 'center',
2888
+ justifyContent: 'center',
2631
2889
  ...(isActive && {
2632
2890
  fontWeight: 'bold',
2633
2891
  }),
@@ -3011,6 +3269,71 @@ const SortBy = ({ options, value: valueProp, defaultValue, onSortChange, renderS
3011
3269
  return null;
3012
3270
  };
3013
3271
 
3272
+ /**
3273
+ * useFilters Hook
3274
+ *
3275
+ * React hook for fetching filter values, searching facet values,
3276
+ * and retrieving filter schema via the dedicated Filters API.
3277
+ */
3278
+ const useFilters = (options) => {
3279
+ const { stateManager } = useSearchContext();
3280
+ const [filters, setFilters] = React.useState([]);
3281
+ const [schema, setSchema] = React.useState(null);
3282
+ const [loading, setLoading] = React.useState(false);
3283
+ const [error, setError] = React.useState(null);
3284
+ const mountedRef = React.useRef(true);
3285
+ const autoFetch = options?.autoFetch !== false;
3286
+ // Extract non-autoFetch options to pass to fetchFilters
3287
+ const fetchFilters = React.useCallback(async () => {
3288
+ setLoading(true);
3289
+ setError(null);
3290
+ try {
3291
+ const { autoFetch: _, ...filterOptions } = options || {};
3292
+ const response = await stateManager.fetchFilters(filterOptions);
3293
+ if (mountedRef.current) {
3294
+ setFilters(response?.filters || []);
3295
+ setLoading(false);
3296
+ }
3297
+ }
3298
+ catch (err) {
3299
+ if (mountedRef.current) {
3300
+ setError(err instanceof Error ? err : new Error(String(err)));
3301
+ setLoading(false);
3302
+ }
3303
+ }
3304
+ }, [stateManager, options?.facetBy, options?.maxFacetValues, options?.disjunctiveFacets?.join(',')]);
3305
+ // Refetch when query or refinements change
3306
+ React.useEffect(() => {
3307
+ if (!autoFetch)
3308
+ return;
3309
+ const unsubscribe = stateManager.subscribe((_state) => {
3310
+ fetchFilters();
3311
+ });
3312
+ return unsubscribe;
3313
+ }, [stateManager, autoFetch, fetchFilters]);
3314
+ // Fetch schema once on mount
3315
+ React.useEffect(() => {
3316
+ stateManager.getFiltersSchema().then((response) => {
3317
+ if (mountedRef.current) {
3318
+ setSchema(response);
3319
+ }
3320
+ }).catch(() => {
3321
+ // Schema fetch failure is non-critical
3322
+ });
3323
+ }, [stateManager]);
3324
+ // Cleanup
3325
+ React.useEffect(() => {
3326
+ return () => {
3327
+ mountedRef.current = false;
3328
+ };
3329
+ }, []);
3330
+ const searchFacetValues = React.useCallback((facetName, query) => stateManager.searchFacetValues(facetName, query), [stateManager]);
3331
+ const refetch = React.useCallback(async () => {
3332
+ await fetchFilters();
3333
+ }, [fetchFilters]);
3334
+ return { filters, schema, loading, error, searchFacetValues, refetch };
3335
+ };
3336
+
3014
3337
  /**
3015
3338
  * RangeSlider Component
3016
3339
  *
@@ -3327,10 +3650,12 @@ const CSS_VAR_DEFAULTS = {
3327
3650
  // ---------------------------------------------------------------------------
3328
3651
  // Component
3329
3652
  // ---------------------------------------------------------------------------
3330
- 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, }) => {
3653
+ 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, }) => {
3331
3654
  const { theme } = useSearchContext();
3332
3655
  const { results: stateResults, refinements, addRefinement, removeRefinement } = useSearchState();
3333
3656
  const facetsTheme = customTheme || {};
3657
+ // Use dedicated Filters API when useFiltersApi is enabled
3658
+ const { filters: filtersApiData, searchFacetValues } = useFilters(useFiltersApi ? { disjunctiveFacets } : { autoFetch: false });
3334
3659
  // expandedFacets is used for "Show more/less" in checkbox/color-swatch variants
3335
3660
  // AND for collapse/expand in collapsible variant.
3336
3661
  const [expandedFacets, setExpandedFacets] = React.useState({});
@@ -3338,11 +3663,27 @@ const Facets = ({ results: resultsProp, facets: facetsProp, onFacetChange, rende
3338
3663
  // Use results from prop if provided, otherwise from state manager
3339
3664
  const results = resultsProp || stateResults;
3340
3665
  // -------------------------------------------------------------------
3341
- // Extract facets from results
3666
+ // Extract facets from results (or Filters API)
3342
3667
  // -------------------------------------------------------------------
3343
3668
  const extractFacets = () => {
3344
3669
  if (facetsProp)
3345
3670
  return facetsProp;
3671
+ // When using Filters API, convert FilterField[] to Facet[]
3672
+ if (useFiltersApi && filtersApiData.length > 0) {
3673
+ log.verbose('Facets: Using Filters API data', { filterCount: filtersApiData.length });
3674
+ return filtersApiData.map((filter) => ({
3675
+ field: filter.field || '',
3676
+ label: filter.field || '',
3677
+ items: (filter.values || []).map((v) => ({
3678
+ value: v.value || '',
3679
+ count: v.count || 0,
3680
+ selected: refinements.some(r => r.field === filter.field && r.value === v.value),
3681
+ })),
3682
+ stats: filter.stats?.min != null && filter.stats?.max != null
3683
+ ? { min: filter.stats.min, max: filter.stats.max, avg: filter.stats.avg, sum: filter.stats.sum }
3684
+ : undefined,
3685
+ }));
3686
+ }
3346
3687
  // Try to get facets from various locations in the response
3347
3688
  // Primary location: data.facets (widget_mode response structure)
3348
3689
  const rawFacets = results?.data?.facets
@@ -4066,8 +4407,8 @@ const Z_INDEX$1 = {
4066
4407
  // ---------------------------------------------------------------------------
4067
4408
  // Component
4068
4409
  // ---------------------------------------------------------------------------
4069
- 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', }) => {
4070
- const { client: contextClient, theme } = useSearchContext();
4410
+ 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, }) => {
4411
+ const { client: contextClient, theme, stateManager } = useSearchContext();
4071
4412
  const { addRefinement, removeRefinement, refinements, search } = useSearchState();
4072
4413
  const client = providedClient || contextClient;
4073
4414
  const [open, setOpen] = React.useState(false);
@@ -4085,30 +4426,52 @@ const FacetDropdown = ({ field, placeholder = 'All Categories', className, theme
4085
4426
  if (providedOptions || !client)
4086
4427
  return;
4087
4428
  try {
4088
- log.verbose('FacetDropdown: Fetching facet values', { field });
4089
- // Perform a search with facet_by parameter to get facet counts
4090
- const response = await client.search('*', {
4091
- facet_by: field,
4092
- max_facet_values: 100,
4093
- per_page: 0, // We only need facets, not results
4094
- });
4095
- log.verbose('FacetDropdown: Search response', { response });
4096
- // Extract facet counts from the response
4097
- const facetsData = response?.data?.facets || response?.facets;
4098
- const facets = Array.isArray(facetsData) ? facetsData : [];
4099
- const targetFacet = facets.find((f) => f.field_name === field || f.field === field);
4100
- if (targetFacet && targetFacet.counts) {
4101
- const options = targetFacet.counts.map((count) => ({
4102
- value: count.value,
4103
- count: count.count,
4104
- label: count.value,
4105
- }));
4106
- setFacetOptions(options);
4107
- log.verbose('FacetDropdown: Facet options loaded', { field, count: options.length });
4429
+ log.verbose('FacetDropdown: Fetching facet values', { field, useFiltersApi });
4430
+ if (useFiltersApi) {
4431
+ // Use dedicated Filters API via state manager
4432
+ const response = await stateManager.fetchFilters({
4433
+ facetBy: field,
4434
+ maxFacetValues: 100,
4435
+ });
4436
+ const targetFilter = (response?.filters || []).find((f) => f.field === field);
4437
+ if (targetFilter && targetFilter.values) {
4438
+ const options = targetFilter.values.map((v) => ({
4439
+ value: v.value || '',
4440
+ count: v.count || 0,
4441
+ label: v.value || '',
4442
+ }));
4443
+ setFacetOptions(options);
4444
+ log.verbose('FacetDropdown: Facet options loaded via Filters API', { field, count: options.length });
4445
+ }
4446
+ else {
4447
+ log.verbose('FacetDropdown: No filter data found', { field });
4448
+ setFacetOptions([]);
4449
+ }
4108
4450
  }
4109
4451
  else {
4110
- log.verbose('FacetDropdown: No facet data found', { field, facets });
4111
- setFacetOptions([]);
4452
+ // Fallback: use search API with per_page=0
4453
+ const response = await client.search('*', {
4454
+ facet_by: field,
4455
+ max_facet_values: 100,
4456
+ per_page: 0,
4457
+ });
4458
+ log.verbose('FacetDropdown: Search response', { response });
4459
+ const facetsData = response?.data?.facets || response?.facets;
4460
+ const facets = Array.isArray(facetsData) ? facetsData : [];
4461
+ const targetFacet = facets.find((f) => f.field_name === field || f.field === field);
4462
+ if (targetFacet && targetFacet.counts) {
4463
+ const options = targetFacet.counts.map((count) => ({
4464
+ value: count.value,
4465
+ count: count.count,
4466
+ label: count.value,
4467
+ }));
4468
+ setFacetOptions(options);
4469
+ log.verbose('FacetDropdown: Facet options loaded', { field, count: options.length });
4470
+ }
4471
+ else {
4472
+ log.verbose('FacetDropdown: No facet data found', { field, facets });
4473
+ setFacetOptions([]);
4474
+ }
4112
4475
  }
4113
4476
  }
4114
4477
  catch (error) {
@@ -4119,7 +4482,7 @@ const FacetDropdown = ({ field, placeholder = 'All Categories', className, theme
4119
4482
  });
4120
4483
  setFacetOptions([]);
4121
4484
  }
4122
- }, [field, client, providedOptions]);
4485
+ }, [field, client, providedOptions, useFiltersApi]);
4123
4486
  React.useEffect(() => {
4124
4487
  if (!providedOptions && client) {
4125
4488
  fetchFacetValues();
@@ -4754,15 +5117,9 @@ const ClearRefinements = ({ clearsQuery = false, resetLabel = 'Clear all filters
4754
5117
  const SearchLayout = ({ sidebar, children, header, footer, sidebarWidth = '300px', className, style, theme: customTheme, showSidebarOnMobile = false, }) => {
4755
5118
  const { theme } = useSearchContext();
4756
5119
  const layoutTheme = customTheme || {};
4757
- const [isMobile, setIsMobile] = React.useState(false);
4758
- React.useEffect(() => {
4759
- const checkMobile = () => {
4760
- setIsMobile(window.innerWidth <= 768);
4761
- };
4762
- checkMobile();
4763
- window.addEventListener('resize', checkMobile);
4764
- return () => window.removeEventListener('resize', checkMobile);
4765
- }, []);
5120
+ const isMobile = useIsMobile();
5121
+ const responsivePadding = useResponsivePadding(parseInt(theme.spacing.medium) || 16);
5122
+ const responsiveGap = useResponsiveGap(parseInt(theme.spacing.large) || 24);
4766
5123
  return (React.createElement("div", { className: clsx(layoutTheme.container, className), style: {
4767
5124
  display: 'flex',
4768
5125
  flexDirection: 'column',
@@ -4773,7 +5130,7 @@ const SearchLayout = ({ sidebar, children, header, footer, sidebarWidth = '300px
4773
5130
  ...style,
4774
5131
  } },
4775
5132
  header && (React.createElement("header", { className: layoutTheme.header, style: {
4776
- padding: theme.spacing.medium,
5133
+ padding: responsivePadding,
4777
5134
  borderBottom: `1px solid ${theme.colors.border}`,
4778
5135
  backgroundColor: theme.colors.background,
4779
5136
  } }, header)),
@@ -4782,15 +5139,16 @@ const SearchLayout = ({ sidebar, children, header, footer, sidebarWidth = '300px
4782
5139
  flex: 1,
4783
5140
  width: '100%',
4784
5141
  maxWidth: '100%',
4785
- gap: theme.spacing.large,
4786
- padding: theme.spacing.medium,
5142
+ flexDirection: isMobile ? 'column' : 'row',
5143
+ gap: responsiveGap,
5144
+ padding: responsivePadding,
4787
5145
  backgroundColor: theme.colors.background,
4788
5146
  color: theme.colors.text,
4789
- overflow: 'hidden',
5147
+ overflow: isMobile ? 'visible' : 'hidden',
4790
5148
  } },
4791
5149
  sidebar && (!isMobile || showSidebarOnMobile) && (React.createElement("aside", { className: layoutTheme.sidebar, style: {
4792
- width: sidebarWidth,
4793
- minWidth: sidebarWidth,
5150
+ width: isMobile ? '100%' : sidebarWidth,
5151
+ minWidth: isMobile ? '100%' : sidebarWidth,
4794
5152
  flexShrink: 0,
4795
5153
  } }, sidebar)),
4796
5154
  React.createElement("main", { className: layoutTheme.main, style: {
@@ -4803,7 +5161,7 @@ const SearchLayout = ({ sidebar, children, header, footer, sidebarWidth = '300px
4803
5161
  overflow: 'auto',
4804
5162
  } }, children)),
4805
5163
  footer && (React.createElement("footer", { className: layoutTheme.footer, style: {
4806
- padding: theme.spacing.medium,
5164
+ padding: responsivePadding,
4807
5165
  borderTop: `1px solid ${theme.colors.border}`,
4808
5166
  backgroundColor: theme.colors.background,
4809
5167
  } }, footer))));
@@ -5084,7 +5442,7 @@ const HitsPerPage = ({ items, onHitsPerPageChange, renderSelect, className, styl
5084
5442
  * Displays search results with infinite scroll or "Show More" button
5085
5443
  * Accumulates results as user loads more pages
5086
5444
  */
5087
- 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, }) => {
5445
+ const InfiniteHits = ({ renderHit, renderEmpty, renderShowMore, showMoreButton = true, useInfiniteScroll = false, scrollThreshold = 0.1, fieldMapping, showMoreLabel = 'Show more', onHitClick, className, style, theme: customTheme, syncWithState = true, }) => {
5088
5446
  const { theme, stateManager } = useSearchContext();
5089
5447
  const { results, loading, currentPage, setPage } = useSearchState();
5090
5448
  const infiniteHitsTheme = customTheme || {};
@@ -5216,12 +5574,6 @@ const InfiniteHits = ({ renderHit, renderEmpty, showInitialLoading = false, rend
5216
5574
  textAlign: 'center',
5217
5575
  color: theme.colors.textSecondary,
5218
5576
  } }, "No results found"));
5219
- // Default loading state
5220
- const defaultRenderLoading = () => (React.createElement("div", { className: infiniteHitsTheme.loading, style: {
5221
- padding: theme.spacing.medium,
5222
- textAlign: 'center',
5223
- color: theme.colors.textSecondary,
5224
- } }, loadingLabel));
5225
5577
  // Default "Show More" button
5226
5578
  const defaultRenderShowMore = () => (React.createElement("button", { type: "button", onClick: handleShowMore, disabled: isLastPage || isLoadingMore, className: clsx(infiniteHitsTheme.loadMore, (isLastPage || isLoadingMore) && infiniteHitsTheme.loadMoreDisabled), style: {
5227
5579
  display: 'block',
@@ -5238,11 +5590,7 @@ const InfiniteHits = ({ renderHit, renderEmpty, showInitialLoading = false, rend
5238
5590
  : theme.borderRadius.medium,
5239
5591
  cursor: isLastPage || isLoadingMore ? 'not-allowed' : 'pointer',
5240
5592
  transition: theme.transitions?.fast || '150ms ease-in-out',
5241
- } }, isLoadingMore ? loadingLabel : isLastPage ? 'No more results' : showMoreLabel));
5242
- // Initial loading state (only when showInitialLoading: default no loading screen)
5243
- if (loading && accumulatedHits.length === 0 && showInitialLoading) {
5244
- return (React.createElement("div", { className: clsx(infiniteHitsTheme.root, className), style: style }, renderLoading ? renderLoading() : defaultRenderLoading()));
5245
- }
5593
+ } }, isLastPage ? 'No more results' : showMoreLabel));
5246
5594
  if (loading && accumulatedHits.length === 0) {
5247
5595
  return React.createElement("div", { className: clsx(infiniteHitsTheme.root, className), style: style });
5248
5596
  }
@@ -5255,7 +5603,6 @@ const InfiniteHits = ({ renderHit, renderEmpty, showInitialLoading = false, rend
5255
5603
  showMoreButton && !useInfiniteScroll && !isLastPage && (renderShowMore
5256
5604
  ? renderShowMore({ isLoading: isLoadingMore, isLastPage, onClick: handleShowMore })
5257
5605
  : defaultRenderShowMore()),
5258
- isLoadingMore && (renderLoading ? renderLoading() : defaultRenderLoading()),
5259
5606
  useInfiniteScroll && !isLastPage && (React.createElement("div", { ref: sentinelRef, className: infiniteHitsTheme.sentinel, style: { height: '1px', visibility: 'hidden' }, "aria-hidden": "true" }))));
5260
5607
  };
5261
5608
 
@@ -6047,16 +6394,11 @@ const MobileFiltersButton = ({ onClick, text = 'Filters', showCount = true, clas
6047
6394
  * - FrequentlyBoughtTogether: Bundle recommendations
6048
6395
  * - RecentlyViewed: User's recently viewed items
6049
6396
  */
6050
- const RelatedProducts = ({ productId, items: itemsProp, loading: loadingProp = false, showLoadingState = false, title = 'Related Products', maxItems = 6, renderItem, onItemClick, className, style, theme: customTheme, layout = 'horizontal', currencySymbol = '$', }) => {
6397
+ const RelatedProducts = ({ productId, items: itemsProp, loading: loadingProp = false, title = 'Related Products', maxItems = 6, renderItem, onItemClick, className, style, theme: customTheme, layout = 'horizontal', currencySymbol = '$', }) => {
6051
6398
  const { theme } = useSearchContext();
6052
- const recommendationTheme = customTheme || {};
6053
6399
  // If items are provided, use them directly
6054
6400
  const items = itemsProp?.slice(0, maxItems) || [];
6055
6401
  const loading = loadingProp;
6056
- if (loading && items.length === 0 && showLoadingState) {
6057
- return (React.createElement("div", { className: clsx(recommendationTheme.root, className), style: style },
6058
- React.createElement("div", { className: recommendationTheme.loading, style: getLoadingStyle(theme) }, "Loading related products...")));
6059
- }
6060
6402
  if (loading && items.length === 0)
6061
6403
  return null;
6062
6404
  if (items.length === 0) {
@@ -6064,15 +6406,10 @@ const RelatedProducts = ({ productId, items: itemsProp, loading: loadingProp = f
6064
6406
  }
6065
6407
  return (React.createElement(RecommendationSection, { title: title, items: items, renderItem: renderItem, onItemClick: onItemClick, className: className, style: style, theme: customTheme, layout: layout, currencySymbol: currencySymbol }));
6066
6408
  };
6067
- const TrendingItems = ({ items: itemsProp, loading: loadingProp = false, showLoadingState = false, title = 'Trending Now', maxItems = 8, renderItem, onItemClick, className, style, theme: customTheme, layout = 'horizontal', currencySymbol = '$', }) => {
6409
+ const TrendingItems = ({ items: itemsProp, loading: loadingProp = false, title = 'Trending Now', maxItems = 8, renderItem, onItemClick, className, style, theme: customTheme, layout = 'horizontal', currencySymbol = '$', }) => {
6068
6410
  const { theme } = useSearchContext();
6069
- const recommendationTheme = customTheme || {};
6070
6411
  const items = itemsProp?.slice(0, maxItems) || [];
6071
6412
  const loading = loadingProp;
6072
- if (loading && items.length === 0 && showLoadingState) {
6073
- return (React.createElement("div", { className: clsx(recommendationTheme.root, className), style: style },
6074
- React.createElement("div", { className: recommendationTheme.loading, style: getLoadingStyle(theme) }, "Loading trending items...")));
6075
- }
6076
6413
  if (loading && items.length === 0)
6077
6414
  return null;
6078
6415
  if (items.length === 0) {
@@ -6080,7 +6417,7 @@ const TrendingItems = ({ items: itemsProp, loading: loadingProp = false, showLoa
6080
6417
  }
6081
6418
  return (React.createElement(RecommendationSection, { title: title, items: items, renderItem: renderItem, onItemClick: onItemClick, className: className, style: style, theme: customTheme, layout: layout, currencySymbol: currencySymbol }));
6082
6419
  };
6083
- 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, }) => {
6420
+ 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, }) => {
6084
6421
  const { theme } = useSearchContext();
6085
6422
  const recommendationTheme = customTheme || {};
6086
6423
  const items = itemsProp?.slice(0, maxItems) || [];
@@ -6093,10 +6430,6 @@ const FrequentlyBoughtTogether = ({ productId, items: itemsProp, loading: loadin
6093
6430
  return sum + price;
6094
6431
  }, 0);
6095
6432
  }, [items]);
6096
- if (loading && items.length === 0 && showLoadingState) {
6097
- return (React.createElement("div", { className: clsx(recommendationTheme.root, className), style: style },
6098
- React.createElement("div", { className: recommendationTheme.loading, style: getLoadingStyle(theme) }, "Loading recommendations...")));
6099
- }
6100
6433
  if (loading && items.length === 0)
6101
6434
  return null;
6102
6435
  if (items.length === 0) {
@@ -6303,21 +6636,13 @@ const DefaultRecommendationItem = ({ item, theme: recommendationTheme, currencyS
6303
6636
  currencySymbol,
6304
6637
  typeof price === 'number' ? price.toFixed(2) : price)))));
6305
6638
  };
6306
- // Helper function
6307
- function getLoadingStyle(theme) {
6308
- return {
6309
- padding: theme.spacing.large,
6310
- textAlign: 'center',
6311
- color: theme.colors.textSecondary,
6312
- };
6313
- }
6314
6639
 
6315
6640
  /**
6316
6641
  * QuerySuggestions Component
6317
6642
  *
6318
6643
  * Standalone component for displaying query suggestions
6319
6644
  */
6320
- const QuerySuggestions = ({ query = '', maxSuggestions = 10, debounceMs = 300, minQueryLength = 2, onSuggestionClick, renderSuggestion, showLoadingState = false, renderLoading, renderEmpty, showTitle = false, title = 'Suggestions', className, style, theme: customTheme, }) => {
6645
+ const QuerySuggestions = ({ query = '', maxSuggestions = 10, debounceMs = 300, minQueryLength = 2, onSuggestionClick, renderSuggestion, renderEmpty, showTitle = false, title = 'Suggestions', className, style, theme: customTheme, }) => {
6321
6646
  const { client, theme } = useSearchContext();
6322
6647
  const [selectedIndex, setSelectedIndex] = React.useState(-1);
6323
6648
  const { suggestions, loading, error } = useQuerySuggestions({
@@ -6335,11 +6660,6 @@ const QuerySuggestions = ({ query = '', maxSuggestions = 10, debounceMs = 300, m
6335
6660
  "(",
6336
6661
  suggestion.count,
6337
6662
  ")"))));
6338
- const defaultRenderLoading = () => (React.createElement("div", { style: {
6339
- padding: theme.spacing.medium,
6340
- textAlign: 'center',
6341
- color: theme.colors.text,
6342
- } }, "Loading suggestions..."));
6343
6663
  const defaultRenderEmpty = () => (React.createElement("div", { style: {
6344
6664
  padding: theme.spacing.medium,
6345
6665
  textAlign: 'center',
@@ -6354,17 +6674,6 @@ const QuerySuggestions = ({ query = '', maxSuggestions = 10, debounceMs = 300, m
6354
6674
  if (query.length < minQueryLength) {
6355
6675
  return null;
6356
6676
  }
6357
- // When loading with no previous results, show loading only if showLoadingState (default: show previous results, no loading screen)
6358
- if (loading && displayedSuggestions.length === 0 && showLoadingState) {
6359
- return (React.createElement("div", { className: clsx(suggestionsTheme.container, className), style: style },
6360
- showTitle && (React.createElement("div", { className: suggestionsTheme.title, style: {
6361
- fontSize: theme.typography.fontSize.large,
6362
- fontWeight: 'bold',
6363
- marginBottom: theme.spacing.medium,
6364
- color: theme.colors.text,
6365
- } }, title)),
6366
- renderLoading ? renderLoading() : defaultRenderLoading()));
6367
- }
6368
6677
  if (error || (!loading && displayedSuggestions.length === 0)) {
6369
6678
  return (React.createElement("div", { className: clsx(suggestionsTheme.container, className), style: style },
6370
6679
  showTitle && (React.createElement("div", { className: suggestionsTheme.title, style: {
@@ -6906,15 +7215,6 @@ const defaultStyles$1 = {
6906
7215
  removeButtonVisible: {
6907
7216
  opacity: 1,
6908
7217
  },
6909
- loadingState: {
6910
- display: 'flex',
6911
- alignItems: 'center',
6912
- justifyContent: 'center',
6913
- padding: '24px 16px',
6914
- color: 'var(--seekora-text-secondary, inherit)',
6915
- fontSize: '14px',
6916
- gap: '8px',
6917
- },
6918
7218
  emptyState: {
6919
7219
  display: 'flex',
6920
7220
  flexDirection: 'column',
@@ -6968,14 +7268,11 @@ const ClockIcon$6 = ({ className, style }) => (React.createElement("svg", { clas
6968
7268
  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" })));
6969
7269
  const CloseIcon$1 = ({ className, style }) => (React.createElement("svg", { className: className, style: style, viewBox: "0 0 20 20", fill: "currentColor", width: "16", height: "16" },
6970
7270
  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" })));
6971
- const LoadingSpinner = ({ style }) => (React.createElement("svg", { style: { animation: 'spin 1s linear infinite', ...style }, viewBox: "0 0 24 24", width: "20", height: "20" },
6972
- React.createElement("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "3", fill: "none", opacity: "0.25" }),
6973
- React.createElement("path", { fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" })));
6974
7271
  // ============================================================================
6975
7272
  // Component
6976
7273
  // ============================================================================
6977
7274
  const QuerySuggestionsDropdown = React.forwardRef(function QuerySuggestionsDropdown(props, ref) {
6978
- 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;
7275
+ 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;
6979
7276
  const { client, theme } = useSearchContext();
6980
7277
  const containerRef = React.useRef(null);
6981
7278
  const [activeIndex, setActiveIndex] = React.useState(-1);
@@ -7157,9 +7454,6 @@ const QuerySuggestionsDropdown = React.forwardRef(function QuerySuggestionsDropd
7157
7454
  ...animationStyle,
7158
7455
  ...style,
7159
7456
  } },
7160
- loading && showLoading && (React.createElement("div", { className: classNames.loadingState, style: defaultStyles$1.loadingState }, renderLoading ? renderLoading() : (React.createElement(React.Fragment, null,
7161
- React.createElement(LoadingSpinner, null),
7162
- React.createElement("span", null, "Searching..."))))),
7163
7457
  showRecent && (React.createElement("div", { className: clsx('seekora-suggestions-section', classNames.section, classNames.recentSearches) },
7164
7458
  React.createElement("div", { className: classNames.sectionTitle, style: defaultStyles$1.sectionTitle }, "Recent Searches"),
7165
7459
  recentSearches.slice(0, maxRecentSearches).map((search, index) => {
@@ -7406,14 +7700,6 @@ const styles$2 = {
7406
7700
  fontSize: '12px',
7407
7701
  color: 'var(--seekora-text-secondary, inherit)',
7408
7702
  },
7409
- loadingOverlay: {
7410
- position: 'absolute',
7411
- inset: 0,
7412
- display: 'flex',
7413
- alignItems: 'center',
7414
- justifyContent: 'center',
7415
- backgroundColor: 'rgba(255, 255, 255, 0.1)',
7416
- },
7417
7703
  emptyState: {
7418
7704
  padding: '32px 16px',
7419
7705
  textAlign: 'center',
@@ -7441,7 +7727,7 @@ const CloseIcon = () => (React.createElement("svg", { viewBox: "0 0 20 20", fill
7441
7727
  // Component
7442
7728
  // ============================================================================
7443
7729
  const RichQuerySuggestions = React.forwardRef(function RichQuerySuggestions(props, ref) {
7444
- 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;
7730
+ 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;
7445
7731
  const { client } = useSearchContext();
7446
7732
  const containerRef = React.useRef(null);
7447
7733
  const [activeIndex, setActiveIndex] = React.useState(-1);
@@ -7660,7 +7946,6 @@ const RichQuerySuggestions = React.forwardRef(function RichQuerySuggestions(prop
7660
7946
  } },
7661
7947
  header && React.createElement("div", { style: styles$2.header }, header),
7662
7948
  React.createElement("div", { style: { ...styles$2.content, maxHeight } },
7663
- loading && showLoadingOverlay && (React.createElement("div", { style: styles$2.loadingOverlay }, renderLoading ? renderLoading() : React.createElement("span", null, "Loading..."))),
7664
7949
  enabledSections.map((section, index) => {
7665
7950
  let content = null;
7666
7951
  switch (section.id) {
@@ -8534,7 +8819,7 @@ const ClearIcon$1 = () => (React.createElement("svg", { viewBox: "0 0 20 20", fi
8534
8819
  // Component
8535
8820
  // ============================================================================
8536
8821
  const SearchBarWithSuggestions = React.forwardRef(function SearchBarWithSuggestions(props, ref) {
8537
- 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;
8822
+ 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;
8538
8823
  const { client } = useSearchContext();
8539
8824
  const inputRef = React.useRef(null);
8540
8825
  const dropdownRef = React.useRef(null);
@@ -8989,7 +9274,7 @@ const inputStyles = {
8989
9274
  color: 'var(--seekora-text-primary, inherit)',
8990
9275
  fontFamily: 'inherit',
8991
9276
  };
8992
- function SearchInput({ placeholder = 'Search...', autoFocus = false, showClearButton = true, closeOnBlur = true, leftIcon, className, style, inputClassName, inputStyle, ariaLabel = 'Search', }) {
9277
+ function SearchInput({ placeholder = 'Powered by Seekora', autoFocus = false, showClearButton = true, closeOnBlur = true, leftIcon, className, style, inputClassName, inputStyle, ariaLabel = 'Search', }) {
8993
9278
  const { query, setQuery, isOpen, setIsOpen, navigateNext, navigatePrev, selectActive, close, } = useSuggestionsContext();
8994
9279
  const inputRef = React.useRef(null);
8995
9280
  const handleFocus = React.useCallback(() => {
@@ -9197,17 +9482,19 @@ const defaultItemStyle = {
9197
9482
  whiteSpace: 'nowrap',
9198
9483
  };
9199
9484
  function SuggestionItem({ suggestion, index, isActive, onSelect, className, style, enableHighlightMarkup = true, highlightMarkupOptions, renderHighlight, }) {
9485
+ const [isHovered, setIsHovered] = React.useState(false);
9200
9486
  const displayText = suggestion.highlightedQuery ?? suggestion.query;
9201
9487
  const content = renderHighlight != null
9202
9488
  ? renderHighlight(displayText)
9203
9489
  : enableHighlightMarkup
9204
9490
  ? parseHighlightMarkup(displayText ?? '', highlightMarkupOptions)
9205
9491
  : (suggestion.query ?? displayText ?? '');
9206
- 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: {
9492
+ const showHighlight = isActive || isHovered;
9493
+ 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: {
9207
9494
  ...defaultItemStyle,
9208
- ...(isActive ? { backgroundColor: 'var(--seekora-bg-hover, #f3f4f6)' } : {}),
9495
+ ...(showHighlight ? { backgroundColor: 'var(--seekora-bg-hover, #f3f4f6)' } : {}),
9209
9496
  ...style,
9210
- }, onMouseDown: (e) => {
9497
+ }, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), onMouseDown: (e) => {
9211
9498
  e.preventDefault();
9212
9499
  onSelect();
9213
9500
  } },
@@ -9226,18 +9513,12 @@ const listStyle = {
9226
9513
  margin: 0,
9227
9514
  padding: '4px 0',
9228
9515
  };
9229
- function SuggestionList({ maxItems = 10, className, style, listClassName, showLoadingState = false, renderLoading, enableHighlightMarkup = true, highlightMarkupOptions, renderItem, }) {
9516
+ function SuggestionList({ maxItems = 10, className, style, listClassName, enableHighlightMarkup = true, highlightMarkupOptions, renderItem, }) {
9230
9517
  const { suggestions, activeIndex, loading, selectSuggestion, getAllNavigableItems, } = useSuggestionsContext();
9231
9518
  const items = suggestions.slice(0, maxItems);
9232
9519
  const navigableItems = getAllNavigableItems();
9233
9520
  const suggestionStartIndex = navigableItems.findIndex((n) => n.type === 'suggestion');
9234
9521
  const activeIsInSuggestions = suggestionStartIndex >= 0 && activeIndex >= suggestionStartIndex && activeIndex < suggestionStartIndex + items.length;
9235
- // When loading with no previous results, show loading only if showLoadingState (default: don't show loading screen)
9236
- if (loading && items.length === 0 && showLoadingState) {
9237
- if (renderLoading)
9238
- return React.createElement(React.Fragment, null, renderLoading());
9239
- return (React.createElement("div", { className: clsx('seekora-suggestions-loading', className), style: { padding: 16, color: 'var(--seekora-text-secondary, #6b7280)', fontSize: '0.875rem', ...style } }, "Loading..."));
9240
- }
9241
9522
  // When loading with previous results, show previous results (no loading UI)
9242
9523
  if (items.length === 0)
9243
9524
  return null;
@@ -9367,10 +9648,66 @@ function ImageZoom({ src, alt = '', mode = 'both', zoomLevel = 2.5, className, s
9367
9648
  const rect = containerRef.current.getBoundingClientRect();
9368
9649
  const bgPosX = (cursorPos.x / rect.width) * 100;
9369
9650
  const bgPosY = (cursorPos.y / rect.height) * 100;
9651
+ // Calculate available space in all directions
9652
+ const viewportWidth = window.innerWidth;
9653
+ const viewportHeight = window.innerHeight;
9654
+ const gap = 16;
9655
+ const mouseBuffer = 50; // Minimum distance from mouse cursor
9656
+ // Calculate mouse position in viewport
9657
+ const mouseX = rect.left + cursorPos.x;
9658
+ const mouseY = rect.top + cursorPos.y;
9659
+ // Calculate space available in each direction
9660
+ const spaceRight = viewportWidth - rect.right - gap;
9661
+ const spaceLeft = rect.left - gap;
9662
+ // Determine optimal horizontal position
9663
+ let left;
9664
+ let top = rect.top;
9665
+ // Try right side first (default Amazon-style)
9666
+ if (spaceRight >= zoomPanelSize.width) {
9667
+ left = rect.right + gap;
9668
+ }
9669
+ // Try left side
9670
+ else if (spaceLeft >= zoomPanelSize.width) {
9671
+ left = rect.left - zoomPanelSize.width - gap;
9672
+ }
9673
+ // Not enough space on either side - center on the side with more space
9674
+ else if (spaceRight > spaceLeft) {
9675
+ left = rect.right + gap;
9676
+ }
9677
+ else {
9678
+ left = Math.max(gap, rect.left - zoomPanelSize.width - gap);
9679
+ }
9680
+ // Adjust vertical position to keep within viewport
9681
+ // Try to align with image top by default
9682
+ if (rect.top + zoomPanelSize.height > viewportHeight - gap) {
9683
+ // Panel would overflow bottom - adjust upward
9684
+ top = Math.max(gap, viewportHeight - zoomPanelSize.height - gap);
9685
+ }
9686
+ // Ensure panel doesn't overlap with mouse cursor
9687
+ const panelRight = left + zoomPanelSize.width;
9688
+ const panelBottom = top + zoomPanelSize.height;
9689
+ // Check if mouse is inside the panel area
9690
+ if (mouseX >= left - mouseBuffer &&
9691
+ mouseX <= panelRight + mouseBuffer &&
9692
+ mouseY >= top - mouseBuffer &&
9693
+ mouseY <= panelBottom + mouseBuffer) {
9694
+ // Mouse is too close - try to reposition
9695
+ // If on right side and there's space on left, flip to left
9696
+ if (left > rect.right && spaceLeft >= zoomPanelSize.width) {
9697
+ left = rect.left - zoomPanelSize.width - gap;
9698
+ }
9699
+ // If on left side and there's space on right, flip to right
9700
+ else if (left < rect.left && spaceRight >= zoomPanelSize.width) {
9701
+ left = rect.right + gap;
9702
+ }
9703
+ }
9704
+ // Clamp to viewport boundaries
9705
+ left = Math.max(gap, Math.min(left, viewportWidth - zoomPanelSize.width - gap));
9706
+ top = Math.max(gap, Math.min(top, viewportHeight - zoomPanelSize.height - gap));
9370
9707
  return {
9371
- position: 'fixed', // Changed from absolute to fixed for better positioning
9372
- top: rect.top,
9373
- left: rect.right + 16, // 16px gap from the image
9708
+ position: 'fixed',
9709
+ top,
9710
+ left,
9374
9711
  width: zoomPanelSize.width,
9375
9712
  height: zoomPanelSize.height,
9376
9713
  backgroundImage: `url(${src})`,
@@ -9407,7 +9744,6 @@ function ImageZoom({ src, alt = '', mode = 'both', zoomLevel = 2.5, className, s
9407
9744
  if (!containerRef.current || !imageRef.current)
9408
9745
  return {};
9409
9746
  const rect = containerRef.current.getBoundingClientRect();
9410
- imageRef.current.getBoundingClientRect();
9411
9747
  return {
9412
9748
  position: 'absolute',
9413
9749
  width: rect.width * zoomLevel,
@@ -9630,6 +9966,11 @@ function ImageDisplay({ images, variant = 'single', alt = '', className, style,
9630
9966
  const [hovering, setHovering] = React.useState(false);
9631
9967
  const safeImages = Array.isArray(images) ? images.filter(Boolean) : [];
9632
9968
  const current = safeImages[index] ?? safeImages[0];
9969
+ // Responsive values
9970
+ const isMobile = useIsMobile();
9971
+ const arrowSize = useTouchTargetSize(32); // 44px on mobile, 32px on desktop
9972
+ const thumbSize = isMobile ? 56 : 48; // Larger thumbnails on mobile
9973
+ const responsiveGap = useResponsiveGap(8);
9633
9974
  if (safeImages.length === 0) {
9634
9975
  return React.createElement("div", { className: clsx('seekora-img-display', 'seekora-img-placeholder', className), style: { ...imgBaseStyle, ...style }, "aria-hidden": true });
9635
9976
  }
@@ -9666,22 +10007,24 @@ function ImageDisplay({ images, variant = 'single', alt = '', className, style,
9666
10007
  return (React.createElement("div", { className: clsx('seekora-img-display', 'seekora-img-carousel', className), style: { position: 'relative', ...style } },
9667
10008
  mainImage,
9668
10009
  safeImages.length > 1 && (React.createElement(React.Fragment, null,
9669
- 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"),
9670
- 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"),
10010
+ 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"),
10011
+ 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"),
9671
10012
  showDots && (React.createElement("div", { className: "seekora-img-carousel-dots", style: {
9672
10013
  position: 'absolute',
9673
- bottom: 8,
10014
+ bottom: isMobile ? 12 : 8,
9674
10015
  left: '50%',
9675
10016
  transform: 'translateX(-50%)',
9676
10017
  display: 'flex',
9677
- gap: 6,
9678
- padding: '6px 12px',
10018
+ gap: isMobile ? 8 : 6,
10019
+ padding: isMobile ? '8px 16px' : '6px 12px',
9679
10020
  backgroundColor: 'var(--seekora-carousel-dots-bg, rgba(0,0,0,0.5))',
9680
10021
  borderRadius: 12,
9681
10022
  zIndex: 10,
9682
10023
  } }, 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: {
9683
- width: 8,
9684
- height: 8,
10024
+ width: isMobile ? 10 : 8,
10025
+ height: isMobile ? 10 : 8,
10026
+ minWidth: isMobile ? 44 : 32, // Ensure touch target
10027
+ minHeight: isMobile ? 44 : 32,
9685
10028
  borderRadius: '50%',
9686
10029
  border: 'none',
9687
10030
  padding: 0,
@@ -9693,26 +10036,28 @@ function ImageDisplay({ images, variant = 'single', alt = '', className, style,
9693
10036
  if (variant === 'thumbStrip' || variant === 'thumbList') {
9694
10037
  const thumbMainStyle = style?.aspectRatio ? { ...imgBaseStyle, aspectRatio: style.aspectRatio } : imgBaseStyle;
9695
10038
  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" }));
9696
- return (React.createElement("div", { className: clsx('seekora-img-display', 'seekora-img-thumbstrip', className), style: { display: 'flex', flexDirection: 'column', gap: 8, ...style } },
10039
+ return (React.createElement("div", { className: clsx('seekora-img-display', 'seekora-img-thumbstrip', className), style: { display: 'flex', flexDirection: 'column', gap: responsiveGap, ...style } },
9697
10040
  mainImage,
9698
- 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(); } },
10041
+ 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(); } },
9699
10042
  React.createElement("img", { src: src, alt: "", style: { width: '100%', height: '100%', objectFit: 'cover' } })))))));
9700
10043
  }
9701
10044
  return React.createElement("img", { src: current, alt: alt, className: clsx('seekora-img-display', className), style: { ...imgBaseStyle, ...style }, loading: "lazy" });
9702
10045
  }
9703
- function arrowStyle(left) {
10046
+ function arrowStyle(left, size = 32) {
9704
10047
  return {
9705
10048
  position: 'absolute',
9706
10049
  top: '50%',
9707
- [left ? 'left' : 'right']: 8,
10050
+ [left ? 'left' : 'right']: size >= 44 ? 4 : 8, // Less offset on mobile for larger buttons
9708
10051
  transform: 'translateY(-50%)',
9709
- width: 32,
9710
- height: 32,
10052
+ width: size,
10053
+ height: size,
10054
+ minWidth: size,
10055
+ minHeight: size,
9711
10056
  borderRadius: '50%',
9712
10057
  border: 'none',
9713
10058
  backgroundColor: 'var(--seekora-carousel-btn-bg, rgba(255, 255, 255, 0.9))',
9714
10059
  color: 'var(--seekora-carousel-btn-text, #111)',
9715
- fontSize: '1.25rem',
10060
+ fontSize: size >= 44 ? '1.5rem' : '1.25rem', // Larger font on mobile
9716
10061
  fontWeight: 'bold',
9717
10062
  cursor: 'pointer',
9718
10063
  display: 'flex',
@@ -9833,7 +10178,7 @@ const cardStyle$1 = {
9833
10178
  textAlign: 'left',
9834
10179
  fontSize: 'inherit',
9835
10180
  fontFamily: 'inherit',
9836
- transition: `background-color ${TRANSITIONS$1.fast}`,
10181
+ transition: `background-color ${TRANSITIONS$1.fast}, box-shadow ${TRANSITIONS$1.fast}`,
9837
10182
  };
9838
10183
  const imgStyle$1 = {
9839
10184
  width: '100%',
@@ -9849,28 +10194,51 @@ function ItemCard({ item, position, onSelect, className, style, asLink = true, i
9849
10194
  const title = item.title ?? item.primaryText ?? '';
9850
10195
  const description = item.description ?? item.secondaryText;
9851
10196
  const href = item.url;
10197
+ const isMobile = useIsMobile();
10198
+ const horizontalGap = useResponsiveGap(12);
9852
10199
  const isHorizontal = layout === 'horizontal';
9853
- const imageBlock = images.length > 0 ? (React.createElement("div", { style: { position: 'relative', overflow: 'hidden', borderRadius: 4, ...(isHorizontal ? { minWidth: 80, flexBasis: '20%', maxWidth: 120, flexShrink: 0 } : {}) } },
10200
+ const [isHovered, setIsHovered] = React.useState(false);
10201
+ // Responsive dimensions for horizontal layout
10202
+ const imageMinWidth = isMobile ? 60 : 80;
10203
+ const imageMaxWidth = isMobile ? 100 : 120;
10204
+ const imageFlex = isMobile ? '20%' : '20%';
10205
+ const imageHeight = isMobile ? 60 : 80;
10206
+ // Responsive font sizes
10207
+ const titleFontSize = isMobile ? '0.9375rem' : '0.875rem';
10208
+ const descriptionFontSize = isMobile ? '0.875rem' : '0.8125rem';
10209
+ const textGap = isMobile ? 6 : 4;
10210
+ const lineClamp = isMobile ? 3 : 2; // More lines on mobile
10211
+ // Hover style
10212
+ const cardHoverStyle = isHovered
10213
+ ? {
10214
+ backgroundColor: 'var(--seekora-card-hover-bg, var(--seekora-bg-hover, #f9fafb))',
10215
+ boxShadow: 'var(--seekora-card-hover-shadow, 0 2px 8px rgba(0, 0, 0, 0.08))',
10216
+ }
10217
+ : {};
10218
+ const imageBlock = images.length > 0 ? (React.createElement("div", { style: { position: 'relative', overflow: 'hidden', borderRadius: 4, ...(isHorizontal ? { minWidth: imageMinWidth, flexBasis: imageFlex, maxWidth: imageMaxWidth, flexShrink: 0 } : {}) } },
9854
10219
  React.createElement(ImageDisplay, { images: images, variant: imageVariant, alt: String(title), className: "seekora-item-card-image" }),
9855
- 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 }));
9856
- const textBlock = (React.createElement("div", { style: isHorizontal ? { display: 'flex', flexDirection: 'column', gap: 4, flex: 1, minWidth: 0 } : undefined },
9857
- 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)),
9858
- 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,
10220
+ 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 }));
10221
+ const textBlock = (React.createElement("div", { style: isHorizontal ? { display: 'flex', flexDirection: 'column', gap: textGap, flex: 1, minWidth: 0 } : undefined },
10222
+ 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)),
10223
+ 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,
9859
10224
  actionButtons && actionButtons.length > 0 && actionButtonsPosition === 'inline' && (React.createElement(ActionButtons, { buttons: actionButtons, position: "inline", showLabels: showActionLabels, size: "small", layout: "horizontal" }))));
9860
- const content = isHorizontal ? (React.createElement("div", { style: { display: 'flex', gap: 12, alignItems: 'flex-start' } },
10225
+ const content = isHorizontal ? (React.createElement("div", { style: { display: 'flex', gap: horizontalGap, alignItems: 'flex-start' } },
9861
10226
  imageBlock,
9862
10227
  textBlock)) : (React.createElement(React.Fragment, null,
9863
10228
  imageBlock,
9864
10229
  textBlock));
9865
10230
  const commonProps = {
9866
- className: clsx('seekora-item-card', isHorizontal && 'seekora-item-card--horizontal', className),
10231
+ className: clsx('seekora-item-card', isHorizontal && 'seekora-item-card--horizontal', isHovered && 'seekora-item-card--hover', className),
9867
10232
  style: {
9868
10233
  ...cardStyle$1,
9869
10234
  ...(isHorizontal ? { flexDirection: 'row' } : {}),
10235
+ ...cardHoverStyle,
9870
10236
  ...style,
9871
10237
  },
9872
10238
  'data-position': position,
9873
10239
  onClick: onSelect,
10240
+ onMouseEnter: () => setIsHovered(true),
10241
+ onMouseLeave: () => setIsHovered(false),
9874
10242
  onMouseDown: onSelect ? (e) => { e.preventDefault(); onSelect(); } : undefined,
9875
10243
  };
9876
10244
  if (asLink && href) {
@@ -9888,11 +10256,6 @@ function ItemCard({ item, position, onSelect, className, style, asLink = true, i
9888
10256
  */
9889
10257
  const SPACING$1 = {
9890
10258
  md: 12};
9891
- const defaultGridStyle = {
9892
- display: 'grid',
9893
- gap: SPACING$1.md,
9894
- padding: SPACING$1.md,
9895
- };
9896
10259
  function toGenericItem(item, getItemId, getItemTitle, getItemImage, getItemDescription, getItemUrl) {
9897
10260
  return {
9898
10261
  id: getItemId(item),
@@ -9904,12 +10267,18 @@ function toGenericItem(item, getItemId, getItemTitle, getItemImage, getItemDescr
9904
10267
  };
9905
10268
  }
9906
10269
  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, }) {
10270
+ // Responsive columns and spacing
10271
+ const responsiveColumns = useResponsiveColumns(columns);
10272
+ const responsiveGap = useResponsiveGap(SPACING$1.md);
10273
+ const responsivePadding = useResponsivePadding(SPACING$1.md);
9907
10274
  const slice = items.slice(0, maxItems);
9908
10275
  if (slice.length === 0)
9909
10276
  return null;
9910
10277
  const gridStyle = {
9911
- ...defaultGridStyle,
9912
- gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
10278
+ display: 'grid',
10279
+ gap: responsiveGap,
10280
+ padding: responsivePadding,
10281
+ gridTemplateColumns: `repeat(${responsiveColumns}, minmax(0, 1fr))`,
9913
10282
  };
9914
10283
  return (React.createElement("div", { className: clsx('seekora-item-grid', className), style: style },
9915
10284
  React.createElement("div", { className: clsx('seekora-item-grid-inner', gridClassName), style: gridStyle }, slice.map((item, i) => {
@@ -10708,6 +11077,12 @@ const getAvailability$1 = (optionName, value, options, variants, selectedValues)
10708
11077
  };
10709
11078
  function VariantSwatches({ options, visibleOptions, maxValues = 5, colorMap, selectedValues, variants, showAvailability = true, onSwatchHover, onSwatchClick, className, style, }) {
10710
11079
  const [expandedOptions, setExpandedOptions] = React.useState(new Set());
11080
+ const isMobile = useIsMobile();
11081
+ // Responsive sizing
11082
+ const colorSwatchSize = isMobile ? 20 : 14; // Larger on mobile for touch
11083
+ const textSwatchPadding = isMobile ? '4px 10px' : '1px 6px';
11084
+ const textSwatchFontSize = isMobile ? '0.75rem' : '0.6875rem';
11085
+ const gap = isMobile ? 6 : 4;
10711
11086
  if (!options || options.length === 0)
10712
11087
  return null;
10713
11088
  const filtered = visibleOptions
@@ -10728,14 +11103,14 @@ function VariantSwatches({ options, visibleOptions, maxValues = 5, colorMap, sel
10728
11103
  return next;
10729
11104
  });
10730
11105
  };
10731
- return (React.createElement("div", { className: clsx('seekora-variant-swatches', className), style: { display: 'flex', flexDirection: 'column', gap: 4, ...style } }, filtered.map((option) => {
11106
+ return (React.createElement("div", { className: clsx('seekora-variant-swatches', className), style: { display: 'flex', flexDirection: 'column', gap, ...style } }, filtered.map((option) => {
10732
11107
  const isColor = isColorOption$1(option.name);
10733
11108
  const isExpanded = expandedOptions.has(option.name);
10734
11109
  const visible = isExpanded ? option.values : option.values.slice(0, maxValues);
10735
11110
  const overflow = option.values.length - maxValues;
10736
11111
  const hasOverflow = overflow > 0;
10737
11112
  const selectedValue = selectedValues?.[option.name];
10738
- return (React.createElement("div", { key: option.name, className: "seekora-variant-swatch-group", style: { display: 'flex', alignItems: 'center', gap: 4, flexWrap: 'wrap' } },
11113
+ return (React.createElement("div", { key: option.name, className: "seekora-variant-swatch-group", style: { display: 'flex', alignItems: 'center', gap, flexWrap: 'wrap' } },
10739
11114
  visible.map((value) => {
10740
11115
  const color = isColor ? resolveColor$1(value, colorMap) : null;
10741
11116
  const isSelected = selectedValue === value;
@@ -10744,9 +11119,13 @@ function VariantSwatches({ options, visibleOptions, maxValues = 5, colorMap, sel
10744
11119
  : true;
10745
11120
  if (color) {
10746
11121
  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: {
10747
- display: 'inline-block',
10748
- width: 14,
10749
- height: 14,
11122
+ display: 'inline-flex',
11123
+ alignItems: 'center',
11124
+ justifyContent: 'center',
11125
+ width: colorSwatchSize,
11126
+ height: colorSwatchSize,
11127
+ minWidth: colorSwatchSize,
11128
+ minHeight: colorSwatchSize,
10750
11129
  borderRadius: '50%',
10751
11130
  backgroundColor: color,
10752
11131
  border: isSelected
@@ -10778,9 +11157,12 @@ function VariantSwatches({ options, visibleOptions, maxValues = 5, colorMap, sel
10778
11157
  } }))));
10779
11158
  }
10780
11159
  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: {
10781
- display: 'inline-block',
10782
- padding: '1px 6px',
10783
- fontSize: '0.6875rem',
11160
+ display: 'inline-flex',
11161
+ alignItems: 'center',
11162
+ justifyContent: 'center',
11163
+ padding: textSwatchPadding,
11164
+ minHeight: isMobile ? 32 : 24, // Minimum touch target on mobile
11165
+ fontSize: textSwatchFontSize,
10784
11166
  borderRadius: 3,
10785
11167
  border: isSelected
10786
11168
  ? '1px solid var(--seekora-primary, #111827)'
@@ -10807,11 +11189,12 @@ function VariantSwatches({ options, visibleOptions, maxValues = 5, colorMap, sel
10807
11189
  } }, value));
10808
11190
  }),
10809
11191
  hasOverflow && (React.createElement("button", { type: "button", className: "seekora-variant-swatch--overflow", onClick: (e) => toggleExpanded(option.name, e), style: {
10810
- fontSize: '0.6875rem',
11192
+ fontSize: textSwatchFontSize,
10811
11193
  color: 'var(--seekora-primary, #2563eb)',
10812
11194
  background: 'none',
10813
11195
  border: 'none',
10814
- padding: 0,
11196
+ padding: isMobile ? '4px 8px' : 0,
11197
+ minHeight: isMobile ? 32 : 'auto', // Touch target on mobile
10815
11198
  cursor: 'pointer',
10816
11199
  textDecoration: 'underline',
10817
11200
  fontWeight: 500,
@@ -10846,16 +11229,24 @@ function ImageBlock({ images, title, imageVariant, aspectRatio, enableZoom, zoom
10846
11229
  }
10847
11230
  /** minimal: image, title, price (current default behavior) */
10848
11231
  function MinimalLayout({ images, title, price, product, imageVariant, displayConfig, enableImageZoom, imageZoomMode, imageZoomLevel }) {
11232
+ const isMobile = useIsMobile();
11233
+ const titleFontSize = isMobile ? '0.9375rem' : '0.875rem'; // Slightly larger on mobile
11234
+ const priceFontSize = isMobile ? '0.9375rem' : '0.875rem';
10849
11235
  return (React.createElement(React.Fragment, null,
10850
11236
  React.createElement(ImageBlock, { images: images, title: title, imageVariant: imageVariant, aspectRatio: displayConfig.imageAspectRatio, enableZoom: enableImageZoom, zoomMode: imageZoomMode, zoomLevel: imageZoomLevel }),
10851
- React.createElement("span", { className: "seekora-product-card__title", style: { fontSize: '0.875rem', fontWeight: 500, lineHeight: 1.4, overflow: 'hidden', textOverflow: 'ellipsis' } }, title),
10852
- 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)' } },
11237
+ 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),
11238
+ 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)' } },
10853
11239
  product.currency ?? '$',
10854
11240
  price.toFixed(2)))));
10855
11241
  }
10856
11242
  /** standard: image, badges, brand, title, price + compare price, color swatches */
10857
11243
  function StandardLayout({ images, title, price, comparePrice, brand, badges, options, variants, product, imageVariant, displayConfig, onVariantHover, onVariantClick, selectedVariants, actionButtons, actionButtonsPosition, showActionLabels, enableImageZoom, imageZoomMode, imageZoomLevel, }) {
10858
11244
  const cfg = displayConfig;
11245
+ const isMobile = useIsMobile();
11246
+ const bodyGap = isMobile ? 6 : 4; // More gap on mobile
11247
+ const titleFontSize = isMobile ? '0.9375rem' : '0.875rem';
11248
+ const brandFontSize = isMobile ? '0.8125rem' : '0.75rem';
11249
+ const priceFontSize = isMobile ? '0.9375rem' : '0.875rem';
10859
11250
  // Normalize position: 'overlay-*' positions render over image, everything else renders inline
10860
11251
  const isOverlayPosition = actionButtonsPosition?.startsWith('overlay');
10861
11252
  return (React.createElement(React.Fragment, null,
@@ -10863,11 +11254,11 @@ function StandardLayout({ images, title, price, comparePrice, brand, badges, opt
10863
11254
  React.createElement(ImageBlock, { images: images, title: title, imageVariant: imageVariant, aspectRatio: cfg.imageAspectRatio, enableZoom: enableImageZoom, zoomMode: imageZoomMode, zoomLevel: imageZoomLevel }),
10864
11255
  cfg.showBadges !== false && badges.length > 0 && (React.createElement(BadgeList, { badges: badges, position: "top-left", maxBadges: 2 })),
10865
11256
  actionButtons && actionButtons.length > 0 && isOverlayPosition && (React.createElement(ActionButtons, { buttons: actionButtons, position: actionButtonsPosition === 'overlay-top-right' ? 'top-right' : 'bottom-center', showLabels: showActionLabels, size: "small" }))),
10866
- React.createElement("div", { className: "seekora-product-card__body", style: { display: 'flex', flexDirection: 'column', gap: 4 } },
10867
- 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)),
10868
- React.createElement("span", { className: "seekora-product-card__title", style: { fontSize: '0.875rem', fontWeight: 500, lineHeight: 1.4, overflow: 'hidden', textOverflow: 'ellipsis' } }, title),
11257
+ React.createElement("div", { className: "seekora-product-card__body", style: { display: 'flex', flexDirection: 'column', gap: bodyGap } },
11258
+ 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)),
11259
+ 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),
10869
11260
  React.createElement("div", { className: "seekora-product-card__price" },
10870
- 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' } })),
11261
+ 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 } })),
10871
11262
  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 })),
10872
11263
  actionButtons && actionButtons.length > 0 && !isOverlayPosition && (React.createElement(ActionButtons, { buttons: actionButtons, position: "inline", showLabels: showActionLabels, size: "small", layout: "horizontal" })))));
10873
11264
  }
@@ -10912,16 +11303,27 @@ function CompactLayout({ images, title, price, product, imageVariant, displayCon
10912
11303
  /** horizontal: image left + content right (title, brand, price, swatches) */
10913
11304
  function HorizontalLayout({ images, title, price, comparePrice, brand, badges, options, variants, product, imageVariant, displayConfig, onVariantHover, onVariantClick, selectedVariants, actionButtons, actionButtonsPosition, showActionLabels, enableImageZoom, imageZoomMode, imageZoomLevel, }) {
10914
11305
  const cfg = displayConfig;
10915
- return (React.createElement("div", { style: { display: 'flex', gap: 12, alignItems: 'flex-start' } },
10916
- React.createElement("div", { style: { position: 'relative', minWidth: 80, flexBasis: '25%', maxWidth: 120, flexShrink: 0 } },
11306
+ const isMobile = useIsMobile();
11307
+ const containerGap = useResponsiveGap(12);
11308
+ const bodyGap = isMobile ? 6 : 4;
11309
+ // Responsive image dimensions
11310
+ const imageMinWidth = isMobile ? 60 : 80;
11311
+ const imageMaxWidth = isMobile ? 100 : 120;
11312
+ const imageFlex = isMobile ? '20%' : '25%';
11313
+ // Responsive font sizes
11314
+ const brandFontSize = isMobile ? '0.6875rem' : '0.75rem';
11315
+ const titleFontSize = isMobile ? '0.875rem' : '0.875rem';
11316
+ const priceFontSize = isMobile ? '0.875rem' : '0.875rem';
11317
+ return (React.createElement("div", { style: { display: 'flex', gap: containerGap, alignItems: 'flex-start' } },
11318
+ React.createElement("div", { style: { position: 'relative', minWidth: imageMinWidth, flexBasis: imageFlex, maxWidth: imageMaxWidth, flexShrink: 0 } },
10917
11319
  React.createElement(ImageBlock, { images: images, title: title, imageVariant: imageVariant, aspectRatio: "1:1", enableZoom: enableImageZoom, zoomMode: imageZoomMode, zoomLevel: imageZoomLevel }),
10918
11320
  cfg.showBadges !== false && badges.length > 0 && (React.createElement(BadgeList, { badges: badges, position: "top-left", maxBadges: 1 })),
10919
11321
  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" }))),
10920
- React.createElement("div", { className: "seekora-product-card__body", style: { display: 'flex', flexDirection: 'column', gap: 4, flex: 1, minWidth: 0 } },
10921
- 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)),
10922
- React.createElement("span", { className: "seekora-product-card__title", style: { fontSize: '0.875rem', fontWeight: 500, lineHeight: 1.4, overflow: 'hidden', textOverflow: 'ellipsis' } }, title),
11322
+ React.createElement("div", { className: "seekora-product-card__body", style: { display: 'flex', flexDirection: 'column', gap: bodyGap, flex: 1, minWidth: 0 } },
11323
+ cfg.showBrand !== false && brand && (React.createElement("span", { className: "seekora-product-card__brand", style: { fontSize: brandFontSize, color: 'var(--seekora-text-secondary, #6b7280)', textTransform: 'uppercase' } }, brand)),
11324
+ 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),
10923
11325
  React.createElement("div", { className: "seekora-product-card__price" },
10924
- 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' } })),
11326
+ 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 } })),
10925
11327
  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 })),
10926
11328
  actionButtons && actionButtons.length > 0 && actionButtonsPosition === 'inline' && (React.createElement(ActionButtons, { buttons: actionButtons, position: "inline", showLabels: showActionLabels, size: "small", layout: "horizontal" })))));
10927
11329
  }
@@ -10947,7 +11349,7 @@ const cardStyle = {
10947
11349
  borderRadius: `var(--seekora-border-radius, ${BORDER_RADIUS.md}px)`,
10948
11350
  backgroundColor: 'transparent',
10949
11351
  textAlign: 'left',
10950
- transition: `background-color ${TRANSITIONS.fast}`,
11352
+ transition: `background-color ${TRANSITIONS.fast}, box-shadow ${TRANSITIONS.fast}`,
10951
11353
  };
10952
11354
  const imgStyle = {
10953
11355
  width: '100%',
@@ -10999,9 +11401,17 @@ function ProductCard({ product, position, section, tabId, onSelect, className, s
10999
11401
  const images = effectiveImages;
11000
11402
  const title = effectiveTitle;
11001
11403
  const price = effectivePrice;
11404
+ // Hover state for card
11405
+ const [isHovered, setIsHovered] = React.useState(false);
11406
+ const cardHoverStyle = isHovered
11407
+ ? {
11408
+ backgroundColor: 'var(--seekora-card-hover-bg, var(--seekora-bg-hover, #f9fafb))',
11409
+ boxShadow: 'var(--seekora-card-hover-shadow, 0 2px 8px rgba(0, 0, 0, 0.08))',
11410
+ }
11411
+ : {};
11002
11412
  // If no displayConfig, render original minimal layout (backwards compat)
11003
11413
  if (!displayConfig) {
11004
- return (React.createElement("div", { role: "button", tabIndex: 0, className: clsx('seekora-suggestions-product-card', className), style: { ...cardStyle, ...style }, onClick: () => onSelect(), onKeyDown: (e) => {
11414
+ 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) => {
11005
11415
  if (e.key === 'Enter' || e.key === ' ') {
11006
11416
  e.preventDefault();
11007
11417
  onSelect();
@@ -11062,20 +11472,21 @@ function ProductCard({ product, position, section, tabId, onSelect, className, s
11062
11472
  horizontal: HorizontalLayout,
11063
11473
  };
11064
11474
  const LayoutComponent = layoutMap[layoutStyle] ?? MinimalLayout;
11065
- const rootClassName = clsx('seekora-product-card', `seekora-product-card--${layoutStyle}`, 'seekora-suggestions-product-card', className);
11475
+ const rootClassName = clsx('seekora-product-card', `seekora-product-card--${layoutStyle}`, 'seekora-suggestions-product-card', isHovered && 'seekora-product-card--hover', className);
11066
11476
  const rootStyle = {
11067
11477
  ...cardStyle,
11068
11478
  ...(layoutStyle === 'horizontal' ? { flexDirection: 'row' } : {}),
11479
+ ...cardHoverStyle,
11069
11480
  ...style,
11070
11481
  };
11071
11482
  if (asLink && product.url) {
11072
- return (React.createElement("a", { href: product.url, className: rootClassName, style: { ...rootStyle, textDecoration: 'none', color: 'inherit' }, onClick: (e) => {
11483
+ return (React.createElement("a", { href: product.url, className: rootClassName, style: { ...rootStyle, textDecoration: 'none', color: 'inherit' }, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), onClick: (e) => {
11073
11484
  e.preventDefault();
11074
11485
  onSelect();
11075
11486
  }, "data-position": position, "data-section": section, "data-tab-id": tabId },
11076
11487
  React.createElement(LayoutComponent, { ...layoutProps })));
11077
11488
  }
11078
- return (React.createElement("div", { role: "button", tabIndex: 0, className: rootClassName, style: rootStyle, onClick: () => onSelect(), onKeyDown: (e) => {
11489
+ return (React.createElement("div", { role: "button", tabIndex: 0, className: rootClassName, style: rootStyle, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), onClick: () => onSelect(), onKeyDown: (e) => {
11079
11490
  if (e.key === 'Enter' || e.key === ' ') {
11080
11491
  e.preventDefault();
11081
11492
  onSelect();
@@ -11091,6 +11502,10 @@ function ProductCard({ product, position, section, tabId, onSelect, className, s
11091
11502
  */
11092
11503
  function ProductGrid({ maxItems = 8, source = 'trending', columns = 4, className, style, gridClassName, displayConfig, onVariantHover, }) {
11093
11504
  const { trendingProducts, filteredTabs, activeTabId, selectProduct, getAllNavigableItems, } = useSuggestionsContext();
11505
+ // Responsive columns and spacing
11506
+ const responsiveColumns = useResponsiveColumns(columns);
11507
+ const responsiveGap = useResponsiveGap(12);
11508
+ const responsivePadding = useResponsivePadding(12);
11094
11509
  const products = React.useMemo(() => {
11095
11510
  if (source === 'trending')
11096
11511
  return trendingProducts;
@@ -11104,9 +11519,9 @@ function ProductGrid({ maxItems = 8, source = 'trending', columns = 4, className
11104
11519
  return null;
11105
11520
  const gridStyle = {
11106
11521
  display: 'grid',
11107
- gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
11108
- gap: 12,
11109
- padding: 12,
11522
+ gridTemplateColumns: `repeat(${responsiveColumns}, minmax(0, 1fr))`,
11523
+ gap: responsiveGap,
11524
+ padding: responsivePadding,
11110
11525
  };
11111
11526
  return (React.createElement("div", { className: clsx('seekora-suggestions-product-grid', className), style: style },
11112
11527
  React.createElement("div", { className: clsx('seekora-suggestions-product-grid-inner', gridClassName), style: gridStyle }, items.map((product, i) => {
@@ -11167,6 +11582,17 @@ const itemStyle$1 = {
11167
11582
  color: 'var(--seekora-text-primary, #111827)',
11168
11583
  transition: 'background-color 120ms ease',
11169
11584
  };
11585
+ function RecentSearchItem({ search, onSelect }) {
11586
+ const [isHovered, setIsHovered] = React.useState(false);
11587
+ return (React.createElement("li", null,
11588
+ React.createElement("button", { type: "button", className: clsx('seekora-suggestions-recent-item', isHovered && 'seekora-suggestions-recent-item--hover'), style: {
11589
+ ...itemStyle$1,
11590
+ ...(isHovered ? { backgroundColor: 'var(--seekora-bg-hover, #f3f4f6)' } : {}),
11591
+ }, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), onMouseDown: (e) => {
11592
+ e.preventDefault();
11593
+ onSelect();
11594
+ } }, search.query)));
11595
+ }
11170
11596
  function RecentSearchesList({ title = 'Recent', maxItems = 8, className, style, listClassName, renderItem, }) {
11171
11597
  const { recentSearches, query, selectRecentSearch } = useSuggestionsContext();
11172
11598
  const items = recentSearches.slice(0, maxItems);
@@ -11179,11 +11605,7 @@ function RecentSearchesList({ title = 'Recent', maxItems = 8, className, style,
11179
11605
  if (renderItem) {
11180
11606
  return React.createElement("li", { key: `${search.query}-${search.timestamp}` }, renderItem(search, i, onSelect));
11181
11607
  }
11182
- return (React.createElement("li", { key: `${search.query}-${search.timestamp}` },
11183
- React.createElement("button", { type: "button", className: "seekora-suggestions-recent-item", style: itemStyle$1, onMouseDown: (e) => {
11184
- e.preventDefault();
11185
- onSelect();
11186
- } }, search.query)));
11608
+ return (React.createElement(RecentSearchItem, { key: `${search.query}-${search.timestamp}`, search: search, onSelect: onSelect }));
11187
11609
  }))));
11188
11610
  }
11189
11611
 
@@ -11204,6 +11626,19 @@ const itemStyle = {
11204
11626
  color: 'var(--seekora-text-primary, #111827)',
11205
11627
  transition: 'background-color 120ms ease',
11206
11628
  };
11629
+ function TrendingItem({ trending, onSelect }) {
11630
+ const [isHovered, setIsHovered] = React.useState(false);
11631
+ return (React.createElement("li", null,
11632
+ React.createElement("button", { type: "button", className: clsx('seekora-suggestions-trending-item', isHovered && 'seekora-suggestions-trending-item--hover'), style: {
11633
+ ...itemStyle,
11634
+ ...(isHovered ? { backgroundColor: 'var(--seekora-bg-hover, #f3f4f6)' } : {}),
11635
+ }, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), onMouseDown: (e) => {
11636
+ e.preventDefault();
11637
+ onSelect();
11638
+ } },
11639
+ trending.query,
11640
+ trending.count != null ? (React.createElement("span", { style: { marginLeft: 8, color: 'var(--seekora-text-secondary, #6b7280)', fontSize: '0.875em' } }, trending.count)) : null)));
11641
+ }
11207
11642
  function TrendingList({ title = 'Trending', maxItems = 8, className, style, listClassName, renderItem, }) {
11208
11643
  const { trendingSearches, selectTrendingSearch } = useSuggestionsContext();
11209
11644
  const items = trendingSearches.slice(0, maxItems);
@@ -11216,31 +11651,10 @@ function TrendingList({ title = 'Trending', maxItems = 8, className, style, list
11216
11651
  if (renderItem) {
11217
11652
  return React.createElement("li", { key: `${trending.query}-${i}` }, renderItem(trending, i, onSelect));
11218
11653
  }
11219
- return (React.createElement("li", { key: `${trending.query}-${i}` },
11220
- React.createElement("button", { type: "button", className: "seekora-suggestions-trending-item", style: itemStyle, onMouseDown: (e) => {
11221
- e.preventDefault();
11222
- onSelect();
11223
- } },
11224
- trending.query,
11225
- trending.count != null ? (React.createElement("span", { style: { marginLeft: 8, color: 'var(--seekora-text-secondary, #6b7280)', fontSize: '0.875em' } }, trending.count)) : null)));
11654
+ return (React.createElement(TrendingItem, { key: `${trending.query}-${i}`, trending: trending, onSelect: onSelect }));
11226
11655
  }))));
11227
11656
  }
11228
11657
 
11229
- /**
11230
- * SuggestionsLoading – loading indicator (primitive)
11231
- */
11232
- function SuggestionsLoading({ className, style, text = 'Loading...' }) {
11233
- const { loading } = useSuggestionsContext();
11234
- if (!loading)
11235
- return null;
11236
- return (React.createElement("div", { className: clsx('seekora-suggestions-loading', className), style: {
11237
- padding: 16,
11238
- color: 'var(--seekora-text-secondary, #6b7280)',
11239
- fontSize: '0.875rem',
11240
- ...style,
11241
- } }, text));
11242
- }
11243
-
11244
11658
  /**
11245
11659
  * SuggestionsError – error message (primitive)
11246
11660
  */
@@ -11718,16 +12132,6 @@ function SectionSearchProvider({ children, query, refinements = [], maxItems = 1
11718
12132
  return React.createElement(SectionSearchContext.Provider, { value: value }, children);
11719
12133
  }
11720
12134
 
11721
- /**
11722
- * SectionLoading – loading state for section (primitive)
11723
- */
11724
- function SectionLoading({ className, style, text = 'Loading...' }) {
11725
- const { loading } = useSectionSearchContext();
11726
- if (!loading)
11727
- return null;
11728
- return (React.createElement("div", { className: className, style: { padding: 16, color: 'var(--seekora-text-secondary)', fontSize: '0.875rem', ...style } }, text));
11729
- }
11730
-
11731
12135
  /**
11732
12136
  * SectionError – error state for section (primitive)
11733
12137
  */
@@ -11743,10 +12147,8 @@ function SectionError({ className, style, render }) {
11743
12147
  /**
11744
12148
  * SectionItemGrid – generic grid of items from SectionSearchProvider (primitive)
11745
12149
  */
11746
- 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, }) {
12150
+ 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, }) {
11747
12151
  const { items, loading, error, trackClick } = useSectionSearchContext();
11748
- if (loading && items.length === 0 && showLoadingState)
11749
- return React.createElement(SectionLoading, { className: className, style: style });
11750
12152
  if (error)
11751
12153
  return React.createElement(SectionError, { className: className, style: style });
11752
12154
  // When loading with previous items, show them (no loading screen)
@@ -12407,22 +12809,6 @@ const createStyles$7 = (theme = {}, isMobile = false) => ({
12407
12809
  fontWeight: 700,
12408
12810
  color: 'var(--seekora-text-primary, #0f1111)',
12409
12811
  },
12410
- loading: {
12411
- display: 'flex',
12412
- alignItems: 'center',
12413
- justifyContent: 'center',
12414
- padding: '40px',
12415
- color: 'var(--seekora-text-secondary, #565959)',
12416
- gap: '10px',
12417
- },
12418
- spinner: {
12419
- width: '20px',
12420
- height: '20px',
12421
- border: '2px solid var(--seekora-border-color, #ddd)',
12422
- borderTopColor: 'var(--seekora-primary, #f90)',
12423
- borderRadius: '50%',
12424
- animation: 'seekora-spin 0.8s linear infinite',
12425
- },
12426
12812
  empty: {
12427
12813
  padding: '40px 20px',
12428
12814
  textAlign: 'center',
@@ -12486,7 +12872,7 @@ const RatingStars = ({ rating, count }) => {
12486
12872
  ")"))));
12487
12873
  };
12488
12874
  const AmazonDropdown = React.forwardRef(function AmazonDropdown(props, ref) {
12489
- 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;
12875
+ 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;
12490
12876
  // Inject global responsive styles
12491
12877
  useInjectResponsiveStyles();
12492
12878
  // Responsive state
@@ -12583,9 +12969,7 @@ const AmazonDropdown = React.forwardRef(function AmazonDropdown(props, ref) {
12583
12969
  }
12584
12970
  `),
12585
12971
  header,
12586
- (loading && showLoadingState) ? (renderLoading ? renderLoading() : (React.createElement("div", { style: styles.loading },
12587
- React.createElement("div", { style: styles.spinner }),
12588
- React.createElement("span", null, "Searching...")))) : (React.createElement(React.Fragment, null,
12972
+ React.createElement(React.Fragment, null,
12589
12973
  filteredTabs.length > 0 && (React.createElement("div", { style: {
12590
12974
  display: 'flex',
12591
12975
  gap: '8px',
@@ -12690,7 +13074,7 @@ const AmazonDropdown = React.forwardRef(function AmazonDropdown(props, ref) {
12690
13074
  currency: '',
12691
13075
  priceDecimals: productDisplay.priceDecimals
12692
13076
  }))),
12693
- product.rating && (React.createElement(RatingStars, { rating: parseFloat(String(product.rating)), count: product.reviewCount ? parseInt(String(product.reviewCount)) : undefined }))))))))))),
13077
+ product.rating && (React.createElement(RatingStars, { rating: parseFloat(String(product.rating)), count: product.reviewCount ? parseInt(String(product.reviewCount)) : undefined })))))))))),
12694
13078
  footer !== undefined ? footer : (React.createElement("div", { style: styles.footer },
12695
13079
  React.createElement("div", { style: styles.keyboard },
12696
13080
  React.createElement("span", { style: styles.key }, "\u2191"),
@@ -12880,22 +13264,6 @@ const createStyles$6 = (isMobile = false) => ({
12880
13264
  backgroundColor: 'var(--seekora-bg-hover, #f1f3f4)',
12881
13265
  color: 'var(--seekora-text-primary, #202124)',
12882
13266
  },
12883
- loading: {
12884
- display: 'flex',
12885
- alignItems: 'center',
12886
- justifyContent: 'center',
12887
- padding: '24px',
12888
- color: 'var(--seekora-text-secondary, #5f6368)',
12889
- gap: '12px',
12890
- },
12891
- spinner: {
12892
- width: '24px',
12893
- height: '24px',
12894
- border: '3px solid var(--seekora-border-color, #e8eaed)',
12895
- borderTopColor: 'var(--seekora-primary, #4285f4)',
12896
- borderRadius: '50%',
12897
- animation: 'seekora-spin 0.8s linear infinite',
12898
- },
12899
13267
  empty: {
12900
13268
  padding: '24px',
12901
13269
  textAlign: 'center',
@@ -12915,7 +13283,7 @@ const TrendingIcon$2 = () => (React.createElement("svg", { width: "20", height:
12915
13283
  const ArrowIcon$1 = () => (React.createElement("svg", { viewBox: "0 0 24 24", fill: "none", width: "20", height: "20" },
12916
13284
  React.createElement("path", { d: "M9 5l-4 4 4 4", stroke: "currentColor", strokeWidth: "2", fill: "none", transform: "rotate(-90 12 12)" })));
12917
13285
  const GoogleDropdown = React.forwardRef(function GoogleDropdown(props, ref) {
12918
- 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;
13286
+ 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;
12919
13287
  // Inject global responsive styles
12920
13288
  useInjectResponsiveStyles();
12921
13289
  // Responsive state
@@ -13030,8 +13398,7 @@ const GoogleDropdown = React.forwardRef(function GoogleDropdown(props, ref) {
13030
13398
  }
13031
13399
  `),
13032
13400
  header,
13033
- (loading && showLoadingState) ? (renderLoading ? renderLoading() : (React.createElement("div", { style: styles.loading },
13034
- React.createElement("div", { style: styles.spinner })))) : (React.createElement(React.Fragment, null,
13401
+ React.createElement(React.Fragment, null,
13035
13402
  React.createElement("ul", { style: styles.list },
13036
13403
  showRecent && (React.createElement(React.Fragment, null,
13037
13404
  recentQueries.map((q, idx) => {
@@ -13061,7 +13428,7 @@ const GoogleDropdown = React.forwardRef(function GoogleDropdown(props, ref) {
13061
13428
  "\"")))),
13062
13429
  (showFeelingLucky || showVoiceSearch) && (React.createElement("div", { style: styles.footer }, showFeelingLucky && (React.createElement(React.Fragment, null,
13063
13430
  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"),
13064
- 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))))))),
13431
+ 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)))))),
13065
13432
  footer));
13066
13433
  });
13067
13434
 
@@ -13365,7 +13732,7 @@ const ImageIcon = () => (React.createElement("svg", { viewBox: "0 0 24 24", fill
13365
13732
  React.createElement("polyline", { points: "21 15 16 10 5 21" })));
13366
13733
  const PinterestDropdown = React.forwardRef(function PinterestDropdown(props, ref) {
13367
13734
  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
13368
- 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;
13735
+ showPriceOverlay = true, gridColumns = 4, width = '800px', maxHeight = '600px', zIndex = 1000, className, style, classNames = {}, onSuggestionSelect, onProductClick, onCategoryClick, onTabChange, onSaveProduct, onViewAll, onClose, header, footer, renderEmpty, } = props;
13369
13736
  // Inject global responsive styles
13370
13737
  useInjectResponsiveStyles();
13371
13738
  // Responsive state
@@ -13459,47 +13826,47 @@ const PinterestDropdown = React.forwardRef(function PinterestDropdown(props, ref
13459
13826
  "(",
13460
13827
  cat.count,
13461
13828
  ")"))))))))),
13462
- React.createElement("div", { style: { ...styles.content, maxHeight } }, (loading && showLoadingState) ? (renderLoading ? renderLoading() : (React.createElement("div", { style: styles.loading },
13463
- React.createElement("div", { style: styles.spinner })))) : (React.createElement(React.Fragment, null,
13464
- 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: () => {
13465
- setHoveredSuggestion(idx);
13466
- setActiveIndex(idx);
13467
- }, onMouseLeave: () => setHoveredSuggestion(-1) },
13468
- React.createElement("span", { style: styles.suggestionIcon }, query ? highlightTextReact(s.query, query, { tag: 'strong' }) : s.query)))))),
13469
- displayProducts.length > 0 && (React.createElement(React.Fragment, null,
13470
- React.createElement("div", { style: styles.sectionTitle },
13471
- React.createElement(TrendingIcon$1, null),
13472
- "Ideas for you"),
13473
- React.createElement("div", { style: {
13474
- ...styles.productsGrid,
13475
- gridTemplateColumns: `repeat(${gridColumns}, 1fr)`,
13476
- } }, 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) },
13477
- React.createElement("div", { style: styles.productImageContainer },
13478
- product.image ? (React.createElement("img", { src: product.image, alt: product.title, style: styles.productImage, loading: "lazy" })) : (React.createElement("div", { style: styles.productImagePlaceholder },
13479
- React.createElement(ImageIcon, null))),
13480
- showSaveButton && (React.createElement("button", { style: mergeStyles(styles.productSaveBtn, hoveredProduct === product.id ? styles.productSaveBtnVisible : undefined), onClick: (e) => {
13481
- e.stopPropagation();
13482
- onSaveProduct?.(product._raw);
13483
- }, "aria-label": "Save" },
13484
- React.createElement(HeartIcon, null))),
13485
- showPriceOverlay && product.price !== undefined && (React.createElement("div", { style: mergeStyles(styles.productOverlay, hoveredProduct === product.id ? styles.productOverlayVisible : undefined) },
13486
- React.createElement("div", { style: styles.productTitleOverlay }, product.title),
13487
- React.createElement("div", { style: styles.productPriceOverlay }, formatPrice(product.price, {
13829
+ React.createElement("div", { style: { ...styles.content, maxHeight } },
13830
+ React.createElement(React.Fragment, null,
13831
+ 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: () => {
13832
+ setHoveredSuggestion(idx);
13833
+ setActiveIndex(idx);
13834
+ }, onMouseLeave: () => setHoveredSuggestion(-1) },
13835
+ React.createElement("span", { style: styles.suggestionIcon }, query ? highlightTextReact(s.query, query, { tag: 'strong' }) : s.query)))))),
13836
+ displayProducts.length > 0 && (React.createElement(React.Fragment, null,
13837
+ React.createElement("div", { style: styles.sectionTitle },
13838
+ React.createElement(TrendingIcon$1, null),
13839
+ "Ideas for you"),
13840
+ React.createElement("div", { style: {
13841
+ ...styles.productsGrid,
13842
+ gridTemplateColumns: `repeat(${gridColumns}, 1fr)`,
13843
+ } }, 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) },
13844
+ React.createElement("div", { style: styles.productImageContainer },
13845
+ product.image ? (React.createElement("img", { src: product.image, alt: product.title, style: styles.productImage, loading: "lazy" })) : (React.createElement("div", { style: styles.productImagePlaceholder },
13846
+ React.createElement(ImageIcon, null))),
13847
+ showSaveButton && (React.createElement("button", { style: mergeStyles(styles.productSaveBtn, hoveredProduct === product.id ? styles.productSaveBtnVisible : undefined), onClick: (e) => {
13848
+ e.stopPropagation();
13849
+ onSaveProduct?.(product._raw);
13850
+ }, "aria-label": "Save" },
13851
+ React.createElement(HeartIcon, null))),
13852
+ showPriceOverlay && product.price !== undefined && (React.createElement("div", { style: mergeStyles(styles.productOverlay, hoveredProduct === product.id ? styles.productOverlayVisible : undefined) },
13853
+ React.createElement("div", { style: styles.productTitleOverlay }, product.title),
13854
+ React.createElement("div", { style: styles.productPriceOverlay }, formatPrice(product.price, {
13855
+ currency: productDisplay.currency || product.currency || '$'
13856
+ }))))),
13857
+ !showPriceOverlay && (React.createElement("div", { style: styles.productInfo },
13858
+ React.createElement("div", { style: styles.productTitle }, product.title),
13859
+ product.price !== undefined && (React.createElement("div", { style: styles.productPrice }, formatPrice(product.price, {
13488
13860
  currency: productDisplay.currency || product.currency || '$'
13489
- }))))),
13490
- !showPriceOverlay && (React.createElement("div", { style: styles.productInfo },
13491
- React.createElement("div", { style: styles.productTitle }, product.title),
13492
- product.price !== undefined && (React.createElement("div", { style: styles.productPrice }, formatPrice(product.price, {
13493
- currency: productDisplay.currency || product.currency || '$'
13494
- }))))))))))),
13495
- !loading && displayProducts.length === 0 && processedSuggestions.length === 0 && (renderEmpty ? renderEmpty(query) : (React.createElement("div", { style: styles.empty },
13496
- React.createElement("div", { style: styles.emptyIcon },
13497
- React.createElement(SearchIcon$3, null)),
13498
- React.createElement("p", null,
13499
- "No ideas found for \"",
13500
- query,
13501
- "\""),
13502
- React.createElement("p", { style: { fontSize: '14px', marginTop: '8px' } }, "Try searching for something else"))))))),
13861
+ }))))))))))),
13862
+ !loading && displayProducts.length === 0 && processedSuggestions.length === 0 && (renderEmpty ? renderEmpty(query) : (React.createElement("div", { style: styles.empty },
13863
+ React.createElement("div", { style: styles.emptyIcon },
13864
+ React.createElement(SearchIcon$3, null)),
13865
+ React.createElement("p", null,
13866
+ "No ideas found for \"",
13867
+ query,
13868
+ "\""),
13869
+ React.createElement("p", { style: { fontSize: '14px', marginTop: '8px' } }, "Try searching for something else")))))),
13503
13870
  footer !== undefined ? footer : (displayProducts.length > 0 && (React.createElement("div", { style: styles.footer },
13504
13871
  React.createElement("span", { style: { color: 'var(--seekora-text-secondary, #767676)', fontSize: '14px' } },
13505
13872
  displayProducts.length,
@@ -13799,7 +14166,7 @@ const ShoppingIcon = () => (React.createElement("svg", { viewBox: "0 0 20 20", f
13799
14166
  const ClockIcon$1 = () => (React.createElement("svg", { viewBox: "0 0 20 20", fill: "currentColor", width: "16", height: "16" },
13800
14167
  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" })));
13801
14168
  const SpotlightDropdown = React.forwardRef(function SpotlightDropdown(props, ref) {
13802
- 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;
14169
+ 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;
13803
14170
  // Inject global responsive styles
13804
14171
  useInjectResponsiveStyles();
13805
14172
  // Responsive state
@@ -13953,8 +14320,7 @@ const SpotlightDropdown = React.forwardRef(function SpotlightDropdown(props, ref
13953
14320
  React.createElement("span", { style: styles.kbd }, "K")))),
13954
14321
  header,
13955
14322
  React.createElement("div", { style: styles.content },
13956
- React.createElement("div", { ref: listRef, style: { ...styles.resultsColumn, maxHeight } }, (loading && allItems.length === 0 && showLoadingState) ? (renderLoading ? renderLoading() : (React.createElement("div", { style: styles.loading },
13957
- React.createElement("div", { style: styles.spinner })))) : allItems.length === 0 ? (renderEmpty ? renderEmpty(query) : (React.createElement("div", { style: styles.empty },
14323
+ React.createElement("div", { ref: listRef, style: { ...styles.resultsColumn, maxHeight } }, allItems.length === 0 ? (renderEmpty ? renderEmpty(query) : (React.createElement("div", { style: styles.empty },
13958
14324
  "No results for \"",
13959
14325
  query,
13960
14326
  "\""))) : (React.createElement(React.Fragment, null,
@@ -14350,7 +14716,7 @@ const SearchIcon$1 = () => (React.createElement("svg", { width: "18", height: "1
14350
14716
  const ArrowIcon = () => (React.createElement("svg", { width: "16", height: "16", viewBox: "0 0 20 20", fill: "currentColor" },
14351
14717
  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" })));
14352
14718
  const ShopifyDropdown = React.forwardRef(function ShopifyDropdown(props, ref) {
14353
- 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;
14719
+ 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;
14354
14720
  // Inject global responsive styles
14355
14721
  useInjectResponsiveStyles();
14356
14722
  // Responsive state
@@ -14422,8 +14788,7 @@ const ShopifyDropdown = React.forwardRef(function ShopifyDropdown(props, ref) {
14422
14788
  }
14423
14789
  `),
14424
14790
  header,
14425
- (loading && showLoadingState) ? (renderLoading ? renderLoading() : (React.createElement("div", { style: styles.loading },
14426
- React.createElement("div", { style: styles.spinner })))) : (React.createElement("div", { style: styles.layout },
14791
+ React.createElement("div", { style: styles.layout },
14427
14792
  React.createElement("div", { style: styles.leftPanel },
14428
14793
  React.createElement("div", { style: styles.section },
14429
14794
  React.createElement("div", { style: styles.sectionHeader },
@@ -14478,7 +14843,7 @@ const ShopifyDropdown = React.forwardRef(function ShopifyDropdown(props, ref) {
14478
14843
  React.createElement("div", { style: styles.productTitle }, product.title),
14479
14844
  product.price !== undefined && (React.createElement("div", { style: styles.productPrice }, formatPrice(product.price, {
14480
14845
  currency: productDisplay.currency || product.currency || '$'
14481
- })))))))))))),
14846
+ }))))))))))),
14482
14847
  footer !== undefined ? footer : (React.createElement("div", { style: styles.footer },
14483
14848
  React.createElement("span", null,
14484
14849
  "Press ",
@@ -14755,7 +15120,7 @@ const ChevronIcon = () => (React.createElement("svg", { viewBox: "0 0 24 24", fi
14755
15120
  const ArrowUpIcon = () => (React.createElement("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", width: "20", height: "20" },
14756
15121
  React.createElement("path", { d: "M7 17l5-5-5-5M13 17l5-5-5-5" })));
14757
15122
  const MobileSheetDropdown = React.forwardRef(function MobileSheetDropdown(props, ref) {
14758
- 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;
15123
+ 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;
14759
15124
  // Inject global responsive styles
14760
15125
  useInjectResponsiveStyles();
14761
15126
  const styles = React.useMemo(() => createStyles$2(), []);
@@ -14874,7 +15239,7 @@ const MobileSheetDropdown = React.forwardRef(function MobileSheetDropdown(props,
14874
15239
  React.createElement("div", { style: styles.searchContainer },
14875
15240
  React.createElement("div", { style: styles.searchIcon },
14876
15241
  React.createElement(SearchIcon, null)),
14877
- React.createElement("input", { ref: inputRef, type: "text", placeholder: "Search...", value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyDown: (e) => {
15242
+ React.createElement("input", { ref: inputRef, type: "text", placeholder: "Powered by Seekora", value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyDown: (e) => {
14878
15243
  if (e.key === 'Enter') {
14879
15244
  e.preventDefault();
14880
15245
  if (activeIndex >= 0) {
@@ -14897,70 +15262,70 @@ const MobileSheetDropdown = React.forwardRef(function MobileSheetDropdown(props,
14897
15262
  }
14898
15263
  }, style: styles.searchInput, autoFocus: true })),
14899
15264
  showCancel && (React.createElement("button", { style: styles.cancelBtn, onClick: onClose }, cancelText)))))),
14900
- React.createElement("div", { style: styles.content }, (loading && showLoadingState) ? (renderLoading ? renderLoading() : (React.createElement("div", { style: styles.loading },
14901
- React.createElement("div", { style: styles.spinner })))) : (React.createElement(React.Fragment, null,
14902
- showRecent && (React.createElement("div", { style: styles.section },
14903
- React.createElement("div", { style: styles.sectionHeader },
14904
- React.createElement("span", { style: styles.sectionTitle }, "Recent"),
14905
- React.createElement("button", { style: styles.clearBtn, onClick: onRecentClearAll }, "Clear")),
14906
- recentQueries.map((q, idx) => (React.createElement("div", { key: `recent-${q}`, "data-index": idx, style: mergeStyles(styles.item, activeIndex === idx ? styles.itemActive : undefined), onClick: () => {
14907
- onRecentClick?.(q);
14908
- setInputValue(q);
14909
- }, onMouseEnter: () => setActiveIndex(idx) },
14910
- React.createElement("div", { style: styles.itemIcon },
14911
- React.createElement(ClockIcon, null)),
14912
- React.createElement("div", { style: styles.itemContent },
14913
- React.createElement("div", { style: styles.itemTitle }, q)),
14914
- React.createElement("div", { style: styles.itemAction },
14915
- React.createElement(ArrowUpIcon, null))))))),
14916
- showTrending && (React.createElement("div", { style: styles.section },
14917
- React.createElement("div", { style: styles.sectionHeader },
14918
- React.createElement("span", { style: styles.sectionTitle }, "Trending")),
14919
- trendingQueries.map((q, idx) => {
14920
- const globalIdx = recentQueries.length + idx;
14921
- return (React.createElement("div", { key: `trending-${q}`, "data-index": globalIdx, style: mergeStyles(styles.item, activeIndex === globalIdx ? styles.itemActive : undefined), onClick: () => {
15265
+ React.createElement("div", { style: styles.content },
15266
+ React.createElement(React.Fragment, null,
15267
+ showRecent && (React.createElement("div", { style: styles.section },
15268
+ React.createElement("div", { style: styles.sectionHeader },
15269
+ React.createElement("span", { style: styles.sectionTitle }, "Recent"),
15270
+ React.createElement("button", { style: styles.clearBtn, onClick: onRecentClearAll }, "Clear")),
15271
+ recentQueries.map((q, idx) => (React.createElement("div", { key: `recent-${q}`, "data-index": idx, style: mergeStyles(styles.item, activeIndex === idx ? styles.itemActive : undefined), onClick: () => {
14922
15272
  onRecentClick?.(q);
14923
15273
  setInputValue(q);
14924
- }, onMouseEnter: () => setActiveIndex(globalIdx) },
15274
+ }, onMouseEnter: () => setActiveIndex(idx) },
14925
15275
  React.createElement("div", { style: styles.itemIcon },
14926
- React.createElement(TrendingIcon, null)),
15276
+ React.createElement(ClockIcon, null)),
14927
15277
  React.createElement("div", { style: styles.itemContent },
14928
15278
  React.createElement("div", { style: styles.itemTitle }, q)),
14929
15279
  React.createElement("div", { style: styles.itemAction },
14930
- React.createElement(ChevronIcon, null))));
14931
- }))),
14932
- inputValue && processedSuggestions.length > 0 && (React.createElement("div", { style: styles.section },
14933
- React.createElement("div", { style: styles.sectionHeader },
14934
- React.createElement("span", { style: styles.sectionTitle }, "Suggestions")),
14935
- processedSuggestions.map((s, idx) => {
14936
- const globalIdx = recentQueries.length + trendingQueries.length + idx;
14937
- 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) },
14938
- React.createElement("div", { style: styles.itemIcon },
14939
- React.createElement(SearchIcon, null)),
14940
- React.createElement("div", { style: styles.itemContent },
14941
- React.createElement("div", { style: styles.itemTitle }, highlightTextReact(s.query, inputValue, { tag: 'strong' })),
14942
- s.count && (React.createElement("div", { style: styles.itemSubtitle },
14943
- s.count,
14944
- " results"))),
14945
- React.createElement("div", { style: styles.itemAction },
14946
- React.createElement(ChevronIcon, null))));
14947
- }))),
14948
- showProducts && (React.createElement("div", { style: styles.section },
14949
- React.createElement("div", { style: styles.sectionHeader },
14950
- React.createElement("span", { style: styles.sectionTitle }, "Products")),
14951
- 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) },
14952
- p.image ? (React.createElement("img", { src: p.image, alt: p.title, style: styles.productImage, loading: "lazy" })) : (React.createElement("div", { style: styles.productImage })),
14953
- React.createElement("div", { style: styles.productTitle }, p.title),
14954
- p.price !== undefined && (React.createElement("div", { style: styles.productPrice }, formatPrice(p.price, {
14955
- currency: productDisplay.currency || p.currency || '$'
14956
- }))))))))),
14957
- inputValue && processedSuggestions.length === 0 && !loading && (renderEmpty ? renderEmpty(inputValue) : (React.createElement("div", { style: styles.empty },
14958
- React.createElement("div", { style: styles.emptyIcon },
14959
- React.createElement(SearchIcon, null)),
14960
- React.createElement("p", null,
14961
- "No results for \"",
14962
- inputValue,
14963
- "\""))))))),
15280
+ React.createElement(ArrowUpIcon, null))))))),
15281
+ showTrending && (React.createElement("div", { style: styles.section },
15282
+ React.createElement("div", { style: styles.sectionHeader },
15283
+ React.createElement("span", { style: styles.sectionTitle }, "Trending")),
15284
+ trendingQueries.map((q, idx) => {
15285
+ const globalIdx = recentQueries.length + idx;
15286
+ return (React.createElement("div", { key: `trending-${q}`, "data-index": globalIdx, style: mergeStyles(styles.item, activeIndex === globalIdx ? styles.itemActive : undefined), onClick: () => {
15287
+ onRecentClick?.(q);
15288
+ setInputValue(q);
15289
+ }, onMouseEnter: () => setActiveIndex(globalIdx) },
15290
+ React.createElement("div", { style: styles.itemIcon },
15291
+ React.createElement(TrendingIcon, null)),
15292
+ React.createElement("div", { style: styles.itemContent },
15293
+ React.createElement("div", { style: styles.itemTitle }, q)),
15294
+ React.createElement("div", { style: styles.itemAction },
15295
+ React.createElement(ChevronIcon, null))));
15296
+ }))),
15297
+ inputValue && processedSuggestions.length > 0 && (React.createElement("div", { style: styles.section },
15298
+ React.createElement("div", { style: styles.sectionHeader },
15299
+ React.createElement("span", { style: styles.sectionTitle }, "Suggestions")),
15300
+ processedSuggestions.map((s, idx) => {
15301
+ const globalIdx = recentQueries.length + trendingQueries.length + idx;
15302
+ 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) },
15303
+ React.createElement("div", { style: styles.itemIcon },
15304
+ React.createElement(SearchIcon, null)),
15305
+ React.createElement("div", { style: styles.itemContent },
15306
+ React.createElement("div", { style: styles.itemTitle }, highlightTextReact(s.query, inputValue, { tag: 'strong' })),
15307
+ s.count && (React.createElement("div", { style: styles.itemSubtitle },
15308
+ s.count,
15309
+ " results"))),
15310
+ React.createElement("div", { style: styles.itemAction },
15311
+ React.createElement(ChevronIcon, null))));
15312
+ }))),
15313
+ showProducts && (React.createElement("div", { style: styles.section },
15314
+ React.createElement("div", { style: styles.sectionHeader },
15315
+ React.createElement("span", { style: styles.sectionTitle }, "Products")),
15316
+ 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) },
15317
+ p.image ? (React.createElement("img", { src: p.image, alt: p.title, style: styles.productImage, loading: "lazy" })) : (React.createElement("div", { style: styles.productImage })),
15318
+ React.createElement("div", { style: styles.productTitle }, p.title),
15319
+ p.price !== undefined && (React.createElement("div", { style: styles.productPrice }, formatPrice(p.price, {
15320
+ currency: productDisplay.currency || p.currency || '$'
15321
+ }))))))))),
15322
+ inputValue && processedSuggestions.length === 0 && !loading && (renderEmpty ? renderEmpty(inputValue) : (React.createElement("div", { style: styles.empty },
15323
+ React.createElement("div", { style: styles.emptyIcon },
15324
+ React.createElement(SearchIcon, null)),
15325
+ React.createElement("p", null,
15326
+ "No results for \"",
15327
+ inputValue,
15328
+ "\"")))))),
14964
15329
  footer !== undefined ? footer : (showFooterButton && inputValue && (React.createElement("div", { style: styles.footer },
14965
15330
  React.createElement("button", { style: styles.footerBtn, onClick: () => onSearchSubmit?.(inputValue) },
14966
15331
  footerButtonText,
@@ -15131,7 +15496,7 @@ const createStyles$1 = () => ({
15131
15496
  },
15132
15497
  });
15133
15498
  const MinimalDropdown = React.forwardRef(function MinimalDropdown(props, ref) {
15134
- 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;
15499
+ 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;
15135
15500
  // Inject global responsive styles
15136
15501
  useInjectResponsiveStyles();
15137
15502
  // Responsive state
@@ -15217,7 +15582,7 @@ const MinimalDropdown = React.forwardRef(function MinimalDropdown(props, ref) {
15217
15582
  }
15218
15583
  `),
15219
15584
  header,
15220
- 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,
15585
+ 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,
15221
15586
  showRecent && (React.createElement(React.Fragment, null,
15222
15587
  showSectionDividers && (React.createElement("div", { style: styles.divider }, "Recent")),
15223
15588
  recentQueries.map((q, idx) => {
@@ -15425,24 +15790,12 @@ const createStyles = (isMobile) => ({
15425
15790
  flexShrink: 0,
15426
15791
  transition: 'color 150ms',
15427
15792
  },
15428
- loadingSpinner: {
15429
- width: '18px',
15430
- height: '18px',
15431
- borderWidth: '2px',
15432
- borderStyle: 'solid',
15433
- borderColor: '#e5e7eb',
15434
- borderTopColor: '#f90',
15435
- borderRadius: '50%',
15436
- animation: 'seekora-spin 0.8s linear infinite',
15437
- marginRight: '12px',
15438
- flexShrink: 0,
15439
- },
15440
15793
  });
15441
15794
  // ============================================================================
15442
15795
  // Component
15443
15796
  // ============================================================================
15444
15797
  const SuggestionSearchBar = React.forwardRef(function SuggestionSearchBar(props, ref) {
15445
- 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;
15798
+ 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;
15446
15799
  // Theme: prop overrides context (SearchProvider theme)
15447
15800
  const searchContext = useSearchContext();
15448
15801
  const effectiveTheme = theme ?? searchContext.theme;
@@ -15906,8 +16259,7 @@ const SuggestionSearchBar = React.forwardRef(function SuggestionSearchBar(props,
15906
16259
  React.createElement("svg", { viewBox: "0 0 20 20", fill: "currentColor", width: "20", height: "20" },
15907
16260
  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" }))),
15908
16261
  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" }),
15909
- loading && React.createElement("div", { style: styles.loadingSpinner }),
15910
- currentQuery && !loading && (React.createElement("button", { type: "button", onClick: handleClear, style: styles.clearButton, "aria-label": "Clear search" }, "\u00D7"))),
16262
+ currentQuery && (React.createElement("button", { type: "button", onClick: handleClear, style: styles.clearButton, "aria-label": "Clear search" }, "\u00D7"))),
15911
16263
  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: {
15912
16264
  enabled: enableAnalytics,
15913
16265
  trackSuggestionClicks: analyticsConfig?.trackSuggestionClicks ?? true,
@@ -15992,18 +16344,16 @@ function Modal({ isOpen, onClose, children }) {
15992
16344
  return reactDom.createPortal(modalContent, document.body);
15993
16345
  }
15994
16346
 
15995
- function SearchBox({ value, onChange, onKeyDown, placeholder = 'Search documentation...', isLoading = false, onClear, }) {
16347
+ function SearchBox({ value, onChange, onKeyDown, placeholder = 'Search documentation...', onClear, }) {
15996
16348
  const inputRef = React.useRef(null);
15997
16349
  React.useEffect(() => { if (inputRef.current)
15998
16350
  inputRef.current.focus(); }, []);
15999
16351
  const handleChange = (event) => onChange(event.target.value);
16000
16352
  const handleClear = () => { onChange(''); onClear?.(); inputRef.current?.focus(); };
16001
16353
  return (React.createElement("div", { className: "seekora-docsearch-searchbox" },
16002
- React.createElement("label", { className: "seekora-docsearch-searchbox-icon", htmlFor: "seekora-docsearch-input" }, isLoading ? (React.createElement("span", { className: "seekora-docsearch-spinner", "aria-hidden": "true" },
16003
- React.createElement("svg", { width: "20", height: "20", viewBox: "0 0 20 20" },
16004
- React.createElement("circle", { cx: "10", cy: "10", r: "8", stroke: "currentColor", strokeWidth: "2", fill: "none", strokeDasharray: "40", strokeDashoffset: "10" },
16005
- 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" },
16006
- 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" })))),
16354
+ React.createElement("label", { className: "seekora-docsearch-searchbox-icon", htmlFor: "seekora-docsearch-input" },
16355
+ React.createElement("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true" },
16356
+ 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" }))),
16007
16357
  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" }),
16008
16358
  value && (React.createElement("button", { type: "button", className: "seekora-docsearch-clear", onClick: handleClear, "aria-label": "Clear search" },
16009
16359
  React.createElement("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true" },
@@ -16232,7 +16582,7 @@ function getHitKey(hit, index) {
16232
16582
  return hit.objectID;
16233
16583
  return `suggestion-${hit.url}-${index}`;
16234
16584
  }
16235
- function Results({ hits, groupedHits, selectedIndex, onSelect, onHover, scrollSelectionIntoViewRef, query, isLoading, showLoadingState = false, error, translations = {}, sources: _sources = [], }) {
16585
+ function Results({ hits, groupedHits, selectedIndex, onSelect, onHover, scrollSelectionIntoViewRef, query, isLoading, error, translations = {}, sources: _sources = [], }) {
16236
16586
  const listRef = React.useRef(null);
16237
16587
  React.useEffect(() => {
16238
16588
  if (!listRef.current || hits.length === 0)
@@ -16266,14 +16616,6 @@ function Results({ hits, groupedHits, selectedIndex, onSelect, onHover, scrollSe
16266
16616
  return (React.createElement("div", { className: "seekora-docsearch-empty" },
16267
16617
  React.createElement("p", { className: "seekora-docsearch-empty-text" }, translations.searchPlaceholder || 'Type to start searching...')));
16268
16618
  }
16269
- if (isLoading && hits.length === 0 && showLoadingState) {
16270
- return (React.createElement("div", { className: "seekora-docsearch-loading" },
16271
- React.createElement("div", { className: "seekora-docsearch-loading-spinner" },
16272
- React.createElement("svg", { width: "24", height: "24", viewBox: "0 0 24 24", "aria-hidden": "true" },
16273
- React.createElement("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2", fill: "none", strokeDasharray: "50", strokeDashoffset: "15" },
16274
- React.createElement("animateTransform", { attributeName: "transform", type: "rotate", from: "0 12 12", to: "360 12 12", dur: "0.8s", repeatCount: "indefinite" })))),
16275
- React.createElement("p", { className: "seekora-docsearch-loading-text" }, translations.loadingText || 'Searching...')));
16276
- }
16277
16619
  if (isLoading && hits.length === 0) {
16278
16620
  return React.createElement("div", { className: "seekora-docsearch-empty" });
16279
16621
  }
@@ -16686,6 +17028,12 @@ function useSeekoraSearch$1(options) {
16686
17028
  readSecret: storeSecret,
16687
17029
  logLevel: 'warn',
16688
17030
  enableContextCollection: true,
17031
+ enableEventQueue: true, // Enable batching for better performance
17032
+ eventQueue: {
17033
+ batchSize: 10,
17034
+ flushInterval: 5000, // 5 seconds
17035
+ enablePersistence: true,
17036
+ },
16689
17037
  };
16690
17038
  if (apiEndpoint) {
16691
17039
  if (['local', 'stage', 'production'].includes(apiEndpoint)) {
@@ -16897,7 +17245,7 @@ function DocSearch({ storeId, storeSecret, seekoraApiEndpoint, apiEndpoint, apiK
16897
17245
  React.createElement(Modal, { isOpen: isOpen, onClose: handleClose },
16898
17246
  React.createElement("div", { className: "seekora-docsearch-modal", onKeyDown: handleKeyDown },
16899
17247
  React.createElement("header", { className: "seekora-docsearch-header" },
16900
- React.createElement(SearchBox, { value: query, onChange: setQuery, onKeyDown: handleKeyDown, placeholder: placeholder, isLoading: isLoading, onClear: reset }),
17248
+ React.createElement(SearchBox, { value: query, onChange: setQuery, onKeyDown: handleKeyDown, placeholder: placeholder, onClear: reset }),
16901
17249
  React.createElement("button", { type: "button", className: "seekora-docsearch-close", onClick: handleClose, "aria-label": "Close search" },
16902
17250
  React.createElement("span", { className: "seekora-docsearch-close-text" }, "esc"))),
16903
17251
  React.createElement("div", { className: "seekora-docsearch-body" },
@@ -18236,7 +18584,6 @@ exports.SearchProvider = SearchProvider;
18236
18584
  exports.SearchResults = SearchResults;
18237
18585
  exports.SectionError = SectionError;
18238
18586
  exports.SectionItemGrid = SectionItemGrid;
18239
- exports.SectionLoading = SectionLoading;
18240
18587
  exports.SectionSearchProvider = SectionSearchProvider;
18241
18588
  exports.ShopifyDropdown = ShopifyDropdown;
18242
18589
  exports.Snippet = Snippet;
@@ -18249,7 +18596,6 @@ exports.SuggestionList = SuggestionList;
18249
18596
  exports.SuggestionSearchBar = SuggestionSearchBar;
18250
18597
  exports.SuggestionsDropdownComposition = SuggestionsDropdownComposition;
18251
18598
  exports.SuggestionsError = SuggestionsError;
18252
- exports.SuggestionsLoading = SuggestionsLoading;
18253
18599
  exports.SuggestionsProvider = SuggestionsProvider;
18254
18600
  exports.TrendingItems = TrendingItems;
18255
18601
  exports.TrendingList = TrendingList;
@@ -18299,6 +18645,7 @@ exports.useAnalytics = useAnalytics;
18299
18645
  exports.useAnalyticsProvider = useAnalyticsProvider;
18300
18646
  exports.useDocSearch = useDocSearch;
18301
18647
  exports.useDocSearchSeekoraSearch = useSeekoraSearch$1;
18648
+ exports.useFilters = useFilters;
18302
18649
  exports.useInjectResponsiveStyles = useInjectResponsiveStyles;
18303
18650
  exports.useKeyboard = useKeyboard;
18304
18651
  exports.useNaturalLanguageFilters = useNaturalLanguageFilters;