@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.
- package/dist/components/FederatedDropdown.d.ts +6 -0
- package/dist/components/FederatedDropdown.d.ts.map +1 -1
- package/dist/components/FederatedDropdown.js +4 -2
- package/dist/components/SearchBarWithSuggestions.d.ts +4 -0
- package/dist/components/SearchBarWithSuggestions.d.ts.map +1 -1
- package/dist/components/SearchBarWithSuggestions.js +2 -2
- package/dist/docsearch/components/DocSearch.d.ts +1 -1
- package/dist/docsearch/components/DocSearch.d.ts.map +1 -1
- package/dist/docsearch/components/DocSearch.js +15 -5
- package/dist/docsearch/components/Results.d.ts +3 -1
- package/dist/docsearch/components/Results.d.ts.map +1 -1
- package/dist/docsearch/components/Results.js +66 -12
- package/dist/docsearch/hooks/useDocSearch.d.ts +1 -0
- package/dist/docsearch/hooks/useDocSearch.d.ts.map +1 -1
- package/dist/docsearch/hooks/useDocSearch.js +6 -3
- package/dist/docsearch/hooks/useSeekoraSearch.d.ts.map +1 -1
- package/dist/docsearch/hooks/useSeekoraSearch.js +46 -26
- package/dist/docsearch/types.d.ts +2 -0
- package/dist/docsearch/types.d.ts.map +1 -1
- package/dist/docsearch.css +1 -1
- package/dist/hooks/useQuerySuggestionsEnhanced.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/src/index.d.ts +14 -1
- package/dist/src/index.esm.js +140 -51
- package/dist/src/index.esm.js.map +1 -1
- package/dist/src/index.js +140 -51
- package/dist/src/index.js.map +1 -1
- package/package.json +5 -4
- package/src/docsearch/docsearch.css +237 -0
package/dist/src/index.d.ts
CHANGED
|
@@ -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[];
|
package/dist/src/index.esm.js
CHANGED
|
@@ -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 =
|
|
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:
|
|
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
|
|
11443
|
-
if (!groups.has(
|
|
11444
|
-
groups.set(
|
|
11445
|
-
groups.get(
|
|
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 [
|
|
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
|
-
|
|
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
|
|
11480
|
-
|
|
11481
|
-
|
|
11482
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
11835
|
-
const
|
|
11836
|
-
const
|
|
11837
|
-
const
|
|
11838
|
-
const
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
|
|
11843
|
-
|
|
11844
|
-
|
|
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:
|
|
11853
|
-
content:
|
|
11854
|
-
description: description
|
|
11855
|
-
category: result.category
|
|
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
|
|
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
|
-
|
|
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:
|
|
12071
|
-
onSelectPrev:
|
|
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
|
|