@seekora-ai/ui-sdk-react 0.0.0-stage-20260517092419
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Breadcrumb.d.ts +43 -0
- package/dist/components/Breadcrumb.d.ts.map +1 -0
- package/dist/components/Breadcrumb.js +119 -0
- package/dist/components/ClearRefinements.d.ts +42 -0
- package/dist/components/ClearRefinements.d.ts.map +1 -0
- package/dist/components/ClearRefinements.js +81 -0
- package/dist/components/CurrentRefinements.d.ts +63 -0
- package/dist/components/CurrentRefinements.d.ts.map +1 -0
- package/dist/components/CurrentRefinements.js +302 -0
- package/dist/components/FacetDropdown.d.ts +94 -0
- package/dist/components/FacetDropdown.d.ts.map +1 -0
- package/dist/components/FacetDropdown.js +396 -0
- package/dist/components/Facets.d.ts +118 -0
- package/dist/components/Facets.d.ts.map +1 -0
- package/dist/components/Facets.js +785 -0
- package/dist/components/FederatedDropdown.d.ts +98 -0
- package/dist/components/FederatedDropdown.d.ts.map +1 -0
- package/dist/components/FederatedDropdown.js +526 -0
- package/dist/components/HierarchicalMenu.d.ts +55 -0
- package/dist/components/HierarchicalMenu.d.ts.map +1 -0
- package/dist/components/HierarchicalMenu.js +276 -0
- package/dist/components/Highlight.d.ts +51 -0
- package/dist/components/Highlight.d.ts.map +1 -0
- package/dist/components/Highlight.js +155 -0
- package/dist/components/HitsPerPage.d.ts +41 -0
- package/dist/components/HitsPerPage.d.ts.map +1 -0
- package/dist/components/HitsPerPage.js +82 -0
- package/dist/components/InfiniteHits.d.ts +51 -0
- package/dist/components/InfiniteHits.d.ts.map +1 -0
- package/dist/components/InfiniteHits.js +173 -0
- package/dist/components/MobileFilters.d.ts +71 -0
- package/dist/components/MobileFilters.d.ts.map +1 -0
- package/dist/components/MobileFilters.js +242 -0
- package/dist/components/Pagination.d.ts +90 -0
- package/dist/components/Pagination.d.ts.map +1 -0
- package/dist/components/Pagination.js +298 -0
- package/dist/components/QuerySuggestions.d.ts +36 -0
- package/dist/components/QuerySuggestions.d.ts.map +1 -0
- package/dist/components/QuerySuggestions.js +71 -0
- package/dist/components/QuerySuggestionsDropdown.d.ts +82 -0
- package/dist/components/QuerySuggestionsDropdown.d.ts.map +1 -0
- package/dist/components/QuerySuggestionsDropdown.js +394 -0
- package/dist/components/RangeInput.d.ts +58 -0
- package/dist/components/RangeInput.d.ts.map +1 -0
- package/dist/components/RangeInput.js +203 -0
- package/dist/components/RangeSlider.d.ts +51 -0
- package/dist/components/RangeSlider.d.ts.map +1 -0
- package/dist/components/RangeSlider.js +262 -0
- package/dist/components/Recommendations.d.ts +89 -0
- package/dist/components/Recommendations.d.ts.map +1 -0
- package/dist/components/Recommendations.js +256 -0
- package/dist/components/RichQuerySuggestions.d.ts +88 -0
- package/dist/components/RichQuerySuggestions.d.ts.map +1 -0
- package/dist/components/RichQuerySuggestions.js +498 -0
- package/dist/components/SearchBar.d.ts +57 -0
- package/dist/components/SearchBar.d.ts.map +1 -0
- package/dist/components/SearchBar.js +361 -0
- package/dist/components/SearchBarWithSuggestions.d.ts +105 -0
- package/dist/components/SearchBarWithSuggestions.d.ts.map +1 -0
- package/dist/components/SearchBarWithSuggestions.js +275 -0
- package/dist/components/SearchLayout.d.ts +35 -0
- package/dist/components/SearchLayout.d.ts.map +1 -0
- package/dist/components/SearchLayout.js +61 -0
- package/dist/components/SearchProvider.d.ts +35 -0
- package/dist/components/SearchProvider.d.ts.map +1 -0
- package/dist/components/SearchProvider.js +53 -0
- package/dist/components/SearchResults.d.ts +57 -0
- package/dist/components/SearchResults.d.ts.map +1 -0
- package/dist/components/SearchResults.js +456 -0
- package/dist/components/SortBy.d.ts +84 -0
- package/dist/components/SortBy.d.ts.map +1 -0
- package/dist/components/SortBy.js +183 -0
- package/dist/components/Stats.d.ts +51 -0
- package/dist/components/Stats.d.ts.map +1 -0
- package/dist/components/Stats.js +201 -0
- package/dist/components/primitives/ActionButtons.d.ts +27 -0
- package/dist/components/primitives/ActionButtons.d.ts.map +1 -0
- package/dist/components/primitives/ActionButtons.js +102 -0
- package/dist/components/primitives/AnalyticsProvider.d.ts +22 -0
- package/dist/components/primitives/AnalyticsProvider.d.ts.map +1 -0
- package/dist/components/primitives/AnalyticsProvider.js +87 -0
- package/dist/components/primitives/BadgeList.d.ts +14 -0
- package/dist/components/primitives/BadgeList.d.ts.map +1 -0
- package/dist/components/primitives/BadgeList.js +65 -0
- package/dist/components/primitives/CustomSelect.d.ts +40 -0
- package/dist/components/primitives/CustomSelect.d.ts.map +1 -0
- package/dist/components/primitives/CustomSelect.js +196 -0
- package/dist/components/primitives/ImageDisplay.d.ts +28 -0
- package/dist/components/primitives/ImageDisplay.d.ts.map +1 -0
- package/dist/components/primitives/ImageDisplay.js +127 -0
- package/dist/components/primitives/ImageZoom.d.ts +33 -0
- package/dist/components/primitives/ImageZoom.d.ts.map +1 -0
- package/dist/components/primitives/ImageZoom.js +433 -0
- package/dist/components/primitives/PriceDisplay.d.ts +21 -0
- package/dist/components/primitives/PriceDisplay.d.ts.map +1 -0
- package/dist/components/primitives/PriceDisplay.js +44 -0
- package/dist/components/primitives/RatingDisplay.d.ts +43 -0
- package/dist/components/primitives/RatingDisplay.d.ts.map +1 -0
- package/dist/components/primitives/RatingDisplay.js +114 -0
- package/dist/components/primitives/VariantSelector.d.ts +30 -0
- package/dist/components/primitives/VariantSelector.d.ts.map +1 -0
- package/dist/components/primitives/VariantSelector.js +155 -0
- package/dist/components/primitives/VariantSwatches.d.ts +28 -0
- package/dist/components/primitives/VariantSwatches.d.ts.map +1 -0
- package/dist/components/primitives/VariantSwatches.js +188 -0
- package/dist/components/primitives/index.d.ts +12 -0
- package/dist/components/primitives/index.d.ts.map +1 -0
- package/dist/components/primitives/index.js +11 -0
- package/dist/components/primitives/withAnalytics.d.ts +24 -0
- package/dist/components/primitives/withAnalytics.d.ts.map +1 -0
- package/dist/components/primitives/withAnalytics.js +73 -0
- package/dist/components/product-page/ProductGallery.d.ts +26 -0
- package/dist/components/product-page/ProductGallery.d.ts.map +1 -0
- package/dist/components/product-page/ProductGallery.js +13 -0
- package/dist/components/product-page/ProductInfo.d.ts +44 -0
- package/dist/components/product-page/ProductInfo.d.ts.map +1 -0
- package/dist/components/product-page/ProductInfo.js +34 -0
- package/dist/components/product-page/ProductRecommendations.d.ts +21 -0
- package/dist/components/product-page/ProductRecommendations.d.ts.map +1 -0
- package/dist/components/product-page/ProductRecommendations.js +17 -0
- package/dist/components/product-page/index.d.ts +4 -0
- package/dist/components/product-page/index.d.ts.map +1 -0
- package/dist/components/product-page/index.js +3 -0
- package/dist/components/section-primitives/SectionError.d.ts +11 -0
- package/dist/components/section-primitives/SectionError.d.ts.map +1 -0
- package/dist/components/section-primitives/SectionError.js +13 -0
- package/dist/components/section-primitives/SectionItemGrid.d.ts +18 -0
- package/dist/components/section-primitives/SectionItemGrid.d.ts.map +1 -0
- package/dist/components/section-primitives/SectionItemGrid.js +14 -0
- package/dist/components/section-primitives/SectionLoading.d.ts +11 -0
- package/dist/components/section-primitives/SectionLoading.d.ts.map +1 -0
- package/dist/components/section-primitives/SectionLoading.js +11 -0
- package/dist/components/section-primitives/SectionSearchContext.d.ts +17 -0
- package/dist/components/section-primitives/SectionSearchContext.d.ts.map +1 -0
- package/dist/components/section-primitives/SectionSearchContext.js +17 -0
- package/dist/components/section-primitives/SectionSearchProvider.d.ts +25 -0
- package/dist/components/section-primitives/SectionSearchProvider.d.ts.map +1 -0
- package/dist/components/section-primitives/SectionSearchProvider.js +106 -0
- package/dist/components/section-primitives/index.d.ts +5 -0
- package/dist/components/section-primitives/index.d.ts.map +1 -0
- package/dist/components/section-primitives/index.js +4 -0
- package/dist/components/suggestions/AmazonDropdown.d.ts +30 -0
- package/dist/components/suggestions/AmazonDropdown.d.ts.map +1 -0
- package/dist/components/suggestions/AmazonDropdown.js +509 -0
- package/dist/components/suggestions/GoogleDropdown.d.ts +31 -0
- package/dist/components/suggestions/GoogleDropdown.d.ts.map +1 -0
- package/dist/components/suggestions/GoogleDropdown.js +349 -0
- package/dist/components/suggestions/MinimalDropdown.d.ts +24 -0
- package/dist/components/suggestions/MinimalDropdown.d.ts.map +1 -0
- package/dist/components/suggestions/MinimalDropdown.js +312 -0
- package/dist/components/suggestions/MobileSheetDropdown.d.ts +31 -0
- package/dist/components/suggestions/MobileSheetDropdown.d.ts.map +1 -0
- package/dist/components/suggestions/MobileSheetDropdown.js +483 -0
- package/dist/components/suggestions/PinterestDropdown.d.ts +29 -0
- package/dist/components/suggestions/PinterestDropdown.d.ts.map +1 -0
- package/dist/components/suggestions/PinterestDropdown.js +446 -0
- package/dist/components/suggestions/ShopifyDropdown.d.ts +27 -0
- package/dist/components/suggestions/ShopifyDropdown.d.ts.map +1 -0
- package/dist/components/suggestions/ShopifyDropdown.js +448 -0
- package/dist/components/suggestions/SpotlightDropdown.d.ts +33 -0
- package/dist/components/suggestions/SpotlightDropdown.d.ts.map +1 -0
- package/dist/components/suggestions/SpotlightDropdown.js +544 -0
- package/dist/components/suggestions/SuggestionSearchBar.d.ts +127 -0
- package/dist/components/suggestions/SuggestionSearchBar.d.ts.map +1 -0
- package/dist/components/suggestions/SuggestionSearchBar.js +644 -0
- package/dist/components/suggestions/index.d.ts +37 -0
- package/dist/components/suggestions/index.d.ts.map +1 -0
- package/dist/components/suggestions/index.js +59 -0
- package/dist/components/suggestions/styles/index.d.ts +11 -0
- package/dist/components/suggestions/styles/index.d.ts.map +1 -0
- package/dist/components/suggestions/styles/index.js +289 -0
- package/dist/components/suggestions/styles/responsive.d.ts +107 -0
- package/dist/components/suggestions/styles/responsive.d.ts.map +1 -0
- package/dist/components/suggestions/styles/responsive.js +237 -0
- package/dist/components/suggestions/types.d.ts +511 -0
- package/dist/components/suggestions/types.d.ts.map +1 -0
- package/dist/components/suggestions/types.js +6 -0
- package/dist/components/suggestions/utils.d.ts +259 -0
- package/dist/components/suggestions/utils.d.ts.map +1 -0
- package/dist/components/suggestions/utils.js +668 -0
- package/dist/components/suggestions-primitives/CategoriesTabs.d.ts +13 -0
- package/dist/components/suggestions-primitives/CategoriesTabs.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/CategoriesTabs.js +35 -0
- package/dist/components/suggestions-primitives/DropdownPanel.d.ts +24 -0
- package/dist/components/suggestions-primitives/DropdownPanel.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/DropdownPanel.js +67 -0
- package/dist/components/suggestions-primitives/ItemCard.d.ts +48 -0
- package/dist/components/suggestions-primitives/ItemCard.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/ItemCard.js +103 -0
- package/dist/components/suggestions-primitives/ItemGrid.d.ts +28 -0
- package/dist/components/suggestions-primitives/ItemGrid.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/ItemGrid.js +55 -0
- package/dist/components/suggestions-primitives/ProductCard.d.ts +45 -0
- package/dist/components/suggestions-primitives/ProductCard.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/ProductCard.js +177 -0
- package/dist/components/suggestions-primitives/ProductCardLayouts.d.ts +44 -0
- package/dist/components/suggestions-primitives/ProductCardLayouts.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/ProductCardLayouts.js +137 -0
- package/dist/components/suggestions-primitives/ProductGrid.d.ts +22 -0
- package/dist/components/suggestions-primitives/ProductGrid.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/ProductGrid.js +41 -0
- package/dist/components/suggestions-primitives/RecentSearchesList.d.ts +17 -0
- package/dist/components/suggestions-primitives/RecentSearchesList.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/RecentSearchesList.js +46 -0
- package/dist/components/suggestions-primitives/SearchInput.d.ts +23 -0
- package/dist/components/suggestions-primitives/SearchInput.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/SearchInput.js +114 -0
- package/dist/components/suggestions-primitives/SuggestionItem.d.ts +31 -0
- package/dist/components/suggestions-primitives/SuggestionItem.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/SuggestionItem.js +47 -0
- package/dist/components/suggestions-primitives/SuggestionList.d.ts +26 -0
- package/dist/components/suggestions-primitives/SuggestionList.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/SuggestionList.js +35 -0
- package/dist/components/suggestions-primitives/SuggestionsContext.d.ts +44 -0
- package/dist/components/suggestions-primitives/SuggestionsContext.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/SuggestionsContext.js +18 -0
- package/dist/components/suggestions-primitives/SuggestionsDropdownComposition.d.ts +24 -0
- package/dist/components/suggestions-primitives/SuggestionsDropdownComposition.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/SuggestionsDropdownComposition.js +30 -0
- package/dist/components/suggestions-primitives/SuggestionsError.d.ts +11 -0
- package/dist/components/suggestions-primitives/SuggestionsError.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/SuggestionsError.js +19 -0
- package/dist/components/suggestions-primitives/SuggestionsLoading.d.ts +11 -0
- package/dist/components/suggestions-primitives/SuggestionsLoading.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/SuggestionsLoading.js +17 -0
- package/dist/components/suggestions-primitives/SuggestionsProvider.d.ts +38 -0
- package/dist/components/suggestions-primitives/SuggestionsProvider.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/SuggestionsProvider.js +259 -0
- package/dist/components/suggestions-primitives/TrendingList.d.ts +17 -0
- package/dist/components/suggestions-primitives/TrendingList.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/TrendingList.js +48 -0
- package/dist/components/suggestions-primitives/highlightMarkup.d.ts +31 -0
- package/dist/components/suggestions-primitives/highlightMarkup.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/highlightMarkup.js +70 -0
- package/dist/components/suggestions-primitives/index.d.ts +39 -0
- package/dist/components/suggestions-primitives/index.d.ts.map +1 -0
- package/dist/components/suggestions-primitives/index.js +24 -0
- package/dist/docsearch/components/DocSearch.d.ts +4 -0
- package/dist/docsearch/components/DocSearch.d.ts.map +1 -0
- package/dist/docsearch/components/DocSearch.js +93 -0
- package/dist/docsearch/components/DocSearchButton.d.ts +4 -0
- package/dist/docsearch/components/DocSearchButton.d.ts.map +1 -0
- package/dist/docsearch/components/DocSearchButton.js +12 -0
- package/dist/docsearch/components/Footer.d.ts +8 -0
- package/dist/docsearch/components/Footer.d.ts.map +1 -0
- package/dist/docsearch/components/Footer.js +40 -0
- package/dist/docsearch/components/Highlight.d.ts +9 -0
- package/dist/docsearch/components/Highlight.d.ts.map +1 -0
- package/dist/docsearch/components/Highlight.js +48 -0
- package/dist/docsearch/components/Hit.d.ts +15 -0
- package/dist/docsearch/components/Hit.d.ts.map +1 -0
- package/dist/docsearch/components/Hit.js +96 -0
- package/dist/docsearch/components/Modal.d.ts +10 -0
- package/dist/docsearch/components/Modal.d.ts.map +1 -0
- package/dist/docsearch/components/Modal.js +57 -0
- package/dist/docsearch/components/Results.d.ts +23 -0
- package/dist/docsearch/components/Results.d.ts.map +1 -0
- package/dist/docsearch/components/Results.js +141 -0
- package/dist/docsearch/components/SearchBox.d.ts +11 -0
- package/dist/docsearch/components/SearchBox.d.ts.map +1 -0
- package/dist/docsearch/components/SearchBox.js +16 -0
- package/dist/docsearch/hooks/useDocSearch.d.ts +33 -0
- package/dist/docsearch/hooks/useDocSearch.d.ts.map +1 -0
- package/dist/docsearch/hooks/useDocSearch.js +224 -0
- package/dist/docsearch/hooks/useKeyboard.d.ts +17 -0
- package/dist/docsearch/hooks/useKeyboard.d.ts.map +1 -0
- package/dist/docsearch/hooks/useKeyboard.js +71 -0
- package/dist/docsearch/hooks/useSeekoraSearch.d.ts +27 -0
- package/dist/docsearch/hooks/useSeekoraSearch.d.ts.map +1 -0
- package/dist/docsearch/hooks/useSeekoraSearch.js +213 -0
- package/dist/docsearch/index.d.ts +13 -0
- package/dist/docsearch/index.d.ts.map +1 -0
- package/dist/docsearch/index.js +11 -0
- package/dist/docsearch/types.d.ts +175 -0
- package/dist/docsearch/types.d.ts.map +1 -0
- package/dist/docsearch/types.js +4 -0
- package/dist/docsearch.css +234 -0
- package/dist/hooks/useAnalytics.d.ts +24 -0
- package/dist/hooks/useAnalytics.d.ts.map +1 -0
- package/dist/hooks/useAnalytics.js +67 -0
- package/dist/hooks/useClickTracking.d.ts +36 -0
- package/dist/hooks/useClickTracking.d.ts.map +1 -0
- package/dist/hooks/useClickTracking.js +89 -0
- package/dist/hooks/useExperiment.d.ts +25 -0
- package/dist/hooks/useExperiment.d.ts.map +1 -0
- package/dist/hooks/useExperiment.js +123 -0
- package/dist/hooks/useFilters.d.ts +27 -0
- package/dist/hooks/useFilters.d.ts.map +1 -0
- package/dist/hooks/useFilters.js +86 -0
- package/dist/hooks/useKeyboardNavigation.d.ts +51 -0
- package/dist/hooks/useKeyboardNavigation.d.ts.map +1 -0
- package/dist/hooks/useKeyboardNavigation.js +113 -0
- package/dist/hooks/useNaturalLanguageFilters.d.ts +48 -0
- package/dist/hooks/useNaturalLanguageFilters.d.ts.map +1 -0
- package/dist/hooks/useNaturalLanguageFilters.js +221 -0
- package/dist/hooks/useProductAnalytics.d.ts +49 -0
- package/dist/hooks/useProductAnalytics.d.ts.map +1 -0
- package/dist/hooks/useProductAnalytics.js +116 -0
- package/dist/hooks/useQuerySuggestions.d.ts +21 -0
- package/dist/hooks/useQuerySuggestions.d.ts.map +1 -0
- package/dist/hooks/useQuerySuggestions.js +84 -0
- package/dist/hooks/useQuerySuggestionsEnhanced.d.ts +120 -0
- package/dist/hooks/useQuerySuggestionsEnhanced.d.ts.map +1 -0
- package/dist/hooks/useQuerySuggestionsEnhanced.js +444 -0
- package/dist/hooks/useSearchState.d.ts +35 -0
- package/dist/hooks/useSearchState.d.ts.map +1 -0
- package/dist/hooks/useSearchState.js +68 -0
- package/dist/hooks/useSeekoraSearch.d.ts +20 -0
- package/dist/hooks/useSeekoraSearch.d.ts.map +1 -0
- package/dist/hooks/useSeekoraSearch.js +63 -0
- package/dist/hooks/useSmartSuggestions.d.ts +55 -0
- package/dist/hooks/useSmartSuggestions.d.ts.map +1 -0
- package/dist/hooks/useSmartSuggestions.js +236 -0
- package/dist/hooks/useSuggestionsAnalytics.d.ts +93 -0
- package/dist/hooks/useSuggestionsAnalytics.d.ts.map +1 -0
- package/dist/hooks/useSuggestionsAnalytics.js +239 -0
- package/dist/hooks/useVariantSelection.d.ts +28 -0
- package/dist/hooks/useVariantSelection.d.ts.map +1 -0
- package/dist/hooks/useVariantSelection.js +44 -0
- package/dist/index.d.ts +105 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +110 -0
- package/dist/index.umd.js +1 -0
- package/dist/src/index.d.ts +4469 -0
- package/dist/src/index.esm.js +18952 -0
- package/dist/src/index.esm.js.map +1 -0
- package/dist/src/index.js +19086 -0
- package/dist/src/index.js.map +1 -0
- package/dist/themes/createTheme.d.ts +8 -0
- package/dist/themes/createTheme.d.ts.map +1 -0
- package/dist/themes/createTheme.js +10 -0
- package/dist/themes/dark.d.ts +6 -0
- package/dist/themes/dark.d.ts.map +1 -0
- package/dist/themes/dark.js +34 -0
- package/dist/themes/default.d.ts +6 -0
- package/dist/themes/default.d.ts.map +1 -0
- package/dist/themes/default.js +71 -0
- package/dist/themes/mergeThemes.d.ts +7 -0
- package/dist/themes/mergeThemes.d.ts.map +1 -0
- package/dist/themes/mergeThemes.js +6 -0
- package/dist/themes/minimal.d.ts +6 -0
- package/dist/themes/minimal.d.ts.map +1 -0
- package/dist/themes/minimal.js +34 -0
- package/dist/themes/suggestions.d.ts +216 -0
- package/dist/themes/suggestions.d.ts.map +1 -0
- package/dist/themes/suggestions.js +546 -0
- package/dist/themes/types.d.ts +7 -0
- package/dist/themes/types.d.ts.map +1 -0
- package/dist/themes/types.js +6 -0
- package/dist/types/index.d.ts +33 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +4 -0
- package/dist/utils/responsive.d.ts +130 -0
- package/dist/utils/responsive.d.ts.map +1 -0
- package/dist/utils/responsive.js +225 -0
- package/package.json +68 -0
- package/src/docsearch/docsearch.css +234 -0
|
@@ -0,0 +1,668 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for Query Suggestions components
|
|
3
|
+
*/
|
|
4
|
+
import { createElement } from 'react';
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// Field Extraction
|
|
7
|
+
// ============================================================================
|
|
8
|
+
/**
|
|
9
|
+
* Get nested value from object using dot notation
|
|
10
|
+
* @example getNestedValue({ a: { b: 'value' } }, 'a.b') => 'value'
|
|
11
|
+
*/
|
|
12
|
+
export const getNestedValue = (obj, path) => {
|
|
13
|
+
if (!obj || !path)
|
|
14
|
+
return undefined;
|
|
15
|
+
return path.split('.').reduce((current, key) => {
|
|
16
|
+
if (current === null || current === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
return current[key];
|
|
19
|
+
}, obj);
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Extract suggestion fields from raw data
|
|
23
|
+
*/
|
|
24
|
+
export const extractSuggestion = (item, mapping = { query: 'query' }) => {
|
|
25
|
+
return {
|
|
26
|
+
query: getNestedValue(item, mapping.query) ?? '',
|
|
27
|
+
count: mapping.count ? getNestedValue(item, mapping.count) : undefined,
|
|
28
|
+
id: mapping.id ? getNestedValue(item, mapping.id) : item?.objectID || item?.id,
|
|
29
|
+
categories: mapping.categories ? getNestedValue(item, mapping.categories) : undefined,
|
|
30
|
+
highlighted: mapping.highlighted ? getNestedValue(item, mapping.highlighted) : undefined,
|
|
31
|
+
_raw: item,
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Extract product fields from raw data
|
|
36
|
+
*/
|
|
37
|
+
export const extractProduct = (item, mapping = { id: 'id', title: 'title' }) => {
|
|
38
|
+
return {
|
|
39
|
+
id: getNestedValue(item, mapping.id) ?? item?.objectID ?? item?.id,
|
|
40
|
+
title: getNestedValue(item, mapping.title) ?? '',
|
|
41
|
+
image: mapping.image ? getNestedValue(item, mapping.image) : undefined,
|
|
42
|
+
price: mapping.price ? getNestedValue(item, mapping.price) : undefined,
|
|
43
|
+
comparePrice: mapping.comparePrice ? getNestedValue(item, mapping.comparePrice) : undefined,
|
|
44
|
+
url: mapping.url ? getNestedValue(item, mapping.url) : undefined,
|
|
45
|
+
brand: mapping.brand ? getNestedValue(item, mapping.brand) : undefined,
|
|
46
|
+
category: mapping.category ? getNestedValue(item, mapping.category) : undefined,
|
|
47
|
+
rating: mapping.rating ? getNestedValue(item, mapping.rating) : undefined,
|
|
48
|
+
reviewCount: mapping.reviewCount ? getNestedValue(item, mapping.reviewCount) : undefined,
|
|
49
|
+
discount: mapping.discount ? getNestedValue(item, mapping.discount) : undefined,
|
|
50
|
+
inStock: mapping.inStock ? getNestedValue(item, mapping.inStock) : undefined,
|
|
51
|
+
currency: mapping.currency ? getNestedValue(item, mapping.currency) : undefined,
|
|
52
|
+
images: mapping.images ? getNestedValue(item, mapping.images) : item?.images,
|
|
53
|
+
originalPrice: mapping.originalPrice ? getNestedValue(item, mapping.originalPrice) : (item?.original_price ?? item?.compare_at_price),
|
|
54
|
+
available: mapping.available ? getNestedValue(item, mapping.available) : item?.available,
|
|
55
|
+
options: mapping.options ? getNestedValue(item, mapping.options) : item?.options,
|
|
56
|
+
variants: mapping.variants ? getNestedValue(item, mapping.variants) : item?.variants,
|
|
57
|
+
tags: mapping.tags ? getNestedValue(item, mapping.tags) : item?.tags,
|
|
58
|
+
_raw: item,
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Extract category fields from raw data
|
|
63
|
+
*/
|
|
64
|
+
export const extractCategory = (item, mapping = { id: 'id', label: 'label' }) => {
|
|
65
|
+
return {
|
|
66
|
+
id: getNestedValue(item, mapping.id) ?? item?.id,
|
|
67
|
+
label: getNestedValue(item, mapping.label) ?? '',
|
|
68
|
+
count: mapping.count ? getNestedValue(item, mapping.count) : undefined,
|
|
69
|
+
icon: mapping.icon ? getNestedValue(item, mapping.icon) : undefined,
|
|
70
|
+
image: mapping.image ? getNestedValue(item, mapping.image) : undefined,
|
|
71
|
+
_raw: item,
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Extract brand fields from raw data
|
|
76
|
+
*/
|
|
77
|
+
export const extractBrand = (item, mapping = { name: 'name' }) => {
|
|
78
|
+
return {
|
|
79
|
+
id: mapping.id ? getNestedValue(item, mapping.id) : item?.id,
|
|
80
|
+
name: getNestedValue(item, mapping.name) ?? '',
|
|
81
|
+
logo: mapping.logo ? getNestedValue(item, mapping.logo) : undefined,
|
|
82
|
+
count: mapping.count ? getNestedValue(item, mapping.count) : undefined,
|
|
83
|
+
_raw: item,
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
// ============================================================================
|
|
87
|
+
// Formatting
|
|
88
|
+
// ============================================================================
|
|
89
|
+
/**
|
|
90
|
+
* Format price with currency
|
|
91
|
+
*/
|
|
92
|
+
export const formatPrice = (value, config = {}) => {
|
|
93
|
+
if (value === undefined || value === null)
|
|
94
|
+
return '';
|
|
95
|
+
const { currency = '$', currencyPosition = 'before', priceDecimals = 2 } = config;
|
|
96
|
+
const num = typeof value === 'string' ? parseFloat(value) : value;
|
|
97
|
+
if (isNaN(num))
|
|
98
|
+
return String(value);
|
|
99
|
+
const formatted = num.toLocaleString(undefined, {
|
|
100
|
+
minimumFractionDigits: priceDecimals,
|
|
101
|
+
maximumFractionDigits: priceDecimals,
|
|
102
|
+
});
|
|
103
|
+
return currencyPosition === 'before'
|
|
104
|
+
? `${currency}${formatted}`
|
|
105
|
+
: `${formatted}${currency}`;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Format count with abbreviation (1.2K, 3.4M, etc.)
|
|
109
|
+
*/
|
|
110
|
+
export const formatCount = (count) => {
|
|
111
|
+
if (count === undefined || count === null)
|
|
112
|
+
return '';
|
|
113
|
+
if (count < 1000)
|
|
114
|
+
return count.toString();
|
|
115
|
+
if (count < 1000000)
|
|
116
|
+
return `${(count / 1000).toFixed(1)}K`;
|
|
117
|
+
return `${(count / 1000000).toFixed(1)}M`;
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Calculate discount percentage
|
|
121
|
+
*/
|
|
122
|
+
export const calculateDiscount = (price, comparePrice) => {
|
|
123
|
+
if (!price || !comparePrice || comparePrice <= price)
|
|
124
|
+
return undefined;
|
|
125
|
+
return Math.round(((comparePrice - price) / comparePrice) * 100);
|
|
126
|
+
};
|
|
127
|
+
// ============================================================================
|
|
128
|
+
// Text Processing
|
|
129
|
+
// ============================================================================
|
|
130
|
+
/**
|
|
131
|
+
* Escape HTML special characters
|
|
132
|
+
*/
|
|
133
|
+
export const escapeHtml = (text) => {
|
|
134
|
+
const map = {
|
|
135
|
+
'&': '&',
|
|
136
|
+
'<': '<',
|
|
137
|
+
'>': '>',
|
|
138
|
+
'"': '"',
|
|
139
|
+
"'": ''',
|
|
140
|
+
};
|
|
141
|
+
return text.replace(/[&<>"']/g, (char) => map[char]);
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* Highlight matching text in a string
|
|
145
|
+
*/
|
|
146
|
+
export const highlightText = (text, query, options = {}) => {
|
|
147
|
+
if (!query || !text)
|
|
148
|
+
return escapeHtml(text);
|
|
149
|
+
const { tag = 'mark', className = '', style } = options;
|
|
150
|
+
const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
151
|
+
const regex = new RegExp(`(${escapedQuery})`, 'gi');
|
|
152
|
+
const styleAttr = style
|
|
153
|
+
? ` style="${Object.entries(style).map(([k, v]) => `${k.replace(/([A-Z])/g, '-$1').toLowerCase()}:${v}`).join(';')}"`
|
|
154
|
+
: '';
|
|
155
|
+
const classAttr = className ? ` class="${className}"` : '';
|
|
156
|
+
return escapeHtml(text).replace(new RegExp(`(${escapeHtml(escapedQuery)})`, 'gi'), `<${tag}${classAttr}${styleAttr}>$1</${tag}>`);
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Safe React-based highlight rendering (no dangerouslySetInnerHTML).
|
|
160
|
+
* Returns React nodes with matched text wrapped in the specified tag element.
|
|
161
|
+
*/
|
|
162
|
+
export const highlightTextReact = (text, query, options = {}) => {
|
|
163
|
+
if (!query || !text)
|
|
164
|
+
return text;
|
|
165
|
+
const { tag = 'mark', className, style } = options;
|
|
166
|
+
const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
167
|
+
const regex = new RegExp(`(${escapedQuery})`, 'gi');
|
|
168
|
+
const parts = text.split(regex);
|
|
169
|
+
if (parts.length <= 1)
|
|
170
|
+
return text;
|
|
171
|
+
const defaultHighlightStyle = {
|
|
172
|
+
backgroundColor: 'var(--seekora-highlight-bg, rgba(251, 191, 36, 0.4))',
|
|
173
|
+
fontWeight: 500,
|
|
174
|
+
borderRadius: '2px',
|
|
175
|
+
padding: '0 2px',
|
|
176
|
+
...style,
|
|
177
|
+
};
|
|
178
|
+
return parts.map((part, i) => {
|
|
179
|
+
if (regex.test(part)) {
|
|
180
|
+
// Reset lastIndex after test
|
|
181
|
+
regex.lastIndex = 0;
|
|
182
|
+
return createElement(tag, {
|
|
183
|
+
key: i,
|
|
184
|
+
className,
|
|
185
|
+
style: defaultHighlightStyle,
|
|
186
|
+
}, part);
|
|
187
|
+
}
|
|
188
|
+
// Reset lastIndex after test
|
|
189
|
+
regex.lastIndex = 0;
|
|
190
|
+
return part;
|
|
191
|
+
});
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* Truncate text to specified length
|
|
195
|
+
*/
|
|
196
|
+
export const truncateText = (text, maxLength, ellipsis = '...') => {
|
|
197
|
+
if (!text || text.length <= maxLength)
|
|
198
|
+
return text;
|
|
199
|
+
return text.slice(0, maxLength - ellipsis.length) + ellipsis;
|
|
200
|
+
};
|
|
201
|
+
// ============================================================================
|
|
202
|
+
// Variant Utilities
|
|
203
|
+
// ============================================================================
|
|
204
|
+
/**
|
|
205
|
+
* Extract badges from product tags (Shopify convention: "badge:new", "badge:limited")
|
|
206
|
+
* and auto-generate sale/soldOut badges from product data.
|
|
207
|
+
*/
|
|
208
|
+
export const extractBadges = (tags, product) => {
|
|
209
|
+
const badges = [];
|
|
210
|
+
// Extract from tags (Shopify convention)
|
|
211
|
+
if (tags) {
|
|
212
|
+
for (const tag of tags) {
|
|
213
|
+
const lower = tag.toLowerCase().trim();
|
|
214
|
+
if (lower.startsWith('badge:') || lower.startsWith('badge: ')) {
|
|
215
|
+
const text = tag.slice(tag.indexOf(':') + 1).trim();
|
|
216
|
+
if (text) {
|
|
217
|
+
badges.push({ text, type: 'custom' });
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else if (lower === 'new' || lower === 'new arrival') {
|
|
221
|
+
badges.push({ text: 'New', type: 'new' });
|
|
222
|
+
}
|
|
223
|
+
else if (lower === 'limited' || lower === 'limited edition') {
|
|
224
|
+
badges.push({ text: 'Limited', type: 'limited' });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// Auto-generate sale badge
|
|
229
|
+
if (product) {
|
|
230
|
+
const comparePrice = product.original_price ?? product.compare_at_price;
|
|
231
|
+
if (comparePrice && product.price && comparePrice > product.price) {
|
|
232
|
+
const discount = Math.round(((comparePrice - product.price) / comparePrice) * 100);
|
|
233
|
+
badges.push({ text: `${discount}% Off`, type: 'sale' });
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Auto-generate sold out badge
|
|
237
|
+
if (product && product.available === false) {
|
|
238
|
+
badges.push({ text: 'Sold Out', type: 'soldOut' });
|
|
239
|
+
}
|
|
240
|
+
return badges;
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* Compute min/max price from variants. Returns null if all variants have the same price.
|
|
244
|
+
*/
|
|
245
|
+
export const getPriceRange = (variants) => {
|
|
246
|
+
if (!variants || variants.length === 0)
|
|
247
|
+
return null;
|
|
248
|
+
const prices = variants
|
|
249
|
+
.map((v) => v.price)
|
|
250
|
+
.filter((p) => p != null && !isNaN(p));
|
|
251
|
+
if (prices.length === 0)
|
|
252
|
+
return null;
|
|
253
|
+
const min = Math.min(...prices);
|
|
254
|
+
const max = Math.max(...prices);
|
|
255
|
+
if (min === max)
|
|
256
|
+
return null;
|
|
257
|
+
return { min, max };
|
|
258
|
+
};
|
|
259
|
+
/**
|
|
260
|
+
* Format a price range like "$54.00 - $72.00"
|
|
261
|
+
*/
|
|
262
|
+
export const formatPriceRange = (range, config = {}) => {
|
|
263
|
+
return `${formatPrice(range.min, config)} - ${formatPrice(range.max, config)}`;
|
|
264
|
+
};
|
|
265
|
+
/**
|
|
266
|
+
* Given current selections, return which values for an option are still available
|
|
267
|
+
* based on variant availability.
|
|
268
|
+
*/
|
|
269
|
+
export const getAvailableValuesForOption = (optionName, options, variants, selections) => {
|
|
270
|
+
const option = options.find((o) => o.name === optionName);
|
|
271
|
+
if (!option)
|
|
272
|
+
return [];
|
|
273
|
+
const optionIndex = options.indexOf(option);
|
|
274
|
+
const optionKey = `option${optionIndex + 1}`;
|
|
275
|
+
return option.values.map((value) => {
|
|
276
|
+
// Check if any variant with this value and current other selections is available
|
|
277
|
+
const available = variants.some((variant) => {
|
|
278
|
+
if (variant[optionKey] !== value)
|
|
279
|
+
return false;
|
|
280
|
+
if (variant.available === false)
|
|
281
|
+
return false;
|
|
282
|
+
// Check other selections match
|
|
283
|
+
for (const [selName, selValue] of Object.entries(selections)) {
|
|
284
|
+
if (selName === optionName)
|
|
285
|
+
continue;
|
|
286
|
+
const selOption = options.find((o) => o.name === selName);
|
|
287
|
+
if (!selOption)
|
|
288
|
+
continue;
|
|
289
|
+
const selIdx = options.indexOf(selOption);
|
|
290
|
+
const selKey = `option${selIdx + 1}`;
|
|
291
|
+
if (variant[selKey] !== selValue)
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
return true;
|
|
295
|
+
});
|
|
296
|
+
return { value, available };
|
|
297
|
+
});
|
|
298
|
+
};
|
|
299
|
+
/**
|
|
300
|
+
* Find the exact variant matching all selected options.
|
|
301
|
+
*/
|
|
302
|
+
export const findVariantBySelections = (options, variants, selections) => {
|
|
303
|
+
return variants.find((variant) => {
|
|
304
|
+
return options.every((option, idx) => {
|
|
305
|
+
const key = `option${idx + 1}`;
|
|
306
|
+
const selected = selections[option.name];
|
|
307
|
+
if (!selected)
|
|
308
|
+
return true; // not yet selected
|
|
309
|
+
return variant[key] === selected;
|
|
310
|
+
});
|
|
311
|
+
}) ?? null;
|
|
312
|
+
};
|
|
313
|
+
// ============================================================================
|
|
314
|
+
// Theme & Styling
|
|
315
|
+
// ============================================================================
|
|
316
|
+
/**
|
|
317
|
+
* Generate CSS variables from theme config
|
|
318
|
+
*/
|
|
319
|
+
export const generateCSSVariables = (theme, prefix = 'seekora') => {
|
|
320
|
+
const vars = {};
|
|
321
|
+
if (theme.primaryColor)
|
|
322
|
+
vars[`--${prefix}-primary`] = theme.primaryColor;
|
|
323
|
+
if (theme.backgroundColor)
|
|
324
|
+
vars[`--${prefix}-bg-surface`] = theme.backgroundColor;
|
|
325
|
+
if (theme.surfaceColor)
|
|
326
|
+
vars[`--${prefix}-bg-secondary`] = theme.surfaceColor;
|
|
327
|
+
if (theme.textColor)
|
|
328
|
+
vars[`--${prefix}-text-primary`] = theme.textColor;
|
|
329
|
+
if (theme.textSecondaryColor)
|
|
330
|
+
vars[`--${prefix}-text-secondary`] = theme.textSecondaryColor;
|
|
331
|
+
if (theme.borderColor)
|
|
332
|
+
vars[`--${prefix}-border-color`] = theme.borderColor;
|
|
333
|
+
if (theme.hoverColor)
|
|
334
|
+
vars[`--${prefix}-bg-hover`] = theme.hoverColor;
|
|
335
|
+
if (theme.highlightColor)
|
|
336
|
+
vars[`--${prefix}-highlight-bg`] = theme.highlightColor;
|
|
337
|
+
if (theme.successColor)
|
|
338
|
+
vars[`--${prefix}-success`] = theme.successColor;
|
|
339
|
+
if (theme.errorColor)
|
|
340
|
+
vars[`--${prefix}-error`] = theme.errorColor;
|
|
341
|
+
if (theme.fontFamily)
|
|
342
|
+
vars[`--${prefix}-font-family`] = theme.fontFamily;
|
|
343
|
+
if (theme.fontSize)
|
|
344
|
+
vars[`--${prefix}-font-size`] = typeof theme.fontSize === 'number' ? `${theme.fontSize}px` : theme.fontSize;
|
|
345
|
+
if (theme.borderRadius)
|
|
346
|
+
vars[`--${prefix}-border-radius`] = typeof theme.borderRadius === 'number' ? `${theme.borderRadius}px` : theme.borderRadius;
|
|
347
|
+
if (theme.boxShadow)
|
|
348
|
+
vars[`--${prefix}-box-shadow`] = theme.boxShadow;
|
|
349
|
+
// Merge custom CSS variables
|
|
350
|
+
if (theme.cssVariables) {
|
|
351
|
+
Object.entries(theme.cssVariables).forEach(([key, value]) => {
|
|
352
|
+
vars[key.startsWith('--') ? key : `--${key}`] = value;
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
return vars;
|
|
356
|
+
};
|
|
357
|
+
/**
|
|
358
|
+
* Merge class names, filtering out falsy values
|
|
359
|
+
*/
|
|
360
|
+
export const cx = (...classes) => {
|
|
361
|
+
return classes.filter(Boolean).join(' ');
|
|
362
|
+
};
|
|
363
|
+
/**
|
|
364
|
+
* Merge styles, filtering out undefined values
|
|
365
|
+
*/
|
|
366
|
+
export const mergeStyles = (...styles) => {
|
|
367
|
+
return Object.assign({}, ...styles.filter(Boolean));
|
|
368
|
+
};
|
|
369
|
+
export const animations = {
|
|
370
|
+
fade: {
|
|
371
|
+
initial: { opacity: 0 },
|
|
372
|
+
animate: { opacity: 1 },
|
|
373
|
+
exit: { opacity: 0 },
|
|
374
|
+
},
|
|
375
|
+
slide: {
|
|
376
|
+
initial: { opacity: 0, transform: 'translateY(-10px)' },
|
|
377
|
+
animate: { opacity: 1, transform: 'translateY(0)' },
|
|
378
|
+
exit: { opacity: 0, transform: 'translateY(-10px)' },
|
|
379
|
+
},
|
|
380
|
+
scale: {
|
|
381
|
+
initial: { opacity: 0, transform: 'scale(0.95)' },
|
|
382
|
+
animate: { opacity: 1, transform: 'scale(1)' },
|
|
383
|
+
exit: { opacity: 0, transform: 'scale(0.95)' },
|
|
384
|
+
},
|
|
385
|
+
spring: {
|
|
386
|
+
initial: { opacity: 0, transform: 'translateY(-20px) scale(0.9)' },
|
|
387
|
+
animate: { opacity: 1, transform: 'translateY(0) scale(1)' },
|
|
388
|
+
exit: { opacity: 0, transform: 'translateY(-10px) scale(0.95)' },
|
|
389
|
+
},
|
|
390
|
+
};
|
|
391
|
+
/**
|
|
392
|
+
* Get animation keyframes CSS
|
|
393
|
+
*/
|
|
394
|
+
export const getAnimationCSS = (type, duration = 200) => {
|
|
395
|
+
if (type === 'none')
|
|
396
|
+
return '';
|
|
397
|
+
const anim = animations[type];
|
|
398
|
+
return `
|
|
399
|
+
@keyframes seekoraDropdownEnter {
|
|
400
|
+
from {
|
|
401
|
+
opacity: ${anim.initial.opacity};
|
|
402
|
+
transform: ${anim.initial.transform || 'none'};
|
|
403
|
+
}
|
|
404
|
+
to {
|
|
405
|
+
opacity: ${anim.animate.opacity};
|
|
406
|
+
transform: ${anim.animate.transform || 'none'};
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
@keyframes seekoraDropdownExit {
|
|
410
|
+
from {
|
|
411
|
+
opacity: ${anim.animate.opacity};
|
|
412
|
+
transform: ${anim.animate.transform || 'none'};
|
|
413
|
+
}
|
|
414
|
+
to {
|
|
415
|
+
opacity: ${anim.exit.opacity};
|
|
416
|
+
transform: ${anim.exit.transform || 'none'};
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
`;
|
|
420
|
+
};
|
|
421
|
+
// ============================================================================
|
|
422
|
+
// Keyboard Navigation
|
|
423
|
+
// ============================================================================
|
|
424
|
+
/**
|
|
425
|
+
* Create keyboard handler for dropdown navigation
|
|
426
|
+
*/
|
|
427
|
+
export const createKeyboardHandler = (handlers) => {
|
|
428
|
+
return (event) => {
|
|
429
|
+
switch (event.key) {
|
|
430
|
+
case 'ArrowUp':
|
|
431
|
+
event.preventDefault();
|
|
432
|
+
handlers.onUp?.();
|
|
433
|
+
break;
|
|
434
|
+
case 'ArrowDown':
|
|
435
|
+
event.preventDefault();
|
|
436
|
+
handlers.onDown?.();
|
|
437
|
+
break;
|
|
438
|
+
case 'ArrowLeft':
|
|
439
|
+
handlers.onLeft?.();
|
|
440
|
+
break;
|
|
441
|
+
case 'ArrowRight':
|
|
442
|
+
handlers.onRight?.();
|
|
443
|
+
break;
|
|
444
|
+
case 'Enter':
|
|
445
|
+
event.preventDefault();
|
|
446
|
+
handlers.onEnter?.();
|
|
447
|
+
break;
|
|
448
|
+
case 'Escape':
|
|
449
|
+
event.preventDefault();
|
|
450
|
+
handlers.onEscape?.();
|
|
451
|
+
break;
|
|
452
|
+
case 'Tab':
|
|
453
|
+
handlers.onTab?.(event.shiftKey);
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
};
|
|
458
|
+
// ============================================================================
|
|
459
|
+
// Local Storage (Recent Searches)
|
|
460
|
+
// ============================================================================
|
|
461
|
+
const RECENT_SEARCHES_KEY = 'seekora_recent_searches';
|
|
462
|
+
const MAX_RECENT_SEARCHES = 10;
|
|
463
|
+
export const getRecentSearches = (storeId) => {
|
|
464
|
+
try {
|
|
465
|
+
const key = storeId ? `${RECENT_SEARCHES_KEY}_${storeId}` : RECENT_SEARCHES_KEY;
|
|
466
|
+
const stored = localStorage.getItem(key);
|
|
467
|
+
return stored ? JSON.parse(stored) : [];
|
|
468
|
+
}
|
|
469
|
+
catch {
|
|
470
|
+
return [];
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
export const addRecentSearch = (query, storeId) => {
|
|
474
|
+
try {
|
|
475
|
+
const key = storeId ? `${RECENT_SEARCHES_KEY}_${storeId}` : RECENT_SEARCHES_KEY;
|
|
476
|
+
let searches = getRecentSearches(storeId);
|
|
477
|
+
searches = searches.filter(s => s !== query);
|
|
478
|
+
searches.unshift(query);
|
|
479
|
+
searches = searches.slice(0, MAX_RECENT_SEARCHES);
|
|
480
|
+
localStorage.setItem(key, JSON.stringify(searches));
|
|
481
|
+
return searches;
|
|
482
|
+
}
|
|
483
|
+
catch {
|
|
484
|
+
return [];
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
export const removeRecentSearch = (query, storeId) => {
|
|
488
|
+
try {
|
|
489
|
+
const key = storeId ? `${RECENT_SEARCHES_KEY}_${storeId}` : RECENT_SEARCHES_KEY;
|
|
490
|
+
let searches = getRecentSearches(storeId);
|
|
491
|
+
searches = searches.filter(s => s !== query);
|
|
492
|
+
localStorage.setItem(key, JSON.stringify(searches));
|
|
493
|
+
return searches;
|
|
494
|
+
}
|
|
495
|
+
catch {
|
|
496
|
+
return [];
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
export const clearRecentSearches = (storeId) => {
|
|
500
|
+
try {
|
|
501
|
+
const key = storeId ? `${RECENT_SEARCHES_KEY}_${storeId}` : RECENT_SEARCHES_KEY;
|
|
502
|
+
localStorage.removeItem(key);
|
|
503
|
+
}
|
|
504
|
+
catch {
|
|
505
|
+
// Silent fail
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
// ============================================================================
|
|
509
|
+
// Misc Utilities
|
|
510
|
+
// ============================================================================
|
|
511
|
+
/**
|
|
512
|
+
* Debounce function
|
|
513
|
+
*/
|
|
514
|
+
export const debounce = (fn, delay) => {
|
|
515
|
+
let timeoutId = null;
|
|
516
|
+
const debounced = (...args) => {
|
|
517
|
+
if (timeoutId)
|
|
518
|
+
clearTimeout(timeoutId);
|
|
519
|
+
timeoutId = setTimeout(() => fn(...args), delay);
|
|
520
|
+
};
|
|
521
|
+
debounced.cancel = () => {
|
|
522
|
+
if (timeoutId)
|
|
523
|
+
clearTimeout(timeoutId);
|
|
524
|
+
};
|
|
525
|
+
return debounced;
|
|
526
|
+
};
|
|
527
|
+
/**
|
|
528
|
+
* Generate unique ID
|
|
529
|
+
*/
|
|
530
|
+
export const generateId = (prefix = 'seekora') => {
|
|
531
|
+
return `${prefix}-${Math.random().toString(36).substring(2, 9)}`;
|
|
532
|
+
};
|
|
533
|
+
/**
|
|
534
|
+
* Check if element is in viewport
|
|
535
|
+
*/
|
|
536
|
+
export const isInViewport = (element) => {
|
|
537
|
+
const rect = element.getBoundingClientRect();
|
|
538
|
+
return (rect.top >= 0 &&
|
|
539
|
+
rect.left >= 0 &&
|
|
540
|
+
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
|
541
|
+
rect.right <= (window.innerWidth || document.documentElement.clientWidth));
|
|
542
|
+
};
|
|
543
|
+
/**
|
|
544
|
+
* Scroll element into view if needed
|
|
545
|
+
*/
|
|
546
|
+
export const scrollIntoViewIfNeeded = (element, container) => {
|
|
547
|
+
const elementRect = element.getBoundingClientRect();
|
|
548
|
+
const containerRect = container.getBoundingClientRect();
|
|
549
|
+
if (elementRect.top < containerRect.top) {
|
|
550
|
+
element.scrollIntoView({ block: 'start', behavior: 'smooth' });
|
|
551
|
+
}
|
|
552
|
+
else if (elementRect.bottom > containerRect.bottom) {
|
|
553
|
+
element.scrollIntoView({ block: 'end', behavior: 'smooth' });
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
/**
|
|
557
|
+
* Simple in-memory cache with TTL support for suggestions
|
|
558
|
+
* Reduces API calls by caching results for quick repeated queries
|
|
559
|
+
*/
|
|
560
|
+
class SuggestionsCache {
|
|
561
|
+
constructor(options = {}) {
|
|
562
|
+
this.cache = new Map();
|
|
563
|
+
this.maxSize = options.maxSize ?? 100;
|
|
564
|
+
this.defaultTtl = options.defaultTtlMs ?? 30000; // 30 seconds default
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Generate a cache key from query and options
|
|
568
|
+
*/
|
|
569
|
+
generateKey(query, options) {
|
|
570
|
+
const normalizedQuery = query.toLowerCase().trim();
|
|
571
|
+
if (!options)
|
|
572
|
+
return normalizedQuery;
|
|
573
|
+
// Create a stable key from options
|
|
574
|
+
const optionsKey = Object.keys(options)
|
|
575
|
+
.sort()
|
|
576
|
+
.filter(k => options[k] !== undefined && options[k] !== null)
|
|
577
|
+
.map(k => `${k}:${JSON.stringify(options[k])}`)
|
|
578
|
+
.join('|');
|
|
579
|
+
return optionsKey ? `${normalizedQuery}::${optionsKey}` : normalizedQuery;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Get cached data if valid (not expired)
|
|
583
|
+
*/
|
|
584
|
+
get(key) {
|
|
585
|
+
const entry = this.cache.get(key);
|
|
586
|
+
if (!entry)
|
|
587
|
+
return null;
|
|
588
|
+
const now = Date.now();
|
|
589
|
+
if (now - entry.timestamp > entry.ttl) {
|
|
590
|
+
// Expired - remove and return null
|
|
591
|
+
this.cache.delete(key);
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
return entry.data;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Store data in cache
|
|
598
|
+
*/
|
|
599
|
+
set(key, data, ttlMs) {
|
|
600
|
+
// Evict oldest entries if at max size
|
|
601
|
+
if (this.cache.size >= this.maxSize) {
|
|
602
|
+
const oldestKey = this.cache.keys().next().value;
|
|
603
|
+
if (oldestKey)
|
|
604
|
+
this.cache.delete(oldestKey);
|
|
605
|
+
}
|
|
606
|
+
this.cache.set(key, {
|
|
607
|
+
data,
|
|
608
|
+
timestamp: Date.now(),
|
|
609
|
+
ttl: ttlMs ?? this.defaultTtl,
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Check if key exists and is valid
|
|
614
|
+
*/
|
|
615
|
+
has(key) {
|
|
616
|
+
return this.get(key) !== null;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Clear all cached entries
|
|
620
|
+
*/
|
|
621
|
+
clear() {
|
|
622
|
+
this.cache.clear();
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Clear expired entries
|
|
626
|
+
*/
|
|
627
|
+
cleanup() {
|
|
628
|
+
const now = Date.now();
|
|
629
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
630
|
+
if (now - entry.timestamp > entry.ttl) {
|
|
631
|
+
this.cache.delete(key);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Get cache statistics
|
|
637
|
+
*/
|
|
638
|
+
getStats() {
|
|
639
|
+
return {
|
|
640
|
+
size: this.cache.size,
|
|
641
|
+
maxSize: this.maxSize,
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
// Global cache instance for suggestions (shared across components)
|
|
646
|
+
let globalSuggestionsCache = null;
|
|
647
|
+
/**
|
|
648
|
+
* Get the global suggestions cache instance
|
|
649
|
+
*/
|
|
650
|
+
export const getSuggestionsCache = (options) => {
|
|
651
|
+
if (!globalSuggestionsCache) {
|
|
652
|
+
globalSuggestionsCache = new SuggestionsCache(options);
|
|
653
|
+
}
|
|
654
|
+
return globalSuggestionsCache;
|
|
655
|
+
};
|
|
656
|
+
/**
|
|
657
|
+
* Create a new cache instance (for isolated caching per component)
|
|
658
|
+
*/
|
|
659
|
+
export const createSuggestionsCache = (options) => {
|
|
660
|
+
return new SuggestionsCache(options);
|
|
661
|
+
};
|
|
662
|
+
/**
|
|
663
|
+
* Clear the global cache
|
|
664
|
+
*/
|
|
665
|
+
export const clearSuggestionsCache = () => {
|
|
666
|
+
globalSuggestionsCache?.clear();
|
|
667
|
+
};
|
|
668
|
+
export { SuggestionsCache };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CategoriesTabs – horizontal tabs (e.g. filtered tabs) (primitive)
|
|
3
|
+
*
|
|
4
|
+
* Active tab from context; on select updates context and tracks analytics.
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
export interface CategoriesTabsProps {
|
|
8
|
+
className?: string;
|
|
9
|
+
style?: React.CSSProperties;
|
|
10
|
+
tabClassName?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function CategoriesTabs({ className, style, tabClassName }: CategoriesTabsProps): React.JSX.Element | null;
|
|
13
|
+
//# sourceMappingURL=CategoriesTabs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CategoriesTabs.d.ts","sourceRoot":"","sources":["../../../src/components/suggestions-primitives/CategoriesTabs.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,cAAc,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,mBAAmB,4BA+CrF"}
|