@seekora-ai/ui-sdk-react 0.2.14 → 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/CurrentRefinements.d.ts.map +1 -1
- package/dist/components/CurrentRefinements.js +69 -9
- package/dist/components/FacetDropdown.d.ts +94 -0
- package/dist/components/FacetDropdown.d.ts.map +1 -0
- package/dist/components/FacetDropdown.js +396 -0
- package/dist/components/Facets.d.ts +30 -0
- package/dist/components/Facets.d.ts.map +1 -1
- package/dist/components/Facets.js +215 -7
- package/dist/components/FederatedDropdown.d.ts.map +1 -1
- package/dist/components/FederatedDropdown.js +45 -31
- 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 +32 -33
- package/dist/components/RangeInput.d.ts.map +1 -1
- package/dist/components/RangeInput.js +6 -6
- package/dist/components/RangeSlider.d.ts.map +1 -1
- package/dist/components/RangeSlider.js +54 -32
- 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 +40 -35
- package/dist/components/SearchBar.d.ts +0 -4
- package/dist/components/SearchBar.d.ts.map +1 -1
- package/dist/components/SearchBar.js +17 -11
- package/dist/components/SearchBarWithSuggestions.js +4 -4
- package/dist/components/SearchLayout.d.ts.map +1 -1
- package/dist/components/SearchLayout.js +22 -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 +38 -39
- package/dist/components/primitives/ActionButtons.d.ts.map +1 -1
- package/dist/components/primitives/ActionButtons.js +34 -10
- package/dist/components/primitives/BadgeList.d.ts.map +1 -1
- package/dist/components/primitives/BadgeList.js +33 -13
- package/dist/components/primitives/ImageDisplay.d.ts.map +1 -1
- package/dist/components/primitives/ImageDisplay.js +32 -19
- package/dist/components/primitives/ImageZoom.d.ts.map +1 -1
- package/dist/components/primitives/ImageZoom.js +85 -30
- package/dist/components/primitives/VariantSelector.js +10 -10
- package/dist/components/primitives/VariantSwatches.d.ts.map +1 -1
- package/dist/components/primitives/VariantSwatches.js +28 -13
- package/dist/components/product-page/ProductGallery.d.ts +8 -1
- package/dist/components/product-page/ProductGallery.d.ts.map +1 -1
- package/dist/components/product-page/ProductGallery.js +2 -2
- 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/SectionSearchProvider.d.ts +3 -1
- package/dist/components/section-primitives/SectionSearchProvider.d.ts.map +1 -1
- package/dist/components/section-primitives/SectionSearchProvider.js +3 -2
- 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 +78 -78
- 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 +40 -41
- 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/DropdownPanel.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/DropdownPanel.js +15 -2
- package/dist/components/suggestions-primitives/ItemCard.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/ItemCard.js +48 -11
- package/dist/components/suggestions-primitives/ItemGrid.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/ItemGrid.js +18 -5
- package/dist/components/suggestions-primitives/ProductCard.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/ProductCard.js +36 -12
- package/dist/components/suggestions-primitives/ProductCardLayouts.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/ProductCardLayouts.js +52 -20
- 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 +29 -10
- package/dist/components/suggestions-primitives/SuggestionItem.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/SuggestionItem.js +8 -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 +10 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.umd.js +1 -1
- package/dist/src/index.d.ts +166 -81
- package/dist/src/index.esm.js +2141 -1048
- package/dist/src/index.esm.js.map +1 -1
- package/dist/src/index.js +2142 -1049
- 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 +7 -7
- package/src/docsearch/docsearch.css +2 -5
|
@@ -8,7 +8,7 @@ import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react'
|
|
|
8
8
|
import { useSearchContext } from './SearchProvider';
|
|
9
9
|
import { useSearchState } from '../hooks/useSearchState';
|
|
10
10
|
import { clsx } from 'clsx';
|
|
11
|
-
export const InfiniteHits = ({ renderHit, renderEmpty,
|
|
11
|
+
export const InfiniteHits = ({ renderHit, renderEmpty, renderShowMore, showMoreButton = true, useInfiniteScroll = false, scrollThreshold = 0.1, fieldMapping, showMoreLabel = 'Show more', onHitClick, className, style, theme: customTheme, syncWithState = true, }) => {
|
|
12
12
|
const { theme, stateManager } = useSearchContext();
|
|
13
13
|
const { results, loading, currentPage, setPage } = useSearchState();
|
|
14
14
|
const infiniteHitsTheme = customTheme || {};
|
|
@@ -140,12 +140,6 @@ export const InfiniteHits = ({ renderHit, renderEmpty, showInitialLoading = fals
|
|
|
140
140
|
textAlign: 'center',
|
|
141
141
|
color: theme.colors.textSecondary,
|
|
142
142
|
} }, "No results found"));
|
|
143
|
-
// Default loading state
|
|
144
|
-
const defaultRenderLoading = () => (React.createElement("div", { className: infiniteHitsTheme.loading, style: {
|
|
145
|
-
padding: theme.spacing.medium,
|
|
146
|
-
textAlign: 'center',
|
|
147
|
-
color: theme.colors.textSecondary,
|
|
148
|
-
} }, loadingLabel));
|
|
149
143
|
// Default "Show More" button
|
|
150
144
|
const defaultRenderShowMore = () => (React.createElement("button", { type: "button", onClick: handleShowMore, disabled: isLastPage || isLoadingMore, className: clsx(infiniteHitsTheme.loadMore, (isLastPage || isLoadingMore) && infiniteHitsTheme.loadMoreDisabled), style: {
|
|
151
145
|
display: 'block',
|
|
@@ -162,11 +156,7 @@ export const InfiniteHits = ({ renderHit, renderEmpty, showInitialLoading = fals
|
|
|
162
156
|
: theme.borderRadius.medium,
|
|
163
157
|
cursor: isLastPage || isLoadingMore ? 'not-allowed' : 'pointer',
|
|
164
158
|
transition: theme.transitions?.fast || '150ms ease-in-out',
|
|
165
|
-
} },
|
|
166
|
-
// Initial loading state (only when showInitialLoading: default no loading screen)
|
|
167
|
-
if (loading && accumulatedHits.length === 0 && showInitialLoading) {
|
|
168
|
-
return (React.createElement("div", { className: clsx(infiniteHitsTheme.root, className), style: style }, renderLoading ? renderLoading() : defaultRenderLoading()));
|
|
169
|
-
}
|
|
159
|
+
} }, isLastPage ? 'No more results' : showMoreLabel));
|
|
170
160
|
if (loading && accumulatedHits.length === 0) {
|
|
171
161
|
return React.createElement("div", { className: clsx(infiniteHitsTheme.root, className), style: style });
|
|
172
162
|
}
|
|
@@ -179,6 +169,5 @@ export const InfiniteHits = ({ renderHit, renderEmpty, showInitialLoading = fals
|
|
|
179
169
|
showMoreButton && !useInfiniteScroll && !isLastPage && (renderShowMore
|
|
180
170
|
? renderShowMore({ isLoading: isLoadingMore, isLastPage, onClick: handleShowMore })
|
|
181
171
|
: defaultRenderShowMore()),
|
|
182
|
-
isLoadingMore && (renderLoading ? renderLoading() : defaultRenderLoading()),
|
|
183
172
|
useInfiniteScroll && !isLastPage && (React.createElement("div", { ref: sentinelRef, className: infiniteHitsTheme.sentinel, style: { height: '1px', visibility: 'hidden' }, "aria-hidden": "true" }))));
|
|
184
173
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../src/components/Pagination.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../src/components/Pagination.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAG7D,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,8BAA8B;IAC9B,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAChC,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4BAA4B;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iCAAiC;IACjC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,6CAA6C;IAC7C,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,KAAK,KAAK,CAAC,SAAS,CAAC;IAC7F,uBAAuB;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,mBAAmB;IACnB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC9C,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpC;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,0DAA0D;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAmBD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA0bhD,CAAC"}
|
|
@@ -16,16 +16,30 @@ import React from 'react';
|
|
|
16
16
|
import { useSearchContext } from './SearchProvider';
|
|
17
17
|
import { useSearchState } from '../hooks/useSearchState';
|
|
18
18
|
import { clsx } from 'clsx';
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
import { useIsMobile } from '../utils/responsive';
|
|
20
|
+
/** Size-specific style tokens (responsive) */
|
|
21
|
+
const getSizeTokens = (size, isMobile) => {
|
|
22
|
+
if (isMobile) {
|
|
23
|
+
// Larger touch targets on mobile
|
|
24
|
+
return {
|
|
25
|
+
small: { paddingKey: 'small', fontSizeKey: 'small', minWidth: '40px', minHeight: '40px' },
|
|
26
|
+
medium: { paddingKey: 'small', fontSizeKey: 'medium', minWidth: '44px', minHeight: '44px' },
|
|
27
|
+
large: { paddingKey: 'medium', fontSizeKey: 'large', minWidth: '48px', minHeight: '48px' },
|
|
28
|
+
}[size];
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
small: { paddingKey: 'small', fontSizeKey: 'small', minWidth: '32px', minHeight: '32px' },
|
|
32
|
+
medium: { paddingKey: 'small', fontSizeKey: 'medium', minWidth: '40px', minHeight: '40px' },
|
|
33
|
+
large: { paddingKey: 'medium', fontSizeKey: 'large', minWidth: '48px', minHeight: '48px' },
|
|
34
|
+
}[size];
|
|
24
35
|
};
|
|
25
|
-
export 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', }) => {
|
|
36
|
+
export 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', }) => {
|
|
26
37
|
const { theme } = useSearchContext();
|
|
27
38
|
const { results: stateResults, currentPage: stateCurrentPage, setPage } = useSearchState();
|
|
28
39
|
const paginationTheme = customTheme || {};
|
|
40
|
+
const isMobile = useIsMobile();
|
|
41
|
+
// Responsive maxPages - show fewer pages on mobile
|
|
42
|
+
const maxPages = isMobile ? Math.min(maxPagesProp, 5) : maxPagesProp;
|
|
29
43
|
// Use results from prop if provided, otherwise from state manager
|
|
30
44
|
const results = resultsProp || stateResults;
|
|
31
45
|
// Use currentPage from prop if provided, otherwise from state manager
|
|
@@ -49,8 +63,8 @@ export const Pagination = ({ results: resultsProp, currentPage: currentPageProp,
|
|
|
49
63
|
const resolvedShowPageInfo = showPageInfo !== undefined
|
|
50
64
|
? showPageInfo
|
|
51
65
|
: variant === 'simple';
|
|
52
|
-
// Size tokens
|
|
53
|
-
const sizeTokens =
|
|
66
|
+
// Size tokens (responsive)
|
|
67
|
+
const sizeTokens = getSizeTokens(size, isMobile);
|
|
54
68
|
// CSS variable aware helpers — allow overrides via custom properties
|
|
55
69
|
const cssVarBg = 'var(--seekora-pagination-bg, ' + theme.colors.background + ')';
|
|
56
70
|
const cssVarColor = 'var(--seekora-pagination-color, ' + theme.colors.text + ')';
|
|
@@ -70,7 +84,7 @@ export const Pagination = ({ results: resultsProp, currentPage: currentPageProp,
|
|
|
70
84
|
};
|
|
71
85
|
const defaultRenderPageButton = (page, isActive, isDisabled) => (React.createElement("button", { type: "button", disabled: isDisabled, onClick: () => !isDisabled && handlePageChange(page), "aria-current": isActive ? 'page' : undefined, "aria-label": `Page ${page}`, className: clsx(paginationTheme.item, isActive && paginationTheme.itemActive, isDisabled && paginationTheme.itemDisabled), style: {
|
|
72
86
|
padding: theme.spacing[sizeTokens.paddingKey],
|
|
73
|
-
margin: `0 ${theme.spacing.small}`,
|
|
87
|
+
margin: isMobile ? `0 4px` : `0 ${theme.spacing.small}`,
|
|
74
88
|
border: `1px solid ${cssVarBorder}`,
|
|
75
89
|
borderRadius: cssVarRadius,
|
|
76
90
|
backgroundColor: isActive ? cssVarActiveBg : cssVarBg,
|
|
@@ -79,6 +93,10 @@ export const Pagination = ({ results: resultsProp, currentPage: currentPageProp,
|
|
|
79
93
|
opacity: isDisabled ? 0.5 : 1,
|
|
80
94
|
fontSize: theme.typography.fontSize[sizeTokens.fontSizeKey],
|
|
81
95
|
minWidth: sizeTokens.minWidth,
|
|
96
|
+
minHeight: sizeTokens.minHeight,
|
|
97
|
+
display: 'inline-flex',
|
|
98
|
+
alignItems: 'center',
|
|
99
|
+
justifyContent: 'center',
|
|
82
100
|
...(isActive && {
|
|
83
101
|
fontWeight: 'bold',
|
|
84
102
|
}),
|
|
@@ -16,7 +16,6 @@ export interface QuerySuggestionsTheme {
|
|
|
16
16
|
suggestionItem?: string;
|
|
17
17
|
suggestionItemHover?: string;
|
|
18
18
|
suggestionItemActive?: string;
|
|
19
|
-
loadingState?: string;
|
|
20
19
|
emptyState?: string;
|
|
21
20
|
}
|
|
22
21
|
export interface QuerySuggestionsProps {
|
|
@@ -26,9 +25,6 @@ export interface QuerySuggestionsProps {
|
|
|
26
25
|
minQueryLength?: number;
|
|
27
26
|
onSuggestionClick?: (suggestion: string) => void;
|
|
28
27
|
renderSuggestion?: (suggestion: SuggestionItem, index: number) => React.ReactNode;
|
|
29
|
-
/** Show loading state when fetching and no previous suggestions (default false: show previous results until new render) */
|
|
30
|
-
showLoadingState?: boolean;
|
|
31
|
-
renderLoading?: () => React.ReactNode;
|
|
32
28
|
renderEmpty?: () => React.ReactNode;
|
|
33
29
|
showTitle?: boolean;
|
|
34
30
|
title?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QuerySuggestions.d.ts","sourceRoot":"","sources":["../../src/components/QuerySuggestions.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAmB,MAAM,OAAO,CAAC;AAMxC,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,
|
|
1
|
+
{"version":3,"file":"QuerySuggestions.d.ts","sourceRoot":"","sources":["../../src/components/QuerySuggestions.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAmB,MAAM,OAAO,CAAC;AAMxC,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAC;IAClF,WAAW,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,KAAK,CAAC,EAAE,qBAAqB,CAAC;CAC/B;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAsI5D,CAAC"}
|
|
@@ -7,7 +7,7 @@ import React, { useState } from 'react';
|
|
|
7
7
|
import { useSearchContext } from './SearchProvider';
|
|
8
8
|
import { useQuerySuggestions } from '../hooks/useQuerySuggestions';
|
|
9
9
|
import { clsx } from 'clsx';
|
|
10
|
-
export const QuerySuggestions = ({ query = '', maxSuggestions = 10, debounceMs = 300, minQueryLength = 2, onSuggestionClick, renderSuggestion,
|
|
10
|
+
export const QuerySuggestions = ({ query = '', maxSuggestions = 10, debounceMs = 300, minQueryLength = 2, onSuggestionClick, renderSuggestion, renderEmpty, showTitle = false, title = 'Suggestions', className, style, theme: customTheme, }) => {
|
|
11
11
|
const { client, theme } = useSearchContext();
|
|
12
12
|
const [selectedIndex, setSelectedIndex] = useState(-1);
|
|
13
13
|
const { suggestions, loading, error } = useQuerySuggestions({
|
|
@@ -25,11 +25,6 @@ export const QuerySuggestions = ({ query = '', maxSuggestions = 10, debounceMs =
|
|
|
25
25
|
"(",
|
|
26
26
|
suggestion.count,
|
|
27
27
|
")"))));
|
|
28
|
-
const defaultRenderLoading = () => (React.createElement("div", { style: {
|
|
29
|
-
padding: theme.spacing.medium,
|
|
30
|
-
textAlign: 'center',
|
|
31
|
-
color: theme.colors.text,
|
|
32
|
-
} }, "Loading suggestions..."));
|
|
33
28
|
const defaultRenderEmpty = () => (React.createElement("div", { style: {
|
|
34
29
|
padding: theme.spacing.medium,
|
|
35
30
|
textAlign: 'center',
|
|
@@ -44,17 +39,6 @@ export const QuerySuggestions = ({ query = '', maxSuggestions = 10, debounceMs =
|
|
|
44
39
|
if (query.length < minQueryLength) {
|
|
45
40
|
return null;
|
|
46
41
|
}
|
|
47
|
-
// When loading with no previous results, show loading only if showLoadingState (default: show previous results, no loading screen)
|
|
48
|
-
if (loading && displayedSuggestions.length === 0 && showLoadingState) {
|
|
49
|
-
return (React.createElement("div", { className: clsx(suggestionsTheme.container, className), style: style },
|
|
50
|
-
showTitle && (React.createElement("div", { className: suggestionsTheme.title, style: {
|
|
51
|
-
fontSize: theme.typography.fontSize.large,
|
|
52
|
-
fontWeight: 'bold',
|
|
53
|
-
marginBottom: theme.spacing.medium,
|
|
54
|
-
color: theme.colors.text,
|
|
55
|
-
} }, title)),
|
|
56
|
-
renderLoading ? renderLoading() : defaultRenderLoading()));
|
|
57
|
-
}
|
|
58
42
|
if (error || (!loading && displayedSuggestions.length === 0)) {
|
|
59
43
|
return (React.createElement("div", { className: clsx(suggestionsTheme.container, className), style: style },
|
|
60
44
|
showTitle && (React.createElement("div", { className: suggestionsTheme.title, style: {
|
|
@@ -28,8 +28,6 @@ export interface QuerySuggestionsDropdownProps extends QuerySuggestionsEventHand
|
|
|
28
28
|
maxRecentSearches?: number;
|
|
29
29
|
/** Show suggestion counts */
|
|
30
30
|
showCounts?: boolean;
|
|
31
|
-
/** Show loading state (default false: show previous results until new results render) */
|
|
32
|
-
showLoading?: boolean;
|
|
33
31
|
/** Show empty state when no results */
|
|
34
32
|
showEmptyState?: boolean;
|
|
35
33
|
/** Highlight configuration */
|
|
@@ -46,8 +44,6 @@ export interface QuerySuggestionsDropdownProps extends QuerySuggestionsEventHand
|
|
|
46
44
|
renderSuggestion?: (suggestion: SuggestionItem, index: number, isActive: boolean, highlight: (text: string) => React.ReactNode) => React.ReactNode;
|
|
47
45
|
/** Custom render for recent search item */
|
|
48
46
|
renderRecentSearch?: (search: RecentSearch, index: number, isActive: boolean) => React.ReactNode;
|
|
49
|
-
/** Custom render for loading state */
|
|
50
|
-
renderLoading?: () => React.ReactNode;
|
|
51
47
|
/** Custom render for empty state */
|
|
52
48
|
renderEmpty?: () => React.ReactNode;
|
|
53
49
|
/** Custom footer content */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QuerySuggestionsDropdown.d.ts","sourceRoot":"","sources":["../../src/components/QuerySuggestionsDropdown.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAQN,MAAM,OAAO,CAAC;AAIf,OAAO,KAAK,EACV,cAAc,EACd,YAAY,EACZ,0BAA0B,EAC1B,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,6BAA6B,EAC9B,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"QuerySuggestionsDropdown.d.ts","sourceRoot":"","sources":["../../src/components/QuerySuggestionsDropdown.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAQN,MAAM,OAAO,CAAC;AAIf,OAAO,KAAK,EACV,cAAc,EACd,YAAY,EACZ,0BAA0B,EAC1B,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,6BAA6B,EAC9B,MAAM,0BAA0B,CAAC;AAuBlC,MAAM,WAAW,6BAA8B,SAAQ,6BAA6B;IAClF,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qCAAqC;IACrC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2BAA2B;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,yCAAyC;IACzC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6BAA6B;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,uCAAuC;IACvC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,8BAA8B;IAC9B,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,wCAAwC;IACxC,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,8BAA8B;IAC9B,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,yBAAyB;IACzB,UAAU,CAAC,EAAE,0BAA0B,CAAC;IACxC,wBAAwB;IACxB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,wCAAwC;IACxC,gBAAgB,CAAC,EAAE,CACjB,UAAU,EAAE,cAAc,EAC1B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,OAAO,EACjB,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,KACzC,KAAK,CAAC,SAAS,CAAC;IACrB,2CAA2C;IAC3C,kBAAkB,CAAC,EAAE,CACnB,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,OAAO,KACd,KAAK,CAAC,SAAS,CAAC;IACrB,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;IACpC,4BAA4B;IAC5B,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC;IAChC,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAC7C,6BAA6B;IAC7B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,0BAA0B;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iBAAiB;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,2BAA2B;IAC1C,uCAAuC;IACvC,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,4BAA4B;IAC5B,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,gCAAgC;IAChC,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,sBAAsB;IACtB,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,uBAAuB;IACvB,cAAc,EAAE,MAAM,MAAM,CAAC;IAC7B,4BAA4B;IAC5B,aAAa,EAAE,MAAM,MAAM,CAAC;CAC7B;AAsJD,eAAO,MAAM,wBAAwB,mHAqZnC,CAAC;AAEH,eAAe,wBAAwB,CAAC"}
|
|
@@ -14,13 +14,27 @@ import { useSearchContext } from './SearchProvider';
|
|
|
14
14
|
import { useQuerySuggestionsEnhanced } from '../hooks/useQuerySuggestionsEnhanced';
|
|
15
15
|
import { clsx } from 'clsx';
|
|
16
16
|
// ============================================================================
|
|
17
|
+
// Constants
|
|
18
|
+
// ============================================================================
|
|
19
|
+
const TRANSITIONS = {
|
|
20
|
+
fast: '150ms ease-in-out',
|
|
21
|
+
normal: '200ms ease-in-out',
|
|
22
|
+
slow: '300ms ease-in-out',
|
|
23
|
+
};
|
|
24
|
+
const BORDER_RADIUS = {
|
|
25
|
+
sm: 4,
|
|
26
|
+
md: 6,
|
|
27
|
+
lg: 8,
|
|
28
|
+
full: 9999,
|
|
29
|
+
};
|
|
30
|
+
// ============================================================================
|
|
17
31
|
// Styles
|
|
18
32
|
// ============================================================================
|
|
19
33
|
const defaultStyles = {
|
|
20
34
|
container: {
|
|
21
|
-
backgroundColor: 'var(--seekora-bg-surface,
|
|
22
|
-
border: '1px solid var(--seekora-border-color,
|
|
23
|
-
borderRadius:
|
|
35
|
+
backgroundColor: 'var(--seekora-bg-surface, transparent)',
|
|
36
|
+
border: '1px solid var(--seekora-border-color, rgba(128,128,128,0.2))',
|
|
37
|
+
borderRadius: `var(--seekora-border-radius, ${BORDER_RADIUS.lg}px)`,
|
|
24
38
|
boxShadow: 'var(--seekora-shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05))',
|
|
25
39
|
maxHeight: '400px',
|
|
26
40
|
overflowY: 'auto',
|
|
@@ -32,7 +46,7 @@ const defaultStyles = {
|
|
|
32
46
|
sectionTitle: {
|
|
33
47
|
fontSize: '12px',
|
|
34
48
|
fontWeight: 600,
|
|
35
|
-
color: 'var(--seekora-text-secondary,
|
|
49
|
+
color: 'var(--seekora-text-secondary, inherit)',
|
|
36
50
|
textTransform: 'uppercase',
|
|
37
51
|
letterSpacing: '0.05em',
|
|
38
52
|
padding: '8px 16px 4px',
|
|
@@ -43,9 +57,9 @@ const defaultStyles = {
|
|
|
43
57
|
alignItems: 'center',
|
|
44
58
|
padding: '10px 16px',
|
|
45
59
|
cursor: 'pointer',
|
|
46
|
-
transition:
|
|
60
|
+
transition: `background-color ${TRANSITIONS.fast}`,
|
|
47
61
|
fontSize: '14px',
|
|
48
|
-
color: 'var(--seekora-text-primary,
|
|
62
|
+
color: 'var(--seekora-text-primary, inherit)',
|
|
49
63
|
gap: '12px',
|
|
50
64
|
},
|
|
51
65
|
suggestionItemActive: {
|
|
@@ -65,10 +79,10 @@ const defaultStyles = {
|
|
|
65
79
|
},
|
|
66
80
|
highlight: {
|
|
67
81
|
fontWeight: 600,
|
|
68
|
-
color: 'var(--seekora-text-primary,
|
|
82
|
+
color: 'var(--seekora-text-primary, inherit)',
|
|
69
83
|
backgroundColor: 'var(--seekora-highlight-bg, #fef3c7)',
|
|
70
84
|
padding: '0 2px',
|
|
71
|
-
borderRadius:
|
|
85
|
+
borderRadius: `${BORDER_RADIUS.sm / 2}px`,
|
|
72
86
|
},
|
|
73
87
|
recentIcon: {
|
|
74
88
|
width: '16px',
|
|
@@ -78,46 +92,37 @@ const defaultStyles = {
|
|
|
78
92
|
},
|
|
79
93
|
removeButton: {
|
|
80
94
|
padding: '4px',
|
|
81
|
-
borderRadius:
|
|
95
|
+
borderRadius: `${BORDER_RADIUS.sm}px`,
|
|
82
96
|
border: 'none',
|
|
83
97
|
background: 'transparent',
|
|
84
98
|
cursor: 'pointer',
|
|
85
99
|
color: 'var(--seekora-text-secondary, #9ca3af)',
|
|
86
100
|
opacity: 0,
|
|
87
|
-
transition:
|
|
101
|
+
transition: `opacity ${TRANSITIONS.fast}, color ${TRANSITIONS.fast}`,
|
|
88
102
|
},
|
|
89
103
|
removeButtonVisible: {
|
|
90
104
|
opacity: 1,
|
|
91
105
|
},
|
|
92
|
-
loadingState: {
|
|
93
|
-
display: 'flex',
|
|
94
|
-
alignItems: 'center',
|
|
95
|
-
justifyContent: 'center',
|
|
96
|
-
padding: '24px 16px',
|
|
97
|
-
color: 'var(--seekora-text-secondary, #6b7280)',
|
|
98
|
-
fontSize: '14px',
|
|
99
|
-
gap: '8px',
|
|
100
|
-
},
|
|
101
106
|
emptyState: {
|
|
102
107
|
display: 'flex',
|
|
103
108
|
flexDirection: 'column',
|
|
104
109
|
alignItems: 'center',
|
|
105
110
|
justifyContent: 'center',
|
|
106
111
|
padding: '24px 16px',
|
|
107
|
-
color: 'var(--seekora-text-secondary,
|
|
112
|
+
color: 'var(--seekora-text-secondary, inherit)',
|
|
108
113
|
fontSize: '14px',
|
|
109
114
|
textAlign: 'center',
|
|
110
115
|
},
|
|
111
116
|
divider: {
|
|
112
117
|
height: '1px',
|
|
113
|
-
backgroundColor: 'var(--seekora-border-color,
|
|
118
|
+
backgroundColor: 'var(--seekora-border-color, rgba(128,128,128,0.2))',
|
|
114
119
|
margin: '4px 0',
|
|
115
120
|
},
|
|
116
121
|
footer: {
|
|
117
|
-
borderTop: '1px solid var(--seekora-border-color,
|
|
122
|
+
borderTop: '1px solid var(--seekora-border-color, rgba(128,128,128,0.2))',
|
|
118
123
|
padding: '8px 16px',
|
|
119
124
|
fontSize: '12px',
|
|
120
|
-
color: 'var(--seekora-text-secondary,
|
|
125
|
+
color: 'var(--seekora-text-secondary, inherit)',
|
|
121
126
|
display: 'flex',
|
|
122
127
|
alignItems: 'center',
|
|
123
128
|
justifyContent: 'space-between',
|
|
@@ -135,9 +140,9 @@ const defaultStyles = {
|
|
|
135
140
|
minWidth: '20px',
|
|
136
141
|
height: '18px',
|
|
137
142
|
padding: '0 4px',
|
|
138
|
-
borderRadius:
|
|
139
|
-
backgroundColor: 'var(--seekora-bg-secondary,
|
|
140
|
-
border: '1px solid var(--seekora-border-color,
|
|
143
|
+
borderRadius: `${BORDER_RADIUS.sm - 1}px`,
|
|
144
|
+
backgroundColor: 'var(--seekora-bg-secondary, rgba(255, 255, 255, 0.1))',
|
|
145
|
+
border: '1px solid var(--seekora-border-color, rgba(128,128,128,0.2))',
|
|
141
146
|
fontSize: '10px',
|
|
142
147
|
fontWeight: 500,
|
|
143
148
|
},
|
|
@@ -151,14 +156,11 @@ const ClockIcon = ({ className, style }) => (React.createElement("svg", { classN
|
|
|
151
156
|
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" })));
|
|
152
157
|
const CloseIcon = ({ className, style }) => (React.createElement("svg", { className: className, style: style, viewBox: "0 0 20 20", fill: "currentColor", width: "16", height: "16" },
|
|
153
158
|
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" })));
|
|
154
|
-
const LoadingSpinner = ({ style }) => (React.createElement("svg", { style: { animation: 'spin 1s linear infinite', ...style }, viewBox: "0 0 24 24", width: "20", height: "20" },
|
|
155
|
-
React.createElement("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "3", fill: "none", opacity: "0.25" }),
|
|
156
|
-
React.createElement("path", { fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" })));
|
|
157
159
|
// ============================================================================
|
|
158
160
|
// Component
|
|
159
161
|
// ============================================================================
|
|
160
162
|
export const QuerySuggestionsDropdown = forwardRef(function QuerySuggestionsDropdown(props, ref) {
|
|
161
|
-
const { query, isOpen = true, maxSuggestions = 8, minQueryLength = 1, debounceMs = 200, showRecentSearches = true, maxRecentSearches = 5, showCounts = true,
|
|
163
|
+
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;
|
|
162
164
|
const { client, theme } = useSearchContext();
|
|
163
165
|
const containerRef = useRef(null);
|
|
164
166
|
const [activeIndex, setActiveIndex] = useState(-1);
|
|
@@ -340,9 +342,6 @@ export const QuerySuggestionsDropdown = forwardRef(function QuerySuggestionsDrop
|
|
|
340
342
|
...animationStyle,
|
|
341
343
|
...style,
|
|
342
344
|
} },
|
|
343
|
-
loading && showLoading && (React.createElement("div", { className: classNames.loadingState, style: defaultStyles.loadingState }, renderLoading ? renderLoading() : (React.createElement(React.Fragment, null,
|
|
344
|
-
React.createElement(LoadingSpinner, null),
|
|
345
|
-
React.createElement("span", null, "Searching..."))))),
|
|
346
345
|
showRecent && (React.createElement("div", { className: clsx('seekora-suggestions-section', classNames.section, classNames.recentSearches) },
|
|
347
346
|
React.createElement("div", { className: classNames.sectionTitle, style: defaultStyles.sectionTitle }, "Recent Searches"),
|
|
348
347
|
recentSearches.slice(0, maxRecentSearches).map((search, index) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RangeInput.d.ts","sourceRoot":"","sources":["../../src/components/RangeInput.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"RangeInput.d.ts","sourceRoot":"","sources":["../../src/components/RangeInput.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAA2C,MAAM,OAAO,CAAC;AAKhE,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAC3E,6BAA6B;IAC7B,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE;QACzB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;QACxB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;QACxB,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;QACjD,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;QACjD,OAAO,EAAE,MAAM,IAAI,CAAC;KACrB,KAAK,KAAK,CAAC,SAAS,CAAC;IACtB,uBAAuB;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,mBAAmB;IACnB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,wBAAwB;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kCAAkC;IAClC,WAAW,CAAC,EAAE;QACZ,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;IACF,8DAA8D;IAC9D,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AA8BD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAuQhD,CAAC"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Displays a range input for filtering numeric values (e.g., price range)
|
|
5
5
|
* Integrates with SearchStateManager for automatic state sync
|
|
6
6
|
*/
|
|
7
|
-
import React, { useState, useCallback, useEffect
|
|
7
|
+
import React, { useState, useCallback, useEffect } from 'react';
|
|
8
8
|
import { useSearchContext } from './SearchProvider';
|
|
9
9
|
import { useSearchState } from '../hooks/useSearchState';
|
|
10
10
|
import { clsx } from 'clsx';
|
|
@@ -35,11 +35,11 @@ export const RangeInput = ({ field, label, min, max, currentMin: currentMinProp,
|
|
|
35
35
|
const { refinements, addRefinement, removeRefinement } = useSearchState();
|
|
36
36
|
const rangeInputTheme = customTheme || {};
|
|
37
37
|
// Parse current range from StateManager
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
// NOTE: computed every render (no useMemo) because the state manager mutates
|
|
39
|
+
// the refinements array in place — the reference never changes.
|
|
40
|
+
const stateRange = !syncWithState
|
|
41
|
+
? { min: undefined, max: undefined }
|
|
42
|
+
: parseRangeFromRefinements(refinements, field);
|
|
43
43
|
const [internalMin, setInternalMin] = useState(currentMinProp ?? stateRange.min);
|
|
44
44
|
const [internalMax, setInternalMax] = useState(currentMaxProp ?? stateRange.max);
|
|
45
45
|
const [appliedMin, setAppliedMin] = useState(currentMinProp ?? stateRange.min);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RangeSlider.d.ts","sourceRoot":"","sources":["../../src/components/RangeSlider.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"RangeSlider.d.ts","sourceRoot":"","sources":["../../src/components/RangeSlider.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAYxE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,oBAAoB;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,8BAA8B;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4BAA4B;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,8DAA8D;IAC9D,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC,uBAAuB;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,mBAAmB;IACnB,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAsVlD,CAAC"}
|
|
@@ -4,18 +4,29 @@
|
|
|
4
4
|
* Visual slider for numeric range filtering
|
|
5
5
|
* Alternative to RangeInput for a more interactive UX
|
|
6
6
|
*/
|
|
7
|
-
import React, { useState, useCallback, useEffect,
|
|
7
|
+
import React, { useState, useCallback, useEffect, useRef } from 'react';
|
|
8
8
|
import { useSearchContext } from './SearchProvider';
|
|
9
9
|
import { useSearchState } from '../hooks/useSearchState';
|
|
10
10
|
import { clsx } from 'clsx';
|
|
11
|
+
const SHADOWS = {
|
|
12
|
+
sm: '0 1px 2px rgba(0,0,0,0.05)',
|
|
13
|
+
md: '0 2px 4px rgba(0,0,0,0.1)',
|
|
14
|
+
lg: '0 4px 6px rgba(0,0,0,0.1)',
|
|
15
|
+
xl: '0 10px 15px rgba(0,0,0,0.1)',
|
|
16
|
+
};
|
|
11
17
|
export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: currentMinProp, currentMax: currentMaxProp, onRangeChange, formatValue = (v) => v.toString(), className, style, theme: customTheme, showValues = true, syncWithState = true, debounceMs = 300, }) => {
|
|
12
18
|
const { theme } = useSearchContext();
|
|
13
19
|
const { refinements, addRefinement, removeRefinement } = useSearchState();
|
|
14
20
|
const rangeSliderTheme = customTheme || {};
|
|
21
|
+
const thumbClass = rangeSliderTheme.thumb || 'seekora-range-slider__thumb';
|
|
15
22
|
// Parse current range from StateManager
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
// NOTE: computed every render (no useMemo) because the state manager mutates
|
|
24
|
+
// the refinements array in place — the reference never changes.
|
|
25
|
+
let stateRange;
|
|
26
|
+
if (!syncWithState) {
|
|
27
|
+
stateRange = { min: undefined, max: undefined };
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
19
30
|
let minVal;
|
|
20
31
|
let maxVal;
|
|
21
32
|
refinements.forEach(r => {
|
|
@@ -28,15 +39,17 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
28
39
|
maxVal = parseFloat(maxMatch[1]);
|
|
29
40
|
}
|
|
30
41
|
});
|
|
31
|
-
|
|
32
|
-
}
|
|
42
|
+
stateRange = { min: minVal, max: maxVal };
|
|
43
|
+
}
|
|
33
44
|
const [internalMin, setInternalMin] = useState(currentMinProp ?? stateRange.min ?? min);
|
|
34
45
|
const [internalMax, setInternalMax] = useState(currentMaxProp ?? stateRange.max ?? max);
|
|
35
|
-
const
|
|
46
|
+
const isDraggingRef = useRef(false);
|
|
36
47
|
const debounceRef = useRef(null);
|
|
37
|
-
|
|
48
|
+
const pendingMinRef = useRef(internalMin);
|
|
49
|
+
const pendingMaxRef = useRef(internalMax);
|
|
50
|
+
// Sync with StateManager changes (only when stateRange actually changes, not on drag)
|
|
38
51
|
useEffect(() => {
|
|
39
|
-
if (syncWithState && !
|
|
52
|
+
if (syncWithState && !isDraggingRef.current) {
|
|
40
53
|
if (stateRange.min !== undefined)
|
|
41
54
|
setInternalMin(stateRange.min);
|
|
42
55
|
else
|
|
@@ -46,7 +59,7 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
46
59
|
else
|
|
47
60
|
setInternalMax(max);
|
|
48
61
|
}
|
|
49
|
-
}, [syncWithState, stateRange.min, stateRange.max,
|
|
62
|
+
}, [syncWithState, stateRange.min, stateRange.max, min, max]);
|
|
50
63
|
// Update StateManager with range refinements
|
|
51
64
|
const updateStateManager = useCallback((minVal, maxVal) => {
|
|
52
65
|
if (!syncWithState)
|
|
@@ -57,24 +70,24 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
57
70
|
removeRefinement(field, r.value, false);
|
|
58
71
|
}
|
|
59
72
|
});
|
|
60
|
-
// Add new range refinements
|
|
61
|
-
|
|
73
|
+
// Add new range refinements — trigger search on the last one added
|
|
74
|
+
const setMin = minVal > min;
|
|
75
|
+
const setMax = maxVal < max;
|
|
76
|
+
if (setMin && setMax) {
|
|
62
77
|
addRefinement(field, `>=${minVal}`, false);
|
|
78
|
+
addRefinement(field, `<=${maxVal}`, true);
|
|
63
79
|
}
|
|
64
|
-
if (
|
|
65
|
-
addRefinement(field, `<=${maxVal}`, minVal <= min); // Trigger search if only max is set
|
|
66
|
-
}
|
|
67
|
-
if (minVal > min && maxVal >= max) {
|
|
68
|
-
// Trigger search after setting min
|
|
80
|
+
else if (setMin) {
|
|
69
81
|
addRefinement(field, `>=${minVal}`, true);
|
|
70
82
|
}
|
|
71
|
-
else if (
|
|
72
|
-
|
|
73
|
-
// already triggered above
|
|
83
|
+
else if (setMax) {
|
|
84
|
+
addRefinement(field, `<=${maxVal}`, true);
|
|
74
85
|
}
|
|
75
86
|
}, [syncWithState, field, refinements, addRefinement, removeRefinement, min, max]);
|
|
76
|
-
// Debounced update
|
|
87
|
+
// Debounced update (during drag only)
|
|
77
88
|
const debouncedUpdate = useCallback((minVal, maxVal) => {
|
|
89
|
+
pendingMinRef.current = minVal;
|
|
90
|
+
pendingMaxRef.current = maxVal;
|
|
78
91
|
if (debounceRef.current) {
|
|
79
92
|
clearTimeout(debounceRef.current);
|
|
80
93
|
}
|
|
@@ -89,19 +102,28 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
89
102
|
const handleMinChange = (e) => {
|
|
90
103
|
const value = Math.min(Number(e.target.value), internalMax - step);
|
|
91
104
|
setInternalMin(value);
|
|
92
|
-
|
|
105
|
+
isDraggingRef.current = true;
|
|
93
106
|
debouncedUpdate(value, internalMax);
|
|
94
107
|
};
|
|
95
108
|
// Handle max slider change
|
|
96
109
|
const handleMaxChange = (e) => {
|
|
97
110
|
const value = Math.max(Number(e.target.value), internalMin + step);
|
|
98
111
|
setInternalMax(value);
|
|
99
|
-
|
|
112
|
+
isDraggingRef.current = true;
|
|
100
113
|
debouncedUpdate(internalMin, value);
|
|
101
114
|
};
|
|
102
|
-
// Handle drag end
|
|
115
|
+
// Handle drag end — flush pending update immediately
|
|
103
116
|
const handleDragEnd = () => {
|
|
104
|
-
|
|
117
|
+
isDraggingRef.current = false;
|
|
118
|
+
// Cancel the debounce and commit immediately
|
|
119
|
+
if (debounceRef.current) {
|
|
120
|
+
clearTimeout(debounceRef.current);
|
|
121
|
+
debounceRef.current = null;
|
|
122
|
+
}
|
|
123
|
+
updateStateManager(pendingMinRef.current, pendingMaxRef.current);
|
|
124
|
+
if (onRangeChange) {
|
|
125
|
+
onRangeChange(pendingMinRef.current, pendingMaxRef.current);
|
|
126
|
+
}
|
|
105
127
|
};
|
|
106
128
|
// Handle keyboard navigation for enhanced control (Shift+Arrow for 10x step, Home/End)
|
|
107
129
|
const handleMinKeyDown = (e) => {
|
|
@@ -166,7 +188,7 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
166
188
|
} }, label)),
|
|
167
189
|
React.createElement("div", { className: rangeSliderTheme.slider, style: {
|
|
168
190
|
position: 'relative',
|
|
169
|
-
|
|
191
|
+
minHeight: '40px',
|
|
170
192
|
display: 'flex',
|
|
171
193
|
alignItems: 'center',
|
|
172
194
|
} },
|
|
@@ -185,7 +207,7 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
185
207
|
backgroundColor: theme.colors.primary,
|
|
186
208
|
borderRadius: '2px',
|
|
187
209
|
} }),
|
|
188
|
-
React.createElement("input", { type: "range", min: min, max: max, step: step, value: internalMin, onChange: handleMinChange, onMouseUp: handleDragEnd, onTouchEnd: handleDragEnd, onKeyDown: handleMinKeyDown, tabIndex: 0, "aria-valuenow": internalMin, "aria-valuemin": min, "aria-valuemax": max, className:
|
|
210
|
+
React.createElement("input", { type: "range", min: min, max: max, step: step, value: internalMin, onChange: handleMinChange, onMouseUp: handleDragEnd, onTouchEnd: handleDragEnd, onKeyDown: handleMinKeyDown, tabIndex: 0, "aria-valuenow": internalMin, "aria-valuemin": min, "aria-valuemax": max, className: thumbClass, style: {
|
|
189
211
|
position: 'absolute',
|
|
190
212
|
width: '100%',
|
|
191
213
|
height: '4px',
|
|
@@ -195,7 +217,7 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
195
217
|
cursor: 'pointer',
|
|
196
218
|
pointerEvents: 'none',
|
|
197
219
|
}, "aria-label": `Minimum ${label || field}` }),
|
|
198
|
-
React.createElement("input", { type: "range", min: min, max: max, step: step, value: internalMax, onChange: handleMaxChange, onMouseUp: handleDragEnd, onTouchEnd: handleDragEnd, onKeyDown: handleMaxKeyDown, tabIndex: 0, "aria-valuenow": internalMax, "aria-valuemin": min, "aria-valuemax": max, className:
|
|
220
|
+
React.createElement("input", { type: "range", min: min, max: max, step: step, value: internalMax, onChange: handleMaxChange, onMouseUp: handleDragEnd, onTouchEnd: handleDragEnd, onKeyDown: handleMaxKeyDown, tabIndex: 0, "aria-valuenow": internalMax, "aria-valuemin": min, "aria-valuemax": max, className: thumbClass, style: {
|
|
199
221
|
position: 'absolute',
|
|
200
222
|
width: '100%',
|
|
201
223
|
height: '4px',
|
|
@@ -215,7 +237,7 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
215
237
|
React.createElement("span", { className: rangeSliderTheme.value }, formatValue(internalMin)),
|
|
216
238
|
React.createElement("span", { className: rangeSliderTheme.value }, formatValue(internalMax)))),
|
|
217
239
|
React.createElement("style", null, `
|
|
218
|
-
.${
|
|
240
|
+
.${thumbClass}::-webkit-slider-thumb {
|
|
219
241
|
-webkit-appearance: none;
|
|
220
242
|
appearance: none;
|
|
221
243
|
width: 20px;
|
|
@@ -224,9 +246,9 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
224
246
|
border-radius: 50%;
|
|
225
247
|
cursor: pointer;
|
|
226
248
|
pointer-events: all;
|
|
227
|
-
box-shadow:
|
|
249
|
+
box-shadow: ${SHADOWS.md};
|
|
228
250
|
}
|
|
229
|
-
.${
|
|
251
|
+
.${thumbClass}::-moz-range-thumb {
|
|
230
252
|
width: 20px;
|
|
231
253
|
height: 20px;
|
|
232
254
|
background: ${theme.colors.primary};
|
|
@@ -234,7 +256,7 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
234
256
|
cursor: pointer;
|
|
235
257
|
pointer-events: all;
|
|
236
258
|
border: none;
|
|
237
|
-
box-shadow:
|
|
259
|
+
box-shadow: ${SHADOWS.md};
|
|
238
260
|
}
|
|
239
261
|
`)));
|
|
240
262
|
};
|