@seekora-ai/ui-sdk-react 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/dist/components/Breadcrumb.d.ts +43 -0
  2. package/dist/components/Breadcrumb.d.ts.map +1 -0
  3. package/dist/components/Breadcrumb.js +119 -0
  4. package/dist/components/ClearRefinements.d.ts +42 -0
  5. package/dist/components/ClearRefinements.d.ts.map +1 -0
  6. package/dist/components/ClearRefinements.js +80 -0
  7. package/dist/components/CurrentRefinements.d.ts +41 -0
  8. package/dist/components/CurrentRefinements.d.ts.map +1 -0
  9. package/dist/components/CurrentRefinements.js +83 -0
  10. package/dist/components/Facets.d.ts +53 -0
  11. package/dist/components/Facets.d.ts.map +1 -0
  12. package/dist/components/Facets.js +195 -0
  13. package/dist/components/FederatedDropdown.d.ts +92 -0
  14. package/dist/components/FederatedDropdown.d.ts.map +1 -0
  15. package/dist/components/FederatedDropdown.js +510 -0
  16. package/dist/components/HierarchicalMenu.d.ts +55 -0
  17. package/dist/components/HierarchicalMenu.d.ts.map +1 -0
  18. package/dist/components/HierarchicalMenu.js +168 -0
  19. package/dist/components/Highlight.d.ts +51 -0
  20. package/dist/components/Highlight.d.ts.map +1 -0
  21. package/dist/components/Highlight.js +155 -0
  22. package/dist/components/HitsPerPage.d.ts +41 -0
  23. package/dist/components/HitsPerPage.d.ts.map +1 -0
  24. package/dist/components/HitsPerPage.js +72 -0
  25. package/dist/components/InfiniteHits.d.ts +56 -0
  26. package/dist/components/InfiniteHits.d.ts.map +1 -0
  27. package/dist/components/InfiniteHits.js +181 -0
  28. package/dist/components/MobileFilters.d.ts +71 -0
  29. package/dist/components/MobileFilters.d.ts.map +1 -0
  30. package/dist/components/MobileFilters.js +242 -0
  31. package/dist/components/Pagination.d.ts +44 -0
  32. package/dist/components/Pagination.d.ts.map +1 -0
  33. package/dist/components/Pagination.js +142 -0
  34. package/dist/components/QuerySuggestions.d.ts +38 -0
  35. package/dist/components/QuerySuggestions.d.ts.map +1 -0
  36. package/dist/components/QuerySuggestions.js +86 -0
  37. package/dist/components/QuerySuggestionsDropdown.d.ts +86 -0
  38. package/dist/components/QuerySuggestionsDropdown.d.ts.map +1 -0
  39. package/dist/components/QuerySuggestionsDropdown.js +395 -0
  40. package/dist/components/RangeInput.d.ts +58 -0
  41. package/dist/components/RangeInput.d.ts.map +1 -0
  42. package/dist/components/RangeInput.js +203 -0
  43. package/dist/components/RangeSlider.d.ts +51 -0
  44. package/dist/components/RangeSlider.d.ts.map +1 -0
  45. package/dist/components/RangeSlider.js +193 -0
  46. package/dist/components/Recommendations.d.ts +90 -0
  47. package/dist/components/Recommendations.d.ts.map +1 -0
  48. package/dist/components/Recommendations.js +270 -0
  49. package/dist/components/RichQuerySuggestions.d.ts +77 -0
  50. package/dist/components/RichQuerySuggestions.d.ts.map +1 -0
  51. package/dist/components/RichQuerySuggestions.js +492 -0
  52. package/dist/components/SearchBar.d.ts +40 -0
  53. package/dist/components/SearchBar.d.ts.map +1 -0
  54. package/dist/components/SearchBar.js +217 -0
  55. package/dist/components/SearchBarWithSuggestions.d.ts +99 -0
  56. package/dist/components/SearchBarWithSuggestions.d.ts.map +1 -0
  57. package/dist/components/SearchBarWithSuggestions.js +275 -0
  58. package/dist/components/SearchLayout.d.ts +35 -0
  59. package/dist/components/SearchLayout.d.ts.map +1 -0
  60. package/dist/components/SearchLayout.js +56 -0
  61. package/dist/components/SearchProvider.d.ts +28 -0
  62. package/dist/components/SearchProvider.d.ts.map +1 -0
  63. package/dist/components/SearchProvider.js +43 -0
  64. package/dist/components/SearchResults.d.ts +51 -0
  65. package/dist/components/SearchResults.d.ts.map +1 -0
  66. package/dist/components/SearchResults.js +485 -0
  67. package/dist/components/SortBy.d.ts +44 -0
  68. package/dist/components/SortBy.d.ts.map +1 -0
  69. package/dist/components/SortBy.js +61 -0
  70. package/dist/components/Stats.d.ts +37 -0
  71. package/dist/components/Stats.d.ts.map +1 -0
  72. package/dist/components/Stats.js +52 -0
  73. package/dist/components/suggestions/AmazonDropdown.d.ts +30 -0
  74. package/dist/components/suggestions/AmazonDropdown.d.ts.map +1 -0
  75. package/dist/components/suggestions/AmazonDropdown.js +529 -0
  76. package/dist/components/suggestions/GoogleDropdown.d.ts +31 -0
  77. package/dist/components/suggestions/GoogleDropdown.d.ts.map +1 -0
  78. package/dist/components/suggestions/GoogleDropdown.js +370 -0
  79. package/dist/components/suggestions/MinimalDropdown.d.ts +24 -0
  80. package/dist/components/suggestions/MinimalDropdown.d.ts.map +1 -0
  81. package/dist/components/suggestions/MinimalDropdown.js +314 -0
  82. package/dist/components/suggestions/MobileSheetDropdown.d.ts +31 -0
  83. package/dist/components/suggestions/MobileSheetDropdown.d.ts.map +1 -0
  84. package/dist/components/suggestions/MobileSheetDropdown.js +485 -0
  85. package/dist/components/suggestions/PinterestDropdown.d.ts +29 -0
  86. package/dist/components/suggestions/PinterestDropdown.d.ts.map +1 -0
  87. package/dist/components/suggestions/PinterestDropdown.js +450 -0
  88. package/dist/components/suggestions/ShopifyDropdown.d.ts +27 -0
  89. package/dist/components/suggestions/ShopifyDropdown.d.ts.map +1 -0
  90. package/dist/components/suggestions/ShopifyDropdown.js +451 -0
  91. package/dist/components/suggestions/SpotlightDropdown.d.ts +33 -0
  92. package/dist/components/suggestions/SpotlightDropdown.d.ts.map +1 -0
  93. package/dist/components/suggestions/SpotlightDropdown.js +547 -0
  94. package/dist/components/suggestions/SuggestionSearchBar.d.ts +123 -0
  95. package/dist/components/suggestions/SuggestionSearchBar.d.ts.map +1 -0
  96. package/dist/components/suggestions/SuggestionSearchBar.js +652 -0
  97. package/dist/components/suggestions/index.d.ts +37 -0
  98. package/dist/components/suggestions/index.d.ts.map +1 -0
  99. package/dist/components/suggestions/index.js +59 -0
  100. package/dist/components/suggestions/styles/index.d.ts +11 -0
  101. package/dist/components/suggestions/styles/index.d.ts.map +1 -0
  102. package/dist/components/suggestions/styles/index.js +289 -0
  103. package/dist/components/suggestions/styles/responsive.d.ts +107 -0
  104. package/dist/components/suggestions/styles/responsive.d.ts.map +1 -0
  105. package/dist/components/suggestions/styles/responsive.js +237 -0
  106. package/dist/components/suggestions/types.d.ts +489 -0
  107. package/dist/components/suggestions/types.d.ts.map +1 -0
  108. package/dist/components/suggestions/types.js +6 -0
  109. package/dist/components/suggestions/utils.d.ts +213 -0
  110. package/dist/components/suggestions/utils.d.ts.map +1 -0
  111. package/dist/components/suggestions/utils.js +514 -0
  112. package/dist/hooks/useAnalytics.d.ts +20 -0
  113. package/dist/hooks/useAnalytics.d.ts.map +1 -0
  114. package/dist/hooks/useAnalytics.js +62 -0
  115. package/dist/hooks/useNaturalLanguageFilters.d.ts +48 -0
  116. package/dist/hooks/useNaturalLanguageFilters.d.ts.map +1 -0
  117. package/dist/hooks/useNaturalLanguageFilters.js +221 -0
  118. package/dist/hooks/useQuerySuggestions.d.ts +21 -0
  119. package/dist/hooks/useQuerySuggestions.d.ts.map +1 -0
  120. package/dist/hooks/useQuerySuggestions.js +68 -0
  121. package/dist/hooks/useQuerySuggestionsEnhanced.d.ts +114 -0
  122. package/dist/hooks/useQuerySuggestionsEnhanced.d.ts.map +1 -0
  123. package/dist/hooks/useQuerySuggestionsEnhanced.js +376 -0
  124. package/dist/hooks/useSearchState.d.ts +35 -0
  125. package/dist/hooks/useSearchState.d.ts.map +1 -0
  126. package/dist/hooks/useSearchState.js +68 -0
  127. package/dist/hooks/useSeekoraSearch.d.ts +20 -0
  128. package/dist/hooks/useSeekoraSearch.d.ts.map +1 -0
  129. package/dist/hooks/useSeekoraSearch.js +63 -0
  130. package/dist/hooks/useSmartSuggestions.d.ts +55 -0
  131. package/dist/hooks/useSmartSuggestions.d.ts.map +1 -0
  132. package/dist/hooks/useSmartSuggestions.js +236 -0
  133. package/dist/hooks/useSuggestionsAnalytics.d.ts +91 -0
  134. package/dist/hooks/useSuggestionsAnalytics.d.ts.map +1 -0
  135. package/dist/hooks/useSuggestionsAnalytics.js +226 -0
  136. package/dist/index.d.ts +80 -0
  137. package/dist/index.d.ts.map +1 -0
  138. package/dist/index.js +86 -0
  139. package/dist/index.umd.js +1 -0
  140. package/dist/src/index.d.ts +2849 -0
  141. package/dist/src/index.esm.js +11679 -0
  142. package/dist/src/index.esm.js.map +1 -0
  143. package/dist/src/index.js +11761 -0
  144. package/dist/src/index.js.map +1 -0
  145. package/dist/themes/createTheme.d.ts +8 -0
  146. package/dist/themes/createTheme.d.ts.map +1 -0
  147. package/dist/themes/createTheme.js +10 -0
  148. package/dist/themes/dark.d.ts +6 -0
  149. package/dist/themes/dark.d.ts.map +1 -0
  150. package/dist/themes/dark.js +34 -0
  151. package/dist/themes/default.d.ts +6 -0
  152. package/dist/themes/default.d.ts.map +1 -0
  153. package/dist/themes/default.js +71 -0
  154. package/dist/themes/mergeThemes.d.ts +7 -0
  155. package/dist/themes/mergeThemes.d.ts.map +1 -0
  156. package/dist/themes/mergeThemes.js +6 -0
  157. package/dist/themes/minimal.d.ts +6 -0
  158. package/dist/themes/minimal.d.ts.map +1 -0
  159. package/dist/themes/minimal.js +34 -0
  160. package/dist/themes/suggestions.d.ts +216 -0
  161. package/dist/themes/suggestions.d.ts.map +1 -0
  162. package/dist/themes/suggestions.js +546 -0
  163. package/dist/themes/types.d.ts +7 -0
  164. package/dist/themes/types.d.ts.map +1 -0
  165. package/dist/themes/types.js +6 -0
  166. package/dist/types/index.d.ts +33 -0
  167. package/dist/types/index.d.ts.map +1 -0
  168. package/dist/types/index.js +4 -0
  169. package/package.json +65 -0
@@ -0,0 +1,217 @@
1
+ /**
2
+ * SearchBar Component
3
+ *
4
+ * Interactive search input component with query suggestions support
5
+ */
6
+ import React, { useState, useRef, useEffect, useCallback } from 'react';
7
+ import { useSearchContext } from './SearchProvider';
8
+ import { useSearchState } from '../hooks/useSearchState';
9
+ import { useQuerySuggestions } from '../hooks/useQuerySuggestions';
10
+ import { log } from '@seekora-ai/ui-sdk-core';
11
+ import { clsx } from 'clsx';
12
+ export const SearchBar = ({ placeholder = 'Search...', showSuggestions = true, debounceMs = 300, minQueryLength = 2, maxSuggestions = 10, onSearch, onQueryChange, onSuggestionSelect, onSearchStateChange, searchOptions, className, style, theme: customTheme, renderSuggestion, renderLoading, }) => {
13
+ const { client, theme, enableAnalytics, autoTrackSearch } = useSearchContext();
14
+ const { query, setQuery, search: triggerSearch, results, loading: searchLoading, error: searchError } = useSearchState();
15
+ const [isFocused, setIsFocused] = useState(false);
16
+ const [selectedIndex, setSelectedIndex] = useState(-1);
17
+ const inputRef = useRef(null);
18
+ const containerRef = useRef(null);
19
+ const isSearchingRef = useRef(false); // Flag to prevent blur handler from interfering
20
+ const { suggestions, loading: suggestionsLoading } = useQuerySuggestions({
21
+ client,
22
+ query,
23
+ enabled: showSuggestions && query.length >= minQueryLength,
24
+ debounceMs,
25
+ maxSuggestions,
26
+ });
27
+ const displayedSuggestions = suggestions.slice(0, maxSuggestions);
28
+ // Expose search state to parent
29
+ useEffect(() => {
30
+ if (onSearchStateChange) {
31
+ onSearchStateChange({
32
+ results,
33
+ loading: searchLoading,
34
+ error: searchError,
35
+ });
36
+ }
37
+ }, [results, searchLoading, searchError, onSearchStateChange]);
38
+ useEffect(() => {
39
+ if (results && onSearch) {
40
+ onSearch(query, results);
41
+ }
42
+ }, [results, query, onSearch]);
43
+ useEffect(() => {
44
+ if (onQueryChange) {
45
+ onQueryChange(query);
46
+ }
47
+ }, [query, onQueryChange]);
48
+ const handleSearch = useCallback(async (searchQuery) => {
49
+ // Allow empty queries - use empty string for search
50
+ const query = searchQuery.trim();
51
+ log.info('SearchBar: Triggering search', { query, originalQuery: searchQuery, isEmpty: !query });
52
+ // Set flag to prevent blur handler from interfering
53
+ isSearchingRef.current = true;
54
+ // Close suggestions first
55
+ setIsFocused(false);
56
+ setSelectedIndex(-1);
57
+ // Update query in state manager (don't trigger search yet - we'll do it explicitly)
58
+ setQuery(query, false);
59
+ // Explicitly trigger search immediately (bypass debounce for Enter key)
60
+ log.info('SearchBar: Calling search() directly');
61
+ await triggerSearch();
62
+ // Blur after a small delay to ensure search is triggered
63
+ setTimeout(() => {
64
+ inputRef.current?.blur();
65
+ // Reset flag after blur
66
+ setTimeout(() => {
67
+ isSearchingRef.current = false;
68
+ }, 50);
69
+ }, 100);
70
+ }, [setQuery, triggerSearch]);
71
+ const handleSuggestionSelect = useCallback((suggestion) => {
72
+ log.verbose('SearchBar: Suggestion selected', { suggestion });
73
+ setSelectedIndex(-1);
74
+ setIsFocused(false);
75
+ inputRef.current?.blur();
76
+ if (onSuggestionSelect) {
77
+ onSuggestionSelect(suggestion);
78
+ }
79
+ // Update query and trigger search
80
+ setQuery(suggestion);
81
+ }, [onSuggestionSelect, setQuery]);
82
+ const handleInputChange = useCallback((e) => {
83
+ const value = e.target.value;
84
+ // Update query in state manager but don't trigger search immediately
85
+ // Search will be triggered on Enter or suggestion select
86
+ setQuery(value, false); // false = don't trigger search immediately
87
+ setSelectedIndex(-1);
88
+ }, [setQuery]);
89
+ const handleKeyDown = useCallback((e) => {
90
+ switch (e.key) {
91
+ case 'ArrowDown':
92
+ if (showSuggestions && displayedSuggestions.length > 0) {
93
+ e.preventDefault();
94
+ setSelectedIndex((prev) => prev < displayedSuggestions.length - 1 ? prev + 1 : prev);
95
+ }
96
+ break;
97
+ case 'ArrowUp':
98
+ if (showSuggestions && displayedSuggestions.length > 0) {
99
+ e.preventDefault();
100
+ setSelectedIndex((prev) => (prev > 0 ? prev - 1 : -1));
101
+ }
102
+ break;
103
+ case 'Enter':
104
+ e.preventDefault();
105
+ e.stopPropagation();
106
+ // Only select suggestion if one is explicitly selected (selectedIndex >= 0)
107
+ // Otherwise, search whatever is in the text box
108
+ if (showSuggestions && displayedSuggestions.length > 0 && selectedIndex >= 0 && selectedIndex < displayedSuggestions.length) {
109
+ // Select suggestion if one is highlighted
110
+ handleSuggestionSelect(displayedSuggestions[selectedIndex].query);
111
+ }
112
+ else {
113
+ // Search whatever is in the text box (even if suggestions are shown)
114
+ // Allow empty search - will use "*" for wildcard
115
+ const currentValue = inputRef.current?.value || query;
116
+ handleSearch(currentValue);
117
+ }
118
+ break;
119
+ case 'Escape':
120
+ setIsFocused(false);
121
+ inputRef.current?.blur();
122
+ break;
123
+ }
124
+ }, [displayedSuggestions, selectedIndex, query, showSuggestions, handleSearch, handleSuggestionSelect]);
125
+ const handleFocus = useCallback(() => {
126
+ setIsFocused(true);
127
+ }, []);
128
+ const handleBlur = useCallback((e) => {
129
+ // Don't interfere if we're explicitly triggering a search
130
+ if (isSearchingRef.current) {
131
+ return;
132
+ }
133
+ // Delay to allow click events on suggestions to fire
134
+ setTimeout(() => {
135
+ if (!containerRef.current?.contains(document.activeElement)) {
136
+ setIsFocused(false);
137
+ setSelectedIndex(-1);
138
+ }
139
+ }, 200);
140
+ }, []);
141
+ const defaultRenderSuggestion = (suggestion, index) => (React.createElement("div", { key: index }, suggestion));
142
+ const defaultRenderLoading = () => (React.createElement("div", { style: { padding: theme.spacing.medium, textAlign: 'center' } }, "Loading suggestions..."));
143
+ const searchBarTheme = customTheme || {};
144
+ const isLoading = suggestionsLoading || searchLoading;
145
+ // Only show suggestions list if:
146
+ // 1. Input is focused
147
+ // 2. Suggestions are enabled
148
+ // 3. Query is long enough
149
+ // 4. AND (we have suggestions to show OR we're currently loading suggestions)
150
+ const hasSuggestions = displayedSuggestions.length > 0;
151
+ const showSuggestionsList = isFocused && showSuggestions && query.length >= minQueryLength && (hasSuggestions || isLoading);
152
+ // Get processing time from results
153
+ const res = results;
154
+ const processingTime = res?.processingTimeMS
155
+ || res?.data?.processingTimeMS
156
+ || res?.data?.data?.processingTimeMS;
157
+ return (React.createElement("div", { ref: containerRef, className: clsx(searchBarTheme.container, className), style: {
158
+ position: 'relative',
159
+ display: 'flex',
160
+ alignItems: 'center',
161
+ ...style,
162
+ } },
163
+ React.createElement("input", { ref: inputRef, type: "text", value: query, onChange: handleInputChange, onKeyDown: handleKeyDown, onFocus: handleFocus, onBlur: handleBlur, placeholder: placeholder, className: clsx(searchBarTheme.input, isFocused && searchBarTheme.inputFocused), style: {
164
+ flex: 1,
165
+ padding: theme.spacing.medium,
166
+ fontSize: theme.typography.fontSize.medium,
167
+ fontFamily: theme.typography.fontFamily,
168
+ backgroundColor: theme.colors.background,
169
+ color: theme.colors.text,
170
+ borderWidth: '1px',
171
+ borderStyle: 'solid',
172
+ borderColor: isFocused ? theme.colors.focus : theme.colors.border,
173
+ borderRadius: typeof theme.borderRadius === 'string' ? theme.borderRadius : theme.borderRadius.medium,
174
+ outline: 'none',
175
+ ...(isFocused && {
176
+ boxShadow: theme.shadows.small,
177
+ }),
178
+ } }),
179
+ processingTime !== undefined && (React.createElement("span", { style: {
180
+ marginLeft: theme.spacing.small,
181
+ fontSize: theme.typography.fontSize.small,
182
+ color: theme.colors.text,
183
+ opacity: 0.7,
184
+ whiteSpace: 'nowrap',
185
+ } },
186
+ processingTime,
187
+ "ms")),
188
+ showSuggestionsList && (React.createElement("div", { className: searchBarTheme.suggestionsContainer, style: {
189
+ position: 'absolute',
190
+ top: '100%',
191
+ left: 0,
192
+ right: 0,
193
+ marginTop: theme.spacing.small,
194
+ backgroundColor: theme.colors.background,
195
+ border: `1px solid ${theme.colors.border}`,
196
+ borderRadius: typeof theme.borderRadius === 'string' ? theme.borderRadius : theme.borderRadius.medium,
197
+ boxShadow: theme.shadows.medium,
198
+ maxHeight: '400px',
199
+ overflowY: 'auto',
200
+ zIndex: 1000,
201
+ } },
202
+ isLoading && (renderLoading ? renderLoading() : defaultRenderLoading()),
203
+ !isLoading && displayedSuggestions.length > 0 && (React.createElement(React.Fragment, null, displayedSuggestions.map((suggestion, index) => {
204
+ const isSelected = index === selectedIndex;
205
+ const renderFn = renderSuggestion || defaultRenderSuggestion;
206
+ return (React.createElement("div", { key: index, className: clsx(searchBarTheme.suggestionItem, isSelected && searchBarTheme.suggestionItemActive), onClick: () => handleSuggestionSelect(suggestion.query), onMouseEnter: () => setSelectedIndex(index), style: {
207
+ padding: theme.spacing.medium,
208
+ cursor: 'pointer',
209
+ backgroundColor: isSelected
210
+ ? theme.colors.hover
211
+ : 'transparent',
212
+ color: theme.colors.text,
213
+ transition: theme.transitions?.fast || '150ms ease-in-out',
214
+ } }, renderFn(suggestion.query, index)));
215
+ }))),
216
+ !isLoading && displayedSuggestions.length === 0 && query.length >= minQueryLength && (React.createElement("div", { style: { padding: theme.spacing.medium, textAlign: 'center', color: theme.colors.textSecondary } }, "No suggestions found"))))));
217
+ };
@@ -0,0 +1,99 @@
1
+ /**
2
+ * SearchBarWithSuggestions Component
3
+ *
4
+ * Integrated search bar with query suggestions dropdown.
5
+ * Combines SearchBar with any of the suggestion dropdown variants.
6
+ */
7
+ import React from 'react';
8
+ import type { SuggestionItem, ProductItem, QuerySuggestionsVariant, QuerySuggestionsClassNames } from '@seekora-ai/ui-sdk-types';
9
+ export interface SearchBarWithSuggestionsProps {
10
+ /** Dropdown variant to use */
11
+ variant?: QuerySuggestionsVariant;
12
+ /** Placeholder text */
13
+ placeholder?: string;
14
+ /** Initial query value */
15
+ initialQuery?: string;
16
+ /** Controlled query value */
17
+ value?: string;
18
+ /** Callback when query changes */
19
+ onQueryChange?: (query: string) => void;
20
+ /** Callback when search is submitted */
21
+ onSearch?: (query: string) => void;
22
+ /** Callback when suggestion is selected */
23
+ onSuggestionSelect?: (suggestion: SuggestionItem) => void;
24
+ /** Callback when product is clicked */
25
+ onProductClick?: (product: ProductItem) => void;
26
+ /** Show search button */
27
+ showSearchButton?: boolean;
28
+ /** Search button text */
29
+ searchButtonText?: string;
30
+ /** Show clear button */
31
+ showClearButton?: boolean;
32
+ /** Auto focus on mount */
33
+ autoFocus?: boolean;
34
+ /** Min query length for suggestions */
35
+ minQueryLength?: number;
36
+ /** Max suggestions to show */
37
+ maxSuggestions?: number;
38
+ /** Debounce delay (ms) */
39
+ debounceMs?: number;
40
+ /** Show recent searches */
41
+ showRecentSearches?: boolean;
42
+ /** Show trending when empty */
43
+ showTrendingOnEmpty?: boolean;
44
+ /** Include dropdown recommendations */
45
+ includeDropdownRecommendations?: boolean;
46
+ /** Filtered tabs config */
47
+ filteredTabs?: Array<{
48
+ id?: string;
49
+ label: string;
50
+ filter: string;
51
+ }>;
52
+ /** Enable analytics tracking */
53
+ enableAnalytics?: boolean;
54
+ /** Analytics tags */
55
+ analyticsTags?: string[];
56
+ /** Dropdown width */
57
+ dropdownWidth?: string | number;
58
+ /** Dropdown max height */
59
+ dropdownMaxHeight?: string | number;
60
+ /** Custom class names */
61
+ classNames?: {
62
+ wrapper?: string;
63
+ input?: string;
64
+ button?: string;
65
+ clearButton?: string;
66
+ dropdown?: string;
67
+ } & QuerySuggestionsClassNames;
68
+ /** Custom styles */
69
+ style?: React.CSSProperties;
70
+ /** Input style */
71
+ inputStyle?: React.CSSProperties;
72
+ /** ARIA label */
73
+ ariaLabel?: string;
74
+ }
75
+ export interface SearchBarWithSuggestionsRef {
76
+ /** Focus the input */
77
+ focus: () => void;
78
+ /** Blur the input */
79
+ blur: () => void;
80
+ /** Clear the input */
81
+ clear: () => void;
82
+ /** Get current query */
83
+ getQuery: () => string;
84
+ /** Set query value */
85
+ setQuery: (query: string) => void;
86
+ /** Open dropdown */
87
+ openDropdown: () => void;
88
+ /** Close dropdown */
89
+ closeDropdown: () => void;
90
+ /** Navigate to next suggestion */
91
+ navigateNext: () => void;
92
+ /** Navigate to previous suggestion */
93
+ navigatePrevious: () => void;
94
+ /** Select current suggestion */
95
+ selectCurrent: () => void;
96
+ }
97
+ export declare const SearchBarWithSuggestions: React.ForwardRefExoticComponent<SearchBarWithSuggestionsProps & React.RefAttributes<SearchBarWithSuggestionsRef>>;
98
+ export default SearchBarWithSuggestions;
99
+ //# sourceMappingURL=SearchBarWithSuggestions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SearchBarWithSuggestions.d.ts","sourceRoot":"","sources":["../../src/components/SearchBarWithSuggestions.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,275 @@
1
+ /**
2
+ * SearchBarWithSuggestions Component
3
+ *
4
+ * Integrated search bar with query suggestions dropdown.
5
+ * Combines SearchBar with any of the suggestion dropdown variants.
6
+ */
7
+ import React, { useState, useRef, useCallback, useEffect, forwardRef, useImperativeHandle, } from 'react';
8
+ import { useSearchContext } from './SearchProvider';
9
+ import { QuerySuggestionsDropdown } from './QuerySuggestionsDropdown';
10
+ import { RichQuerySuggestions } from './RichQuerySuggestions';
11
+ import { FederatedDropdown } from './FederatedDropdown';
12
+ import { useSuggestionsAnalytics } from '../hooks/useSuggestionsAnalytics';
13
+ import { clsx } from 'clsx';
14
+ // ============================================================================
15
+ // Styles
16
+ // ============================================================================
17
+ const styles = {
18
+ wrapper: {
19
+ position: 'relative',
20
+ width: '100%',
21
+ },
22
+ inputWrapper: {
23
+ display: 'flex',
24
+ alignItems: 'center',
25
+ backgroundColor: 'var(--seekora-bg-surface, #ffffff)',
26
+ border: '1px solid var(--seekora-border-color, #e5e7eb)',
27
+ borderRadius: 'var(--seekora-border-radius-lg, 8px)',
28
+ overflow: 'hidden',
29
+ transition: 'border-color 150ms ease, box-shadow 150ms ease',
30
+ },
31
+ inputWrapperFocused: {
32
+ borderColor: 'var(--seekora-primary, #3b82f6)',
33
+ boxShadow: '0 0 0 3px var(--seekora-primary-light, rgba(59, 130, 246, 0.1))',
34
+ },
35
+ input: {
36
+ flex: 1,
37
+ padding: '12px 16px',
38
+ fontSize: '16px',
39
+ border: 'none',
40
+ outline: 'none',
41
+ backgroundColor: 'transparent',
42
+ color: 'var(--seekora-text-primary, #111827)',
43
+ fontFamily: 'inherit',
44
+ },
45
+ searchIcon: {
46
+ width: '20px',
47
+ height: '20px',
48
+ color: 'var(--seekora-text-secondary, #9ca3af)',
49
+ marginLeft: '12px',
50
+ flexShrink: 0,
51
+ },
52
+ clearButton: {
53
+ display: 'flex',
54
+ alignItems: 'center',
55
+ justifyContent: 'center',
56
+ width: '32px',
57
+ height: '32px',
58
+ marginRight: '4px',
59
+ padding: 0,
60
+ border: 'none',
61
+ backgroundColor: 'transparent',
62
+ color: 'var(--seekora-text-secondary, #9ca3af)',
63
+ borderRadius: '50%',
64
+ cursor: 'pointer',
65
+ transition: 'background-color 150ms ease, color 150ms ease',
66
+ },
67
+ searchButton: {
68
+ display: 'flex',
69
+ alignItems: 'center',
70
+ justifyContent: 'center',
71
+ gap: '8px',
72
+ padding: '12px 20px',
73
+ margin: '4px',
74
+ border: 'none',
75
+ backgroundColor: 'var(--seekora-primary, #3b82f6)',
76
+ color: 'white',
77
+ borderRadius: 'var(--seekora-border-radius, 6px)',
78
+ fontSize: '14px',
79
+ fontWeight: 600,
80
+ cursor: 'pointer',
81
+ transition: 'background-color 150ms ease',
82
+ },
83
+ };
84
+ // ============================================================================
85
+ // Icons
86
+ // ============================================================================
87
+ const SearchIcon = () => (React.createElement("svg", { viewBox: "0 0 20 20", fill: "currentColor", style: styles.searchIcon },
88
+ 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" })));
89
+ const ClearIcon = () => (React.createElement("svg", { viewBox: "0 0 20 20", fill: "currentColor", style: { width: 16, height: 16 } },
90
+ 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" })));
91
+ // ============================================================================
92
+ // Component
93
+ // ============================================================================
94
+ export const SearchBarWithSuggestions = forwardRef(function SearchBarWithSuggestions(props, ref) {
95
+ const { variant = 'classic', placeholder = 'Search...', initialQuery = '', value, onQueryChange, onSearch, onSuggestionSelect, onProductClick, showSearchButton = false, searchButtonText = 'Search', showClearButton = true, autoFocus = false, minQueryLength = 1, maxSuggestions = 8, debounceMs = 200, showRecentSearches = true, showTrendingOnEmpty = true, includeDropdownRecommendations = false, filteredTabs, enableAnalytics = true, analyticsTags, dropdownWidth, dropdownMaxHeight, classNames = {}, style, inputStyle, ariaLabel = 'Search', } = props;
96
+ const { client } = useSearchContext();
97
+ const inputRef = useRef(null);
98
+ const dropdownRef = useRef(null);
99
+ const [query, setQuery] = useState(value ?? initialQuery);
100
+ const [isFocused, setIsFocused] = useState(false);
101
+ const [isDropdownOpen, setIsDropdownOpen] = useState(false);
102
+ // Analytics
103
+ const analytics = useSuggestionsAnalytics({
104
+ client,
105
+ enabled: enableAnalytics,
106
+ analyticsTags,
107
+ });
108
+ // Sync controlled value
109
+ useEffect(() => {
110
+ if (value !== undefined && value !== query) {
111
+ setQuery(value);
112
+ }
113
+ }, [value]);
114
+ // Handle query change
115
+ const handleQueryChange = useCallback((e) => {
116
+ const newQuery = e.target.value;
117
+ setQuery(newQuery);
118
+ onQueryChange?.(newQuery);
119
+ // Open dropdown when typing
120
+ if (newQuery.length >= minQueryLength || (showTrendingOnEmpty && newQuery.length === 0)) {
121
+ setIsDropdownOpen(true);
122
+ }
123
+ }, [onQueryChange, minQueryLength, showTrendingOnEmpty]);
124
+ // Handle search submit
125
+ const handleSubmit = useCallback((e) => {
126
+ e?.preventDefault();
127
+ if (query.trim()) {
128
+ onSearch?.(query.trim());
129
+ analytics.trackSearchSubmit(query.trim(), false);
130
+ setIsDropdownOpen(false);
131
+ }
132
+ }, [query, onSearch, analytics]);
133
+ // Handle suggestion select
134
+ const handleSuggestionSelect = useCallback((suggestion) => {
135
+ setQuery(suggestion.query);
136
+ onQueryChange?.(suggestion.query);
137
+ onSuggestionSelect?.(suggestion);
138
+ analytics.trackSuggestionClick({
139
+ suggestion,
140
+ position: 0, // Would need to track actual position
141
+ query,
142
+ });
143
+ setIsDropdownOpen(false);
144
+ // Optionally trigger search
145
+ onSearch?.(suggestion.query);
146
+ }, [query, onQueryChange, onSuggestionSelect, onSearch, analytics]);
147
+ // Handle product click
148
+ const handleProductClick = useCallback((product) => {
149
+ onProductClick?.(product);
150
+ analytics.trackProductClick({
151
+ product,
152
+ position: 0,
153
+ query,
154
+ });
155
+ setIsDropdownOpen(false);
156
+ }, [query, onProductClick, analytics]);
157
+ // Handle recent search click
158
+ const handleRecentSearchClick = useCallback((search) => {
159
+ setQuery(search.query);
160
+ onQueryChange?.(search.query);
161
+ analytics.trackRecentSearchClick(search);
162
+ setIsDropdownOpen(false);
163
+ onSearch?.(search.query);
164
+ }, [onQueryChange, onSearch, analytics]);
165
+ // Handle focus
166
+ const handleFocus = useCallback(() => {
167
+ setIsFocused(true);
168
+ setIsDropdownOpen(true);
169
+ analytics.trackDropdownOpen(query);
170
+ }, [query, analytics]);
171
+ // Handle blur
172
+ const handleBlur = useCallback(() => {
173
+ setIsFocused(false);
174
+ // Delay closing to allow click events on dropdown
175
+ setTimeout(() => {
176
+ setIsDropdownOpen(false);
177
+ analytics.trackDropdownClose(query);
178
+ }, 200);
179
+ }, [query, analytics]);
180
+ // Handle clear
181
+ const handleClear = useCallback(() => {
182
+ setQuery('');
183
+ onQueryChange?.('');
184
+ inputRef.current?.focus();
185
+ }, [onQueryChange]);
186
+ // Handle keyboard navigation
187
+ const handleKeyDown = useCallback((e) => {
188
+ if (!isDropdownOpen) {
189
+ if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
190
+ setIsDropdownOpen(true);
191
+ }
192
+ return;
193
+ }
194
+ switch (e.key) {
195
+ case 'ArrowDown':
196
+ e.preventDefault();
197
+ dropdownRef.current?.navigateNext?.();
198
+ break;
199
+ case 'ArrowUp':
200
+ e.preventDefault();
201
+ dropdownRef.current?.navigatePrevious?.();
202
+ break;
203
+ case 'Enter':
204
+ e.preventDefault();
205
+ if ((dropdownRef.current?.getActiveIndex?.() ?? -1) >= 0) {
206
+ dropdownRef.current?.selectActive?.();
207
+ }
208
+ else {
209
+ handleSubmit();
210
+ }
211
+ break;
212
+ case 'Escape':
213
+ e.preventDefault();
214
+ setIsDropdownOpen(false);
215
+ inputRef.current?.blur();
216
+ break;
217
+ }
218
+ }, [isDropdownOpen, handleSubmit]);
219
+ // Expose ref methods
220
+ useImperativeHandle(ref, () => ({
221
+ focus: () => inputRef.current?.focus(),
222
+ blur: () => inputRef.current?.blur(),
223
+ clear: handleClear,
224
+ getQuery: () => query,
225
+ setQuery: (newQuery) => {
226
+ setQuery(newQuery);
227
+ onQueryChange?.(newQuery);
228
+ },
229
+ openDropdown: () => setIsDropdownOpen(true),
230
+ closeDropdown: () => setIsDropdownOpen(false),
231
+ navigateNext: () => dropdownRef.current?.navigateNext?.(),
232
+ navigatePrevious: () => dropdownRef.current?.navigatePrevious?.(),
233
+ selectCurrent: () => dropdownRef.current?.selectActive?.(),
234
+ }), [query, handleClear, onQueryChange]);
235
+ // Render dropdown based on variant
236
+ const renderDropdown = () => {
237
+ const commonProps = {
238
+ query,
239
+ isOpen: isDropdownOpen,
240
+ maxSuggestions,
241
+ minQueryLength,
242
+ debounceMs,
243
+ showRecentSearches,
244
+ classNames,
245
+ onSuggestionSelect: handleSuggestionSelect,
246
+ onRecentSearchClick: handleRecentSearchClick,
247
+ onClose: () => setIsDropdownOpen(false),
248
+ analyticsTags,
249
+ };
250
+ switch (variant) {
251
+ case 'rich':
252
+ return (React.createElement(RichQuerySuggestions, { ref: dropdownRef, ...commonProps, includeDropdownRecommendations: includeDropdownRecommendations, includeCategories: true, width: dropdownWidth || '100%', maxHeight: dropdownMaxHeight || '480px' }));
253
+ case 'federated':
254
+ return (React.createElement(FederatedDropdown, { ref: dropdownRef, ...commonProps, filteredTabs: filteredTabs, showProducts: true, showBrands: true, showFilteredTabs: !!filteredTabs, onProductClick: handleProductClick, width: dropdownWidth || '800px', maxHeight: dropdownMaxHeight || '600px' }));
255
+ case 'compact':
256
+ return (React.createElement(QuerySuggestionsDropdown, { ref: dropdownRef, ...commonProps, maxSuggestions: 5, showCounts: false, width: dropdownWidth || '100%' }));
257
+ case 'classic':
258
+ default:
259
+ return (React.createElement(QuerySuggestionsDropdown, { ref: dropdownRef, ...commonProps, width: dropdownWidth || '100%' }));
260
+ }
261
+ };
262
+ return (React.createElement("div", { className: clsx('seekora-search-bar-with-suggestions', classNames.wrapper), style: { ...styles.wrapper, ...style } },
263
+ React.createElement("form", { onSubmit: handleSubmit },
264
+ React.createElement("div", { style: {
265
+ ...styles.inputWrapper,
266
+ ...(isFocused ? styles.inputWrapperFocused : {}),
267
+ } },
268
+ React.createElement(SearchIcon, null),
269
+ React.createElement("input", { ref: inputRef, type: "text", value: query, onChange: handleQueryChange, onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown, placeholder: placeholder, autoFocus: autoFocus, autoComplete: "off", autoCorrect: "off", autoCapitalize: "off", spellCheck: false, "aria-label": ariaLabel, "aria-expanded": isDropdownOpen, "aria-haspopup": "listbox", "aria-autocomplete": "list", role: "combobox", className: classNames.input, style: { ...styles.input, ...inputStyle } }),
270
+ showClearButton && query && (React.createElement("button", { type: "button", onClick: handleClear, className: classNames.clearButton, style: styles.clearButton, "aria-label": "Clear search" },
271
+ React.createElement(ClearIcon, null))),
272
+ showSearchButton && (React.createElement("button", { type: "submit", className: classNames.button, style: styles.searchButton }, searchButtonText)))),
273
+ React.createElement("div", { className: classNames.dropdown }, renderDropdown())));
274
+ });
275
+ export default SearchBarWithSuggestions;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * SearchLayout Component
3
+ *
4
+ * Provides a layout structure for search interfaces with sidebar and main content
5
+ */
6
+ import React from 'react';
7
+ export interface SearchLayoutTheme {
8
+ container?: string;
9
+ sidebar?: string;
10
+ main?: string;
11
+ header?: string;
12
+ footer?: string;
13
+ }
14
+ export interface SearchLayoutProps {
15
+ /** Content to render in the sidebar (filters, facets, etc.) */
16
+ sidebar?: React.ReactNode;
17
+ /** Content to render in the main area (results, etc.) */
18
+ children: React.ReactNode;
19
+ /** Header content */
20
+ header?: React.ReactNode;
21
+ /** Footer content */
22
+ footer?: React.ReactNode;
23
+ /** Sidebar width */
24
+ sidebarWidth?: string;
25
+ /** Custom className */
26
+ className?: string;
27
+ /** Custom styles */
28
+ style?: React.CSSProperties;
29
+ /** Custom theme */
30
+ theme?: SearchLayoutTheme;
31
+ /** Show sidebar on mobile */
32
+ showSidebarOnMobile?: boolean;
33
+ }
34
+ export declare const SearchLayout: React.FC<SearchLayoutProps>;
35
+ //# sourceMappingURL=SearchLayout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SearchLayout.d.ts","sourceRoot":"","sources":["../../src/components/SearchLayout.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,yDAAyD;IACzD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,qBAAqB;IACrB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,qBAAqB;IACrB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,oBAAoB;IACpB,YAAY,CAAC,EAAE,MAAM,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,iBAAiB,CAAC;IAC1B,6BAA6B;IAC7B,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAkGpD,CAAC"}