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