@seekora-ai/ui-sdk-react 0.2.4 → 0.2.5

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.
@@ -1199,6 +1199,12 @@ interface FederatedDropdownProps extends QuerySuggestionsEventHandlers {
1199
1199
  zIndex?: number;
1200
1200
  /** Analytics tags */
1201
1201
  analyticsTags?: string[];
1202
+ /** Include facets in suggestions API response */
1203
+ includeFacets?: boolean;
1204
+ /** Include categories in suggestions API response */
1205
+ includeCategories?: boolean;
1206
+ /** Include dropdown recommendations (trending products, top searches, etc.) */
1207
+ includeDropdownRecommendations?: boolean;
1202
1208
  }
1203
1209
  interface FederatedDropdownRef {
1204
1210
  getActiveIndex: () => number;
@@ -1265,6 +1271,10 @@ interface SearchBarWithSuggestionsProps {
1265
1271
  enableAnalytics?: boolean;
1266
1272
  /** Analytics tags */
1267
1273
  analyticsTags?: string[];
1274
+ /** Include facets in suggestions API response (federated/rich) */
1275
+ includeFacets?: boolean;
1276
+ /** Include categories in suggestions API response (federated/rich) */
1277
+ includeCategories?: boolean;
1268
1278
  /** Dropdown width */
1269
1279
  dropdownWidth?: string | number;
1270
1280
  /** Dropdown max height */
@@ -2416,6 +2426,8 @@ interface DocSearchProps {
2416
2426
  initialOpen?: boolean;
2417
2427
  disableShortcut?: boolean;
2418
2428
  shortcutKey?: string;
2429
+ /** Post-process results per source (e.g. filter by permission, resolve :storeId). */
2430
+ processGroupedResults?: (sourceId: string, items: DocSearchSuggestion[]) => DocSearchSuggestion[];
2419
2431
  }
2420
2432
  interface DocSearchButtonProps {
2421
2433
  onClick: () => void;
@@ -2470,7 +2482,7 @@ type DocSearchAction = {
2470
2482
  type: 'RESET';
2471
2483
  };
2472
2484
 
2473
- declare function DocSearch({ storeId, storeSecret, seekoraApiEndpoint, apiEndpoint, apiKey, sources, placeholder, maxResults, debounceMs, onSelect, onClose, translations, renderButton, buttonComponent: ButtonComponent, initialOpen, disableShortcut, shortcutKey, }: DocSearchProps): React__default.JSX.Element;
2485
+ declare function DocSearch({ storeId, storeSecret, seekoraApiEndpoint, apiEndpoint, apiKey, sources, placeholder, maxResults, debounceMs, onSelect, onClose, translations, renderButton, buttonComponent: ButtonComponent, initialOpen, disableShortcut, shortcutKey, processGroupedResults, }: DocSearchProps): React__default.JSX.Element;
2474
2486
 
2475
2487
  declare function DocSearchButton({ onClick, placeholder }: DocSearchButtonProps): React__default.JSX.Element;
2476
2488
 
@@ -2480,6 +2492,7 @@ interface UseDocSearchOptions {
2480
2492
  sources?: SearchSource[];
2481
2493
  maxResults?: number;
2482
2494
  debounceMs?: number;
2495
+ processGroupedResults?: (sourceId: string, items: DocSearchSuggestion[]) => DocSearchSuggestion[];
2483
2496
  }
2484
2497
  declare function useDocSearch(options: UseDocSearchOptions): {
2485
2498
  sources: SearchSource[];
@@ -4564,7 +4564,7 @@ function transformFilteredTab(raw) {
4564
4564
  // Main Hook
4565
4565
  // ============================================================================
4566
4566
  function useQuerySuggestionsEnhanced(options) {
4567
- const { client, query, enabled = true, debounceMs = 200, maxSuggestions = 10, minQueryLength = 1, includeDropdownRecommendations = false, includeCategories = false, includeFacets = false, maxCategories = 3, maxFacets = 5, filteredTabs, minPopularity, timeRange, disableTypoTolerance, analyticsTags, enableRecentSearches = true, maxRecentSearches = MAX_RECENT_SEARCHES_DEFAULT, recentSearchesKey = RECENT_SEARCHES_DEFAULT_KEY, onSuggestionsLoaded, onError, } = options;
4567
+ const { client, query, enabled = true, debounceMs = 200, maxSuggestions = 10, minQueryLength = 1, includeDropdownRecommendations = false, includeCategories = true, includeFacets = false, maxCategories = 3, maxFacets = 5, filteredTabs, minPopularity, timeRange, disableTypoTolerance, analyticsTags, enableRecentSearches = true, maxRecentSearches = MAX_RECENT_SEARCHES_DEFAULT, recentSearchesKey = RECENT_SEARCHES_DEFAULT_KEY, onSuggestionsLoaded, onError, } = options;
4568
4568
  // State
4569
4569
  const [suggestions, setSuggestions] = useState([]);
4570
4570
  const [loading, setLoading] = useState(false);
@@ -5966,7 +5966,7 @@ const ImagePlaceholder = () => (React.createElement("svg", { viewBox: "0 0 24 24
5966
5966
  // Component
5967
5967
  // ============================================================================
5968
5968
  const FederatedDropdown = forwardRef(function FederatedDropdown(props, ref) {
5969
- const { query, isOpen = true, maxSuggestions = 8, maxProducts = 8, maxBrands = 8, minQueryLength = 0, debounceMs = 200, filteredTabs: tabsConfig, showProducts = true, showBrands = true, showFilteredTabs = true, showRecentSearches = true, layout = 'side-by-side', productsColumnWidth = '60%', suggestionsColumnWidth = '40%', showPrices = true, currencySymbol = '$', classNames = {}, style, renderProduct, renderSuggestion, renderBrand, renderTab, header, footer, viewAllProductsLink, width = '800px', maxHeight = '600px', zIndex = 1000, analyticsTags, onSuggestionSelect, onProductClick, onBrandClick, onTabSelect, onRecentSearchClick, onRecentSearchRemove, onViewAllClick, onOpen, onClose, } = props;
5969
+ const { query, isOpen = true, maxSuggestions = 8, maxProducts = 8, maxBrands = 8, minQueryLength = 0, debounceMs = 200, filteredTabs: tabsConfig, showProducts = true, showBrands = true, showFilteredTabs = true, showRecentSearches = true, layout = 'side-by-side', productsColumnWidth = '60%', suggestionsColumnWidth = '40%', showPrices = true, currencySymbol = '$', classNames = {}, style, renderProduct, renderSuggestion, renderBrand, renderTab, header, footer, viewAllProductsLink, width = '800px', maxHeight = '600px', zIndex = 1000, analyticsTags, includeFacets: includeFacetsProp = true, includeCategories: includeCategoriesProp = true, includeDropdownRecommendations: includeDropdownRecommendationsProp = true, onSuggestionSelect, onProductClick, onBrandClick, onTabSelect, onRecentSearchClick, onRecentSearchRemove, onViewAllClick, onOpen, onClose, } = props;
5970
5970
  const { client } = useSearchContext();
5971
5971
  const containerRef = useRef(null);
5972
5972
  const [activeIndex, setActiveIndex] = useState(-1);
@@ -5981,7 +5981,9 @@ const FederatedDropdown = forwardRef(function FederatedDropdown(props, ref) {
5981
5981
  debounceMs,
5982
5982
  maxSuggestions,
5983
5983
  minQueryLength,
5984
- includeDropdownRecommendations: true,
5984
+ includeDropdownRecommendations: includeDropdownRecommendationsProp,
5985
+ includeCategories: includeCategoriesProp,
5986
+ includeFacets: includeFacetsProp,
5985
5987
  filteredTabs: tabsConfig,
5986
5988
  analyticsTags,
5987
5989
  enableRecentSearches: showRecentSearches,
@@ -6520,7 +6522,7 @@ const ClearIcon = () => (React.createElement("svg", { viewBox: "0 0 20 20", fill
6520
6522
  // Component
6521
6523
  // ============================================================================
6522
6524
  const SearchBarWithSuggestions = forwardRef(function SearchBarWithSuggestions(props, ref) {
6523
- 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;
6525
+ 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, includeFacets, includeCategories, dropdownWidth, dropdownMaxHeight, classNames = {}, style, inputStyle, ariaLabel = 'Search', } = props;
6524
6526
  const { client } = useSearchContext();
6525
6527
  const inputRef = useRef(null);
6526
6528
  const dropdownRef = useRef(null);
@@ -6679,7 +6681,7 @@ const SearchBarWithSuggestions = forwardRef(function SearchBarWithSuggestions(pr
6679
6681
  case 'rich':
6680
6682
  return (React.createElement(RichQuerySuggestions, { ref: dropdownRef, ...commonProps, includeDropdownRecommendations: includeDropdownRecommendations, includeCategories: true, width: dropdownWidth || '100%', maxHeight: dropdownMaxHeight || '480px' }));
6681
6683
  case 'federated':
6682
- return (React.createElement(FederatedDropdown, { ref: dropdownRef, ...commonProps, filteredTabs: filteredTabs, showProducts: true, showBrands: true, showFilteredTabs: !!filteredTabs, onProductClick: handleProductClick, width: dropdownWidth || '800px', maxHeight: dropdownMaxHeight || '600px' }));
6684
+ return (React.createElement(FederatedDropdown, { ref: dropdownRef, ...commonProps, filteredTabs: filteredTabs, showProducts: true, showBrands: true, showFilteredTabs: !!filteredTabs, onProductClick: handleProductClick, width: dropdownWidth || '800px', maxHeight: dropdownMaxHeight || '600px', includeFacets: includeFacets, includeCategories: includeCategories, includeDropdownRecommendations: includeDropdownRecommendations }));
6683
6685
  case 'compact':
6684
6686
  return (React.createElement(QuerySuggestionsDropdown, { ref: dropdownRef, ...commonProps, maxSuggestions: 5, showCounts: false, width: dropdownWidth || '100%' }));
6685
6687
  case 'classic':
@@ -11436,16 +11438,48 @@ function getTypeLevel(type) {
11436
11438
  function isChildType(type) {
11437
11439
  return getTypeLevel(type) >= 2;
11438
11440
  }
11441
+ /** Build a stable grouping key from all hierarchy levels (lvl0 through lvl5). */
11442
+ function getHierarchyKey(hit) {
11443
+ const h = hit.hierarchy ?? {};
11444
+ const parts = [
11445
+ h.lvl0 ?? '',
11446
+ h.lvl1 ?? '',
11447
+ h.lvl2 ?? '',
11448
+ h.lvl3 ?? '',
11449
+ h.lvl4 ?? '',
11450
+ h.lvl5 ?? '',
11451
+ ];
11452
+ return parts.join('\0');
11453
+ }
11454
+ /** Build a display breadcrumb from hierarchy, skipping consecutive duplicates (e.g. lvl0 === lvl1). */
11455
+ function getHierarchyBreadcrumb(hit) {
11456
+ const h = hit.hierarchy ?? {};
11457
+ const parts = [h.lvl0, h.lvl1, h.lvl2, h.lvl3, h.lvl4, h.lvl5].filter((v) => typeof v === 'string' && v.length > 0);
11458
+ const deduped = [];
11459
+ for (const p of parts) {
11460
+ if (deduped[deduped.length - 1] !== p)
11461
+ deduped.push(p);
11462
+ }
11463
+ return deduped.join(' › ');
11464
+ }
11439
11465
  function groupHitsByHierarchy(hits) {
11466
+ const hitToIndex = new Map();
11467
+ hits.forEach((h, i) => hitToIndex.set(h, i));
11440
11468
  const groups = new Map();
11441
11469
  for (const hit of hits) {
11442
- const lvl0 = hit.hierarchy?.lvl0 || '';
11443
- if (!groups.has(lvl0))
11444
- groups.set(lvl0, []);
11445
- groups.get(lvl0).push(hit);
11446
- }
11470
+ const key = getHierarchyKey(hit);
11471
+ if (!groups.has(key))
11472
+ groups.set(key, []);
11473
+ groups.get(key).push(hit);
11474
+ }
11475
+ const entries = Array.from(groups.entries());
11476
+ entries.sort(([, aHits], [, bHits]) => {
11477
+ const aMin = Math.min(...aHits.map(h => hitToIndex.get(h) ?? 0));
11478
+ const bMin = Math.min(...bHits.map(h => hitToIndex.get(h) ?? 0));
11479
+ return aMin - bMin;
11480
+ });
11447
11481
  const result = [];
11448
- for (const [lvl0Key, groupHits] of groups.entries()) {
11482
+ for (const [, groupHits] of entries) {
11449
11483
  const sortedHits = [...groupHits].sort((a, b) => {
11450
11484
  const aType = a.type;
11451
11485
  const bType = b.type;
@@ -11458,7 +11492,8 @@ function groupHitsByHierarchy(hits) {
11458
11492
  const isLastChild = isChild && (!nextHit || !isChildType(nextHit.type));
11459
11493
  return { ...hit, isChild, isLastChild };
11460
11494
  });
11461
- result.push({ category: lvl0Key || null, hits: markedHits });
11495
+ const category = getHierarchyBreadcrumb(groupHits[0]);
11496
+ result.push({ category: category || null, hits: markedHits });
11462
11497
  }
11463
11498
  return result;
11464
11499
  }
@@ -11473,15 +11508,36 @@ function getHitKey(hit, index) {
11473
11508
  return hit.objectID;
11474
11509
  return `suggestion-${hit.url}-${index}`;
11475
11510
  }
11476
- function Results({ hits, groupedHits, selectedIndex, onSelect, onHover, query, isLoading, error, translations = {}, sources: _sources = [], }) {
11511
+ function Results({ hits, groupedHits, selectedIndex, onSelect, onHover, scrollSelectionIntoViewRef, query, isLoading, error, translations = {}, sources: _sources = [], }) {
11477
11512
  const listRef = useRef(null);
11478
11513
  useEffect(() => {
11479
- if (listRef.current && hits.length > 0) {
11480
- const selectedItem = listRef.current.children[selectedIndex];
11481
- if (selectedItem)
11482
- selectedItem.scrollIntoView({ block: 'nearest' });
11514
+ if (!listRef.current || hits.length === 0)
11515
+ return;
11516
+ // Only scroll when selection changed via keyboard (avoids scroll-to-selected on hover)
11517
+ if (scrollSelectionIntoViewRef && !scrollSelectionIntoViewRef.current)
11518
+ return;
11519
+ // listRef's direct children are groups (li.seekora-docsearch-results-group), not hits.
11520
+ // Find the actual hit element at flat selectedIndex.
11521
+ const groupEls = listRef.current.querySelectorAll('.seekora-docsearch-results-group');
11522
+ let idx = 0;
11523
+ for (const groupEl of groupEls) {
11524
+ const itemList = groupEl.querySelector('.seekora-docsearch-results-group-items');
11525
+ if (!itemList)
11526
+ continue;
11527
+ const items = itemList.children;
11528
+ for (let i = 0; i < items.length; i++) {
11529
+ if (idx === selectedIndex) {
11530
+ items[i].scrollIntoView({ block: 'nearest' });
11531
+ if (scrollSelectionIntoViewRef)
11532
+ scrollSelectionIntoViewRef.current = false;
11533
+ return;
11534
+ }
11535
+ idx++;
11536
+ }
11483
11537
  }
11484
- }, [selectedIndex, hits.length]);
11538
+ if (scrollSelectionIntoViewRef)
11539
+ scrollSelectionIntoViewRef.current = false;
11540
+ }, [selectedIndex, hits.length, scrollSelectionIntoViewRef]);
11485
11541
  if (!query) {
11486
11542
  return (React.createElement("div", { className: "seekora-docsearch-empty" },
11487
11543
  React.createElement("p", { className: "seekora-docsearch-empty-text" }, translations.searchPlaceholder || 'Type to start searching...')));
@@ -11668,7 +11724,7 @@ function reducer(state, action) {
11668
11724
  }
11669
11725
  }
11670
11726
  function useDocSearch(options) {
11671
- const { apiEndpoint, apiKey, sources, maxResults = 10, debounceMs = 200 } = options;
11727
+ const { apiEndpoint, apiKey, sources, maxResults = 10, debounceMs = 200, processGroupedResults } = options;
11672
11728
  const searchSources = sources || (apiEndpoint ? [{
11673
11729
  id: 'default',
11674
11730
  name: 'Results',
@@ -11771,7 +11827,10 @@ function useDocSearch(options) {
11771
11827
  const results = await Promise.all(searchSources.map(async (source) => {
11772
11828
  const controller = new AbortController();
11773
11829
  abortControllersRef.current.set(source.id, controller);
11774
- const items = await fetchFromSource(source, query, controller.signal);
11830
+ let items = await fetchFromSource(source, query, controller.signal);
11831
+ if (processGroupedResults) {
11832
+ items = processGroupedResults(source.id, items);
11833
+ }
11775
11834
  return { source, items };
11776
11835
  }));
11777
11836
  const groupedResults = results.filter(r => r.items.length > 0);
@@ -11785,7 +11844,7 @@ function useDocSearch(options) {
11785
11844
  finally {
11786
11845
  dispatch({ type: 'SET_LOADING', payload: false });
11787
11846
  }
11788
- }, [searchSources, fetchFromSource]);
11847
+ }, [searchSources, fetchFromSource, processGroupedResults]);
11789
11848
  const search = useCallback((q) => fetchSuggestions(q), [fetchSuggestions]);
11790
11849
  const setQuery = useCallback((query) => {
11791
11850
  dispatch({ type: 'SET_QUERY', payload: query });
@@ -11829,41 +11888,59 @@ function useDocSearch(options) {
11829
11888
  };
11830
11889
  }
11831
11890
 
11891
+ /** Build hierarchy from doc: either nested doc.hierarchy or flat keys like doc['hierarchy.lvl0'] */
11892
+ function getHierarchy(doc) {
11893
+ const hierarchy = {};
11894
+ if (doc?.hierarchy && typeof doc.hierarchy === 'object') {
11895
+ hierarchy.lvl0 = doc.hierarchy.lvl0;
11896
+ hierarchy.lvl1 = doc.hierarchy.lvl1;
11897
+ hierarchy.lvl2 = doc.hierarchy.lvl2;
11898
+ hierarchy.lvl3 = doc.hierarchy.lvl3;
11899
+ hierarchy.lvl4 = doc.hierarchy.lvl4;
11900
+ hierarchy.lvl5 = doc.hierarchy.lvl5;
11901
+ }
11902
+ else if (doc && typeof doc === 'object') {
11903
+ hierarchy.lvl0 = doc['hierarchy.lvl0'];
11904
+ hierarchy.lvl1 = doc['hierarchy.lvl1'];
11905
+ hierarchy.lvl2 = doc['hierarchy.lvl2'];
11906
+ hierarchy.lvl3 = doc['hierarchy.lvl3'];
11907
+ hierarchy.lvl4 = doc['hierarchy.lvl4'];
11908
+ hierarchy.lvl5 = doc['hierarchy.lvl5'];
11909
+ }
11910
+ return hierarchy;
11911
+ }
11832
11912
  function transformResults(results) {
11913
+ const stripMark = (s) => typeof s === 'string' ? s.replace(/<\/?mark>/g, '') : (s ? String(s) : '');
11833
11914
  return results.map((result) => {
11834
- const url = result.url || result.route || result.link || '';
11835
- const title = result.title || result.name || '';
11836
- const content = result.content || result.description || result.snippet || '';
11837
- const description = result.description || result.content?.substring?.(0, 150) || '';
11838
- const hierarchy = {};
11839
- if (result.hierarchy) {
11840
- hierarchy.lvl0 = result.hierarchy.lvl0;
11841
- hierarchy.lvl1 = result.hierarchy.lvl1;
11842
- hierarchy.lvl2 = result.hierarchy.lvl2;
11843
- }
11844
- else {
11845
- if (result.category)
11846
- hierarchy.lvl0 = result.category;
11847
- if (result.parent_title || result.parentTitle)
11848
- hierarchy.lvl1 = result.parent_title || result.parentTitle;
11849
- }
11915
+ const doc = result?.document ?? result;
11916
+ const hierarchy = getHierarchy(doc);
11917
+ const url = doc.url || result.url || doc.route || result.route || doc.link || result.link || '';
11918
+ const rawTitle = doc.title || result.title || doc.name || result.name || hierarchy?.lvl3 || hierarchy?.lvl2 || hierarchy?.lvl1 || hierarchy?.lvl0 || '';
11919
+ const content = doc.content ?? result.content ?? doc.description ?? result.description ?? doc.snippet ?? result.snippet ?? '';
11920
+ const description = doc.description ?? result.description ?? (typeof content === 'string' ? content.substring(0, 150) : '');
11921
+ const plainTitle = stripMark(rawTitle) || stripMark(hierarchy?.lvl3) || stripMark(hierarchy?.lvl2) || stripMark(hierarchy?.lvl1) || stripMark(hierarchy?.lvl0) || 'Untitled';
11922
+ const plainContent = stripMark(content)?.substring(0, 200) || content;
11923
+ const apiHighlight = result?.highlight ?? doc?.highlight;
11924
+ const highlightTitle = typeof apiHighlight?.title === 'string' ? apiHighlight.title : undefined;
11925
+ const highlightContent = typeof apiHighlight?.content === 'string' ? apiHighlight.content : undefined;
11850
11926
  return {
11851
11927
  url,
11852
- title: title?.replace?.(/<\/?mark>/g, '') || title,
11853
- content: content?.replace?.(/<\/?mark>/g, '')?.substring?.(0, 200) || content,
11854
- description: description?.replace?.(/<\/?mark>/g, '') || description,
11855
- category: result.category || hierarchy.lvl0 || '',
11928
+ title: plainTitle,
11929
+ content: plainContent,
11930
+ description: stripMark(description) || description,
11931
+ category: doc.category ?? result.category ?? hierarchy?.lvl0 ?? '',
11856
11932
  hierarchy,
11857
- route: result.route,
11858
- parentTitle: result.parent_title || result.parentTitle,
11859
- type: result.type || '',
11860
- anchor: result.anchor || '',
11933
+ route: doc.route ?? result.route,
11934
+ parentTitle: doc.parent_title ?? doc.parentTitle ?? result.parent_title ?? result.parentTitle,
11935
+ type: doc.type ?? result.type ?? '',
11936
+ anchor: doc.anchor ?? result.anchor ?? '',
11861
11937
  _source: 'seekora',
11938
+ highlight: (highlightTitle ?? highlightContent) ? { title: highlightTitle, content: highlightContent } : undefined,
11862
11939
  };
11863
11940
  });
11864
11941
  }
11865
11942
  function useSeekoraSearch$1(options) {
11866
- const { storeId, storeSecret, apiEndpoint, maxResults = 20, debounceMs = 200, analyticsTags = ['docsearch'], groupField, groupSize, } = options;
11943
+ const { storeId, storeSecret, apiEndpoint, maxResults = 20, debounceMs = 200, analyticsTags = ['docsearch'], groupField = 'hierarchy.lvl3', groupSize = 10, } = options;
11867
11944
  const [query, setQueryState] = useState('');
11868
11945
  const [suggestions, setSuggestions] = useState([]);
11869
11946
  const [isLoading, setIsLoading] = useState(false);
@@ -11887,7 +11964,9 @@ function useSeekoraSearch$1(options) {
11887
11964
  config.environment = apiEndpoint;
11888
11965
  }
11889
11966
  else {
11890
- config.baseUrl = apiEndpoint;
11967
+ // Search SDK path is /v1/search; backend serves at /api/v1/search. baseUrl must include /api.
11968
+ const base = apiEndpoint.replace(/\/$/, '');
11969
+ config.baseUrl = base.endsWith('/api') ? base : `${base}/api`;
11891
11970
  }
11892
11971
  }
11893
11972
  try {
@@ -12015,8 +12094,9 @@ function useSeekoraSearch$1(options) {
12015
12094
  };
12016
12095
  }
12017
12096
 
12018
- function DocSearch({ storeId, storeSecret, seekoraApiEndpoint, apiEndpoint, apiKey, sources, placeholder = 'Search documentation...', maxResults = 10, debounceMs = 200, onSelect, onClose, translations = {}, renderButton = true, buttonComponent: ButtonComponent = DocSearchButton, initialOpen = false, disableShortcut = false, shortcutKey = 'k', }) {
12097
+ function DocSearch({ storeId, storeSecret, seekoraApiEndpoint, apiEndpoint, apiKey, sources, placeholder = 'Search documentation...', maxResults = 10, debounceMs = 200, onSelect, onClose, translations = {}, renderButton = true, buttonComponent: ButtonComponent = DocSearchButton, initialOpen = false, disableShortcut = false, shortcutKey = 'k', processGroupedResults, }) {
12019
12098
  const [isOpen, setIsOpen] = useState(initialOpen);
12099
+ const scrollSelectionIntoViewRef = useRef(false);
12020
12100
  const useSeekoraSDK = !!storeId;
12021
12101
  const seekoraSearch = useSeekoraSearch$1({
12022
12102
  storeId: storeId || '',
@@ -12032,6 +12112,7 @@ function DocSearch({ storeId, storeSecret, seekoraApiEndpoint, apiEndpoint, apiK
12032
12112
  sources,
12033
12113
  maxResults,
12034
12114
  debounceMs,
12115
+ processGroupedResults,
12035
12116
  });
12036
12117
  const { query, suggestions, isLoading, error, selectedIndex, setQuery, selectNext, selectPrev, setSelectedIndex, reset, getSelectedItem, } = useSeekoraSDK ? seekoraSearch : legacySearch;
12037
12118
  const groupedSuggestions = useSeekoraSDK ? undefined : legacySearch.groupedSuggestions;
@@ -12063,12 +12144,20 @@ function DocSearch({ storeId, storeSecret, seekoraApiEndpoint, apiEndpoint, apiK
12063
12144
  if (selectedItem)
12064
12145
  handleSelect(selectedItem);
12065
12146
  }, [getSelectedItem, handleSelect]);
12147
+ const handleSelectNext = useCallback(() => {
12148
+ scrollSelectionIntoViewRef.current = true;
12149
+ selectNext();
12150
+ }, [selectNext]);
12151
+ const handleSelectPrev = useCallback(() => {
12152
+ scrollSelectionIntoViewRef.current = true;
12153
+ selectPrev();
12154
+ }, [selectPrev]);
12066
12155
  const { handleModalKeyDown } = useKeyboard({
12067
12156
  isOpen,
12068
12157
  onOpen: handleOpen,
12069
12158
  onClose: handleClose,
12070
- onSelectNext: selectNext,
12071
- onSelectPrev: selectPrev,
12159
+ onSelectNext: handleSelectNext,
12160
+ onSelectPrev: handleSelectPrev,
12072
12161
  onEnter: handleEnter,
12073
12162
  disableShortcut,
12074
12163
  shortcutKey,
@@ -12084,7 +12173,7 @@ function DocSearch({ storeId, storeSecret, seekoraApiEndpoint, apiEndpoint, apiK
12084
12173
  React.createElement("button", { type: "button", className: "seekora-docsearch-close", onClick: handleClose, "aria-label": "Close search" },
12085
12174
  React.createElement("span", { className: "seekora-docsearch-close-text" }, "esc"))),
12086
12175
  React.createElement("div", { className: "seekora-docsearch-body" },
12087
- React.createElement(Results, { hits: displayHits, groupedHits: groupedSuggestions, selectedIndex: selectedIndex, onSelect: handleSelect, onHover: setSelectedIndex, query: query, isLoading: isLoading, error: error, translations: translations, sources: searchSources })),
12176
+ React.createElement(Results, { hits: displayHits, groupedHits: groupedSuggestions, selectedIndex: selectedIndex, onSelect: handleSelect, onHover: setSelectedIndex, scrollSelectionIntoViewRef: scrollSelectionIntoViewRef, query: query, isLoading: isLoading, error: error, translations: translations, sources: searchSources })),
12088
12177
  React.createElement(Footer, { translations: translations })))));
12089
12178
  }
12090
12179