@seekora-ai/ui-sdk-react 0.2.12 → 0.2.14
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 +22 -2
- package/dist/components/CurrentRefinements.d.ts.map +1 -1
- package/dist/components/CurrentRefinements.js +199 -47
- package/dist/components/Facets.d.ts +30 -1
- package/dist/components/Facets.d.ts.map +1 -1
- package/dist/components/Facets.js +418 -46
- package/dist/components/HierarchicalMenu.d.ts.map +1 -1
- package/dist/components/HierarchicalMenu.js +112 -4
- package/dist/components/InfiniteHits.d.ts +2 -0
- package/dist/components/InfiniteHits.d.ts.map +1 -1
- package/dist/components/InfiniteHits.js +6 -3
- package/dist/components/Pagination.d.ts +47 -1
- package/dist/components/Pagination.d.ts.map +1 -1
- package/dist/components/Pagination.js +166 -28
- package/dist/components/QuerySuggestions.d.ts +2 -0
- package/dist/components/QuerySuggestions.d.ts.map +1 -1
- package/dist/components/QuerySuggestions.js +4 -3
- package/dist/components/QuerySuggestionsDropdown.d.ts +1 -1
- package/dist/components/QuerySuggestionsDropdown.d.ts.map +1 -1
- package/dist/components/QuerySuggestionsDropdown.js +4 -4
- package/dist/components/RangeSlider.d.ts.map +1 -1
- package/dist/components/RangeSlider.js +49 -2
- package/dist/components/Recommendations.d.ts +6 -0
- package/dist/components/Recommendations.d.ts.map +1 -1
- package/dist/components/Recommendations.js +12 -6
- package/dist/components/RichQuerySuggestions.d.ts +11 -0
- package/dist/components/RichQuerySuggestions.d.ts.map +1 -1
- package/dist/components/RichQuerySuggestions.js +2 -3
- package/dist/components/SearchBar.d.ts +18 -0
- package/dist/components/SearchBar.d.ts.map +1 -1
- package/dist/components/SearchBar.js +134 -24
- package/dist/components/SearchProvider.d.ts +8 -1
- package/dist/components/SearchProvider.d.ts.map +1 -1
- package/dist/components/SearchProvider.js +16 -4
- package/dist/components/SearchResults.d.ts +12 -0
- package/dist/components/SearchResults.d.ts.map +1 -1
- package/dist/components/SearchResults.js +11 -5
- package/dist/components/SortBy.d.ts +44 -4
- package/dist/components/SortBy.d.ts.map +1 -1
- package/dist/components/SortBy.js +154 -29
- package/dist/components/Stats.d.ts +14 -0
- package/dist/components/Stats.d.ts.map +1 -1
- package/dist/components/Stats.js +172 -23
- package/dist/components/section-primitives/SectionItemGrid.d.ts +3 -1
- package/dist/components/section-primitives/SectionItemGrid.d.ts.map +1 -1
- package/dist/components/section-primitives/SectionItemGrid.js +3 -2
- package/dist/components/suggestions/AmazonDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/AmazonDropdown.js +4 -6
- package/dist/components/suggestions/GoogleDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/GoogleDropdown.js +4 -8
- package/dist/components/suggestions/MinimalDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/MinimalDropdown.js +4 -6
- package/dist/components/suggestions/MobileSheetDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/MobileSheetDropdown.js +4 -6
- package/dist/components/suggestions/PinterestDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/PinterestDropdown.js +4 -8
- package/dist/components/suggestions/ShopifyDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/ShopifyDropdown.js +4 -6
- package/dist/components/suggestions/SpotlightDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/SpotlightDropdown.js +4 -6
- package/dist/components/suggestions/SuggestionSearchBar.d.ts.map +1 -1
- package/dist/components/suggestions/SuggestionSearchBar.js +1 -0
- package/dist/components/suggestions/types.d.ts +2 -0
- package/dist/components/suggestions/types.d.ts.map +1 -1
- package/dist/components/suggestions/utils.d.ts +10 -1
- package/dist/components/suggestions/utils.d.ts.map +1 -1
- package/dist/components/suggestions/utils.js +36 -0
- package/dist/components/suggestions-primitives/SuggestionList.d.ts +8 -1
- package/dist/components/suggestions-primitives/SuggestionList.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/SuggestionList.js +7 -4
- package/dist/components/suggestions-primitives/SuggestionsDropdownComposition.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/SuggestionsDropdownComposition.js +0 -2
- package/dist/components/suggestions-primitives/highlightMarkup.d.ts +16 -4
- package/dist/components/suggestions-primitives/highlightMarkup.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/highlightMarkup.js +42 -4
- 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 +6 -2
- package/dist/hooks/useClickTracking.d.ts +36 -0
- package/dist/hooks/useClickTracking.d.ts.map +1 -0
- package/dist/hooks/useClickTracking.js +96 -0
- package/dist/hooks/useExperiment.d.ts +25 -0
- package/dist/hooks/useExperiment.d.ts.map +1 -0
- package/dist/hooks/useExperiment.js +146 -0
- package/dist/hooks/useKeyboardNavigation.d.ts +51 -0
- package/dist/hooks/useKeyboardNavigation.d.ts.map +1 -0
- package/dist/hooks/useKeyboardNavigation.js +113 -0
- package/dist/hooks/useQuerySuggestions.d.ts.map +1 -1
- package/dist/hooks/useQuerySuggestions.js +19 -3
- package/dist/hooks/useQuerySuggestionsEnhanced.d.ts.map +1 -1
- package/dist/hooks/useQuerySuggestionsEnhanced.js +25 -7
- package/dist/hooks/useSuggestionsAnalytics.d.ts.map +1 -1
- package/dist/hooks/useSuggestionsAnalytics.js +6 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/src/index.d.ts +249 -19
- package/dist/src/index.esm.js +1659 -305
- package/dist/src/index.esm.js.map +1 -1
- package/dist/src/index.js +1658 -304
- package/dist/src/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Displays a hierarchical menu for nested category navigation
|
|
5
5
|
* Example: Electronics > Phones > iPhone
|
|
6
6
|
*/
|
|
7
|
-
import React, { useState, useMemo, useCallback } from 'react';
|
|
7
|
+
import React, { useState, useMemo, useCallback, useRef } from 'react';
|
|
8
8
|
import { useSearchContext } from './SearchProvider';
|
|
9
9
|
import { useSearchState } from '../hooks/useSearchState';
|
|
10
10
|
import { clsx } from 'clsx';
|
|
@@ -113,6 +113,112 @@ export const HierarchicalMenu = ({ attributes, separator = ' > ', limit = 10, sh
|
|
|
113
113
|
const toggleShowMore = (level) => {
|
|
114
114
|
setExpanded(prev => ({ ...prev, [level]: !prev[level] }));
|
|
115
115
|
};
|
|
116
|
+
const containerRef = useRef(null);
|
|
117
|
+
// Collect all visible treeitem elements within the container
|
|
118
|
+
const getVisibleTreeItems = useCallback(() => {
|
|
119
|
+
if (!containerRef.current)
|
|
120
|
+
return [];
|
|
121
|
+
return Array.from(containerRef.current.querySelectorAll('[role="treeitem"]'));
|
|
122
|
+
}, []);
|
|
123
|
+
// Keyboard navigation handler for the tree
|
|
124
|
+
const handleKeyDown = useCallback((e) => {
|
|
125
|
+
const items = getVisibleTreeItems();
|
|
126
|
+
if (items.length === 0)
|
|
127
|
+
return;
|
|
128
|
+
const activeElement = document.activeElement;
|
|
129
|
+
// Find the treeitem that is or contains the active element
|
|
130
|
+
const currentItem = items.find(item => item === activeElement || item.contains(activeElement));
|
|
131
|
+
const currentIndex = currentItem ? items.indexOf(currentItem) : -1;
|
|
132
|
+
switch (e.key) {
|
|
133
|
+
case 'ArrowDown': {
|
|
134
|
+
e.preventDefault();
|
|
135
|
+
const nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;
|
|
136
|
+
const button = items[nextIndex].querySelector('button');
|
|
137
|
+
if (button)
|
|
138
|
+
button.focus();
|
|
139
|
+
else
|
|
140
|
+
items[nextIndex].focus();
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
case 'ArrowUp': {
|
|
144
|
+
e.preventDefault();
|
|
145
|
+
const prevIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;
|
|
146
|
+
const button = items[prevIndex].querySelector('button');
|
|
147
|
+
if (button)
|
|
148
|
+
button.focus();
|
|
149
|
+
else
|
|
150
|
+
items[prevIndex].focus();
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
case 'ArrowRight': {
|
|
154
|
+
if (!currentItem)
|
|
155
|
+
break;
|
|
156
|
+
const isExpanded = currentItem.getAttribute('aria-expanded');
|
|
157
|
+
if (isExpanded === 'false') {
|
|
158
|
+
// Expand (select) this item
|
|
159
|
+
e.preventDefault();
|
|
160
|
+
const button = currentItem.querySelector('button');
|
|
161
|
+
if (button)
|
|
162
|
+
button.click();
|
|
163
|
+
}
|
|
164
|
+
else if (isExpanded === 'true') {
|
|
165
|
+
// Move to first child
|
|
166
|
+
e.preventDefault();
|
|
167
|
+
const childList = currentItem.querySelector('[role="group"]');
|
|
168
|
+
if (childList) {
|
|
169
|
+
const firstChild = childList.querySelector('[role="treeitem"]');
|
|
170
|
+
if (firstChild) {
|
|
171
|
+
const button = firstChild.querySelector('button');
|
|
172
|
+
if (button)
|
|
173
|
+
button.focus();
|
|
174
|
+
else
|
|
175
|
+
firstChild.focus();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
case 'ArrowLeft': {
|
|
182
|
+
if (!currentItem)
|
|
183
|
+
break;
|
|
184
|
+
const isExpanded = currentItem.getAttribute('aria-expanded');
|
|
185
|
+
if (isExpanded === 'true') {
|
|
186
|
+
// Collapse this item
|
|
187
|
+
e.preventDefault();
|
|
188
|
+
const button = currentItem.querySelector('button');
|
|
189
|
+
if (button)
|
|
190
|
+
button.click();
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
// Move focus to parent treeitem
|
|
194
|
+
e.preventDefault();
|
|
195
|
+
const parentGroup = currentItem.closest('[role="group"]');
|
|
196
|
+
if (parentGroup) {
|
|
197
|
+
const parentItem = parentGroup.closest('[role="treeitem"]');
|
|
198
|
+
if (parentItem) {
|
|
199
|
+
const button = parentItem.querySelector(':scope > button');
|
|
200
|
+
if (button)
|
|
201
|
+
button.focus();
|
|
202
|
+
else
|
|
203
|
+
parentItem.focus();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
case 'Enter': {
|
|
210
|
+
if (!currentItem)
|
|
211
|
+
break;
|
|
212
|
+
e.preventDefault();
|
|
213
|
+
const button = currentItem.querySelector('button');
|
|
214
|
+
if (button)
|
|
215
|
+
button.click();
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
default:
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}, [getVisibleTreeItems]);
|
|
116
222
|
// Render a level of the hierarchy
|
|
117
223
|
const renderLevel = (items, level) => {
|
|
118
224
|
if (!items || items.length === 0)
|
|
@@ -121,12 +227,14 @@ export const HierarchicalMenu = ({ attributes, separator = ' > ', limit = 10, sh
|
|
|
121
227
|
const displayLimit = isExpanded ? showMoreLimit : limit;
|
|
122
228
|
const displayItems = items.slice(0, displayLimit);
|
|
123
229
|
const hasMore = items.length > displayLimit;
|
|
124
|
-
return (React.createElement("ul", { className: hierarchicalTheme.list, style: {
|
|
230
|
+
return (React.createElement("ul", { role: level === 0 ? 'tree' : 'group', className: hierarchicalTheme.list, style: {
|
|
125
231
|
listStyle: 'none',
|
|
126
232
|
margin: 0,
|
|
127
233
|
padding: level > 0 ? `0 0 0 ${theme.spacing.medium}` : 0,
|
|
128
234
|
} },
|
|
129
|
-
displayItems.map((item, index) => (React.createElement("li", { key: item.value,
|
|
235
|
+
displayItems.map((item, index) => (React.createElement("li", { key: item.value, role: "treeitem", ...(item.data && item.data.length > 0
|
|
236
|
+
? { 'aria-expanded': !!item.isRefined }
|
|
237
|
+
: {}), className: clsx(hierarchicalTheme.item, item.isRefined && hierarchicalTheme.itemSelected, item.data && item.data.length > 0 && hierarchicalTheme.itemParent), style: {
|
|
130
238
|
padding: `${theme.spacing.small} 0`,
|
|
131
239
|
} }, renderItem ? (renderItem(item, level)) : (React.createElement(React.Fragment, null,
|
|
132
240
|
React.createElement("button", { type: "button", onClick: () => handleItemClick(item, level), className: hierarchicalTheme.link, style: {
|
|
@@ -164,5 +272,5 @@ export const HierarchicalMenu = ({ attributes, separator = ' > ', limit = 10, sh
|
|
|
164
272
|
if (processedItems.length === 0) {
|
|
165
273
|
return null;
|
|
166
274
|
}
|
|
167
|
-
return (React.createElement("div", { className: clsx(hierarchicalTheme.root, className), style: style }, renderLevel(processedItems, 0)));
|
|
275
|
+
return (React.createElement("div", { ref: containerRef, className: clsx(hierarchicalTheme.root, className), style: style, tabIndex: 0, onKeyDown: handleKeyDown }, renderLevel(processedItems, 0)));
|
|
168
276
|
};
|
|
@@ -21,6 +21,8 @@ export interface InfiniteHitsProps {
|
|
|
21
21
|
renderHit?: (hit: ResultItem, index: number) => React.ReactNode;
|
|
22
22
|
/** Custom render for empty state */
|
|
23
23
|
renderEmpty?: () => React.ReactNode;
|
|
24
|
+
/** Show initial loading when fetching and no hits yet (default false: no loading screen) */
|
|
25
|
+
showInitialLoading?: boolean;
|
|
24
26
|
/** Custom render for loading state */
|
|
25
27
|
renderLoading?: () => React.ReactNode;
|
|
26
28
|
/** Custom render for "Show More" button */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InfiniteHits.d.ts","sourceRoot":"","sources":["../../src/components/InfiniteHits.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAA4D,MAAM,OAAO,CAAC;AAIjF,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAEzE,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAC;IAChE,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;IACpC,sCAAsC;IACtC,aAAa,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;IACtC,2CAA2C;IAC3C,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE;QACvB,SAAS,EAAE,OAAO,CAAC;QACnB,UAAU,EAAE,OAAO,CAAC;QACpB,OAAO,EAAE,MAAM,IAAI,CAAC;KACrB,KAAK,KAAK,CAAC,SAAS,CAAC;IACtB,6DAA6D;IAC7D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,wDAAwD;IACxD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kDAAkD;IAClD,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,kCAAkC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6BAA6B;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qCAAqC;IACrC,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,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,8DAA8D;IAC9D,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,
|
|
1
|
+
{"version":3,"file":"InfiniteHits.d.ts","sourceRoot":"","sources":["../../src/components/InfiniteHits.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAA4D,MAAM,OAAO,CAAC;AAIjF,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAEzE,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAC;IAChE,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;IACpC,4FAA4F;IAC5F,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,sCAAsC;IACtC,aAAa,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;IACtC,2CAA2C;IAC3C,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE;QACvB,SAAS,EAAE,OAAO,CAAC;QACnB,UAAU,EAAE,OAAO,CAAC;QACpB,OAAO,EAAE,MAAM,IAAI,CAAC;KACrB,KAAK,KAAK,CAAC,SAAS,CAAC;IACtB,6DAA6D;IAC7D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,wDAAwD;IACxD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kDAAkD;IAClD,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,kCAAkC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6BAA6B;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qCAAqC;IACrC,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,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,8DAA8D;IAC9D,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAwSpD,CAAC"}
|
|
@@ -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, renderLoading, renderShowMore, showMoreButton = true, useInfiniteScroll = false, scrollThreshold = 0.1, fieldMapping, showMoreLabel = 'Show more', loadingLabel = 'Loading...', onHitClick, className, style, theme: customTheme, syncWithState = true, }) => {
|
|
11
|
+
export const InfiniteHits = ({ renderHit, renderEmpty, showInitialLoading = false, renderLoading, renderShowMore, showMoreButton = true, useInfiniteScroll = false, scrollThreshold = 0.1, fieldMapping, showMoreLabel = 'Show more', loadingLabel = 'Loading...', 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 || {};
|
|
@@ -163,10 +163,13 @@ export const InfiniteHits = ({ renderHit, renderEmpty, renderLoading, renderShow
|
|
|
163
163
|
cursor: isLastPage || isLoadingMore ? 'not-allowed' : 'pointer',
|
|
164
164
|
transition: theme.transitions?.fast || '150ms ease-in-out',
|
|
165
165
|
} }, isLoadingMore ? loadingLabel : isLastPage ? 'No more results' : showMoreLabel));
|
|
166
|
-
// Initial loading state
|
|
167
|
-
if (loading && accumulatedHits.length === 0) {
|
|
166
|
+
// Initial loading state (only when showInitialLoading: default no loading screen)
|
|
167
|
+
if (loading && accumulatedHits.length === 0 && showInitialLoading) {
|
|
168
168
|
return (React.createElement("div", { className: clsx(infiniteHitsTheme.root, className), style: style }, renderLoading ? renderLoading() : defaultRenderLoading()));
|
|
169
169
|
}
|
|
170
|
+
if (loading && accumulatedHits.length === 0) {
|
|
171
|
+
return React.createElement("div", { className: clsx(infiniteHitsTheme.root, className), style: style });
|
|
172
|
+
}
|
|
170
173
|
// Empty state
|
|
171
174
|
if (!loading && accumulatedHits.length === 0) {
|
|
172
175
|
return (React.createElement("div", { className: clsx(infiniteHitsTheme.root, className), style: style }, renderEmpty ? renderEmpty() : defaultRenderEmpty()));
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pagination Component
|
|
3
3
|
*
|
|
4
|
-
* Displays pagination controls for search results
|
|
4
|
+
* Displays pagination controls for search results.
|
|
5
|
+
* Supports three display variants: numbered, load-more, and simple.
|
|
6
|
+
*
|
|
7
|
+
* CSS Variables (applied to the container element):
|
|
8
|
+
* --seekora-pagination-bg
|
|
9
|
+
* --seekora-pagination-color
|
|
10
|
+
* --seekora-pagination-active-bg
|
|
11
|
+
* --seekora-pagination-active-color
|
|
12
|
+
* --seekora-pagination-border
|
|
13
|
+
* --seekora-pagination-radius
|
|
5
14
|
*/
|
|
6
15
|
import React from 'react';
|
|
7
16
|
import type { SearchResponse } from '@seekora-ai/search-sdk';
|
|
@@ -13,6 +22,18 @@ export interface PaginationTheme {
|
|
|
13
22
|
itemDisabled?: string;
|
|
14
23
|
link?: string;
|
|
15
24
|
ellipsis?: string;
|
|
25
|
+
/** Class for the Load More button */
|
|
26
|
+
loadMoreButton?: string;
|
|
27
|
+
/** Class for the Load More descriptive text (e.g. "X remaining") */
|
|
28
|
+
loadMoreText?: string;
|
|
29
|
+
/** Class for the simple variant outer container */
|
|
30
|
+
simpleContainer?: string;
|
|
31
|
+
/** Class for the "Page X of Y" text in the simple variant */
|
|
32
|
+
simpleText?: string;
|
|
33
|
+
/** Class for Previous/Next buttons in the simple variant */
|
|
34
|
+
simpleButton?: string;
|
|
35
|
+
/** Class for the "Page X of Y" info text (usable in any variant) */
|
|
36
|
+
pageInfo?: string;
|
|
16
37
|
}
|
|
17
38
|
export interface PaginationProps {
|
|
18
39
|
/** Search results response */
|
|
@@ -39,6 +60,31 @@ export interface PaginationProps {
|
|
|
39
60
|
style?: React.CSSProperties;
|
|
40
61
|
/** Custom theme */
|
|
41
62
|
theme?: PaginationTheme;
|
|
63
|
+
/**
|
|
64
|
+
* Display variant.
|
|
65
|
+
* - 'numbered' (default): numbered page buttons with prev/next
|
|
66
|
+
* - 'load-more': single "Load More" button
|
|
67
|
+
* - 'simple': Previous / Next buttons with "Page X of Y" text
|
|
68
|
+
*/
|
|
69
|
+
variant?: 'numbered' | 'load-more' | 'simple';
|
|
70
|
+
/** Label for the Load More button (default: "Load More") */
|
|
71
|
+
loadMoreText?: string;
|
|
72
|
+
/**
|
|
73
|
+
* Button / text sizing.
|
|
74
|
+
* - 'small': compact
|
|
75
|
+
* - 'medium' (default): standard
|
|
76
|
+
* - 'large': spacious
|
|
77
|
+
*/
|
|
78
|
+
size?: 'small' | 'medium' | 'large';
|
|
79
|
+
/**
|
|
80
|
+
* Show "Page X of Y" text.
|
|
81
|
+
* Defaults to false for numbered, true for simple.
|
|
82
|
+
*/
|
|
83
|
+
showPageInfo?: boolean;
|
|
84
|
+
/** Label for the Previous button (default: "Previous") */
|
|
85
|
+
previousLabel?: string;
|
|
86
|
+
/** Label for the Next button (default: "Next") */
|
|
87
|
+
nextLabel?: string;
|
|
42
88
|
}
|
|
43
89
|
export declare const Pagination: React.FC<PaginationProps>;
|
|
44
90
|
//# sourceMappingURL=Pagination.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../src/components/Pagination.tsx"],"names":[],"mappings":"AAAA
|
|
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;AAE7D,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;AASD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAkbhD,CAAC"}
|
|
@@ -1,13 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pagination Component
|
|
3
3
|
*
|
|
4
|
-
* Displays pagination controls for search results
|
|
4
|
+
* Displays pagination controls for search results.
|
|
5
|
+
* Supports three display variants: numbered, load-more, and simple.
|
|
6
|
+
*
|
|
7
|
+
* CSS Variables (applied to the container element):
|
|
8
|
+
* --seekora-pagination-bg
|
|
9
|
+
* --seekora-pagination-color
|
|
10
|
+
* --seekora-pagination-active-bg
|
|
11
|
+
* --seekora-pagination-active-color
|
|
12
|
+
* --seekora-pagination-border
|
|
13
|
+
* --seekora-pagination-radius
|
|
5
14
|
*/
|
|
6
15
|
import React from 'react';
|
|
7
16
|
import { useSearchContext } from './SearchProvider';
|
|
8
17
|
import { useSearchState } from '../hooks/useSearchState';
|
|
9
18
|
import { clsx } from 'clsx';
|
|
10
|
-
|
|
19
|
+
/** Size-specific style tokens */
|
|
20
|
+
const SIZE_TOKENS = {
|
|
21
|
+
small: { paddingKey: 'small', fontSizeKey: 'small', minWidth: '32px' },
|
|
22
|
+
medium: { paddingKey: 'small', fontSizeKey: 'medium', minWidth: '40px' },
|
|
23
|
+
large: { paddingKey: 'medium', fontSizeKey: 'large', minWidth: '48px' },
|
|
24
|
+
};
|
|
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', }) => {
|
|
11
26
|
const { theme } = useSearchContext();
|
|
12
27
|
const { results: stateResults, currentPage: stateCurrentPage, setPage } = useSearchState();
|
|
13
28
|
const paginationTheme = customTheme || {};
|
|
@@ -30,6 +45,19 @@ export const Pagination = ({ results: resultsProp, currentPage: currentPageProp,
|
|
|
30
45
|
|| res?.data?.total_pages
|
|
31
46
|
|| res?.data?.data?.total_pages
|
|
32
47
|
|| Math.ceil(totalResults / itemsPerPage);
|
|
48
|
+
// Resolve whether to show page info text
|
|
49
|
+
const resolvedShowPageInfo = showPageInfo !== undefined
|
|
50
|
+
? showPageInfo
|
|
51
|
+
: variant === 'simple';
|
|
52
|
+
// Size tokens
|
|
53
|
+
const sizeTokens = SIZE_TOKENS[size];
|
|
54
|
+
// CSS variable aware helpers — allow overrides via custom properties
|
|
55
|
+
const cssVarBg = 'var(--seekora-pagination-bg, ' + theme.colors.background + ')';
|
|
56
|
+
const cssVarColor = 'var(--seekora-pagination-color, ' + theme.colors.text + ')';
|
|
57
|
+
const cssVarActiveBg = 'var(--seekora-pagination-active-bg, ' + theme.colors.primary + ')';
|
|
58
|
+
const cssVarActiveColor = 'var(--seekora-pagination-active-color, #fff)';
|
|
59
|
+
const cssVarBorder = 'var(--seekora-pagination-border, ' + theme.colors.border + ')';
|
|
60
|
+
const cssVarRadius = 'var(--seekora-pagination-radius, ' + (typeof theme.borderRadius === 'string' ? theme.borderRadius : theme.borderRadius.medium) + ')';
|
|
33
61
|
const handlePageChange = (page) => {
|
|
34
62
|
if (page < 1 || page > totalPages || page === currentPage)
|
|
35
63
|
return;
|
|
@@ -40,22 +68,26 @@ export const Pagination = ({ results: resultsProp, currentPage: currentPageProp,
|
|
|
40
68
|
onPageChange(page);
|
|
41
69
|
}
|
|
42
70
|
};
|
|
43
|
-
const defaultRenderPageButton = (page, isActive, isDisabled) => (React.createElement("button", { type: "button", disabled: isDisabled, onClick: () => !isDisabled && handlePageChange(page), className: clsx(paginationTheme.item, isActive && paginationTheme.itemActive, isDisabled && paginationTheme.itemDisabled), style: {
|
|
44
|
-
padding: theme.spacing.
|
|
71
|
+
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
|
+
padding: theme.spacing[sizeTokens.paddingKey],
|
|
45
73
|
margin: `0 ${theme.spacing.small}`,
|
|
46
|
-
border: `1px solid ${
|
|
47
|
-
borderRadius:
|
|
48
|
-
backgroundColor: isActive ?
|
|
49
|
-
color: isActive ?
|
|
74
|
+
border: `1px solid ${cssVarBorder}`,
|
|
75
|
+
borderRadius: cssVarRadius,
|
|
76
|
+
backgroundColor: isActive ? cssVarActiveBg : cssVarBg,
|
|
77
|
+
color: isActive ? cssVarActiveColor : cssVarColor,
|
|
50
78
|
cursor: isDisabled ? 'not-allowed' : 'pointer',
|
|
51
79
|
opacity: isDisabled ? 0.5 : 1,
|
|
52
|
-
fontSize: theme.typography.fontSize.
|
|
53
|
-
minWidth:
|
|
80
|
+
fontSize: theme.typography.fontSize[sizeTokens.fontSizeKey],
|
|
81
|
+
minWidth: sizeTokens.minWidth,
|
|
54
82
|
...(isActive && {
|
|
55
83
|
fontWeight: 'bold',
|
|
56
84
|
}),
|
|
57
85
|
} }, page));
|
|
58
|
-
if (totalPages <= 1) {
|
|
86
|
+
if (totalPages <= 1 && variant !== 'load-more') {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
// For load-more, hide when there are no more pages to load
|
|
90
|
+
if (variant === 'load-more' && currentPage >= totalPages) {
|
|
59
91
|
return null;
|
|
60
92
|
}
|
|
61
93
|
// Calculate page range to display
|
|
@@ -88,6 +120,94 @@ export const Pagination = ({ results: resultsProp, currentPage: currentPageProp,
|
|
|
88
120
|
}
|
|
89
121
|
return pages;
|
|
90
122
|
};
|
|
123
|
+
// ── Page info element ──────────────────────────────────────────────
|
|
124
|
+
const pageInfoElement = resolvedShowPageInfo ? (React.createElement("span", { className: clsx(paginationTheme.pageInfo), style: {
|
|
125
|
+
fontSize: theme.typography.fontSize[sizeTokens.fontSizeKey],
|
|
126
|
+
color: cssVarColor,
|
|
127
|
+
padding: `0 ${theme.spacing.small}`,
|
|
128
|
+
whiteSpace: 'nowrap',
|
|
129
|
+
}, "aria-live": "polite" },
|
|
130
|
+
"Page ",
|
|
131
|
+
currentPage,
|
|
132
|
+
" of ",
|
|
133
|
+
totalPages)) : null;
|
|
134
|
+
// ── Load More variant ──────────────────────────────────────────────
|
|
135
|
+
if (variant === 'load-more') {
|
|
136
|
+
const remaining = Math.max(0, totalResults - currentPage * itemsPerPage);
|
|
137
|
+
return (React.createElement("nav", { className: clsx(paginationTheme.container, className), style: {
|
|
138
|
+
display: 'flex',
|
|
139
|
+
flexDirection: 'column',
|
|
140
|
+
alignItems: 'center',
|
|
141
|
+
gap: theme.spacing.small,
|
|
142
|
+
...style,
|
|
143
|
+
}, "aria-label": "Pagination" },
|
|
144
|
+
React.createElement("button", { type: "button", onClick: () => handlePageChange(currentPage + 1), className: clsx(paginationTheme.loadMoreButton), style: {
|
|
145
|
+
padding: `${theme.spacing[sizeTokens.paddingKey]} ${theme.spacing.large}`,
|
|
146
|
+
border: 'none',
|
|
147
|
+
borderRadius: cssVarRadius,
|
|
148
|
+
backgroundColor: cssVarActiveBg,
|
|
149
|
+
color: cssVarActiveColor,
|
|
150
|
+
cursor: 'pointer',
|
|
151
|
+
fontSize: theme.typography.fontSize[sizeTokens.fontSizeKey],
|
|
152
|
+
fontWeight: theme.typography.fontWeight?.medium ?? 500,
|
|
153
|
+
transition: theme.transitions?.fast ?? '150ms ease-in-out',
|
|
154
|
+
minWidth: sizeTokens.minWidth,
|
|
155
|
+
}, "aria-label": remaining > 0 ? `${loadMoreText} (${remaining} remaining)` : loadMoreText },
|
|
156
|
+
loadMoreText,
|
|
157
|
+
remaining > 0 && (React.createElement("span", { className: clsx(paginationTheme.loadMoreText), style: {
|
|
158
|
+
marginLeft: theme.spacing.small,
|
|
159
|
+
opacity: 0.85,
|
|
160
|
+
fontSize: theme.typography.fontSize.small,
|
|
161
|
+
} },
|
|
162
|
+
"(",
|
|
163
|
+
remaining,
|
|
164
|
+
" remaining)"))),
|
|
165
|
+
pageInfoElement));
|
|
166
|
+
}
|
|
167
|
+
// ── Simple variant ─────────────────────────────────────────────────
|
|
168
|
+
if (variant === 'simple') {
|
|
169
|
+
return (React.createElement("nav", { className: clsx(paginationTheme.container, paginationTheme.simpleContainer, className), style: {
|
|
170
|
+
display: 'flex',
|
|
171
|
+
alignItems: 'center',
|
|
172
|
+
justifyContent: 'center',
|
|
173
|
+
gap: theme.spacing.medium,
|
|
174
|
+
...style,
|
|
175
|
+
}, "aria-label": "Pagination" },
|
|
176
|
+
React.createElement("button", { type: "button", disabled: currentPage === 1, onClick: () => handlePageChange(currentPage - 1), className: clsx(paginationTheme.simpleButton, currentPage === 1 && paginationTheme.itemDisabled), style: {
|
|
177
|
+
padding: `${theme.spacing[sizeTokens.paddingKey]} ${theme.spacing.medium}`,
|
|
178
|
+
border: `1px solid ${cssVarBorder}`,
|
|
179
|
+
borderRadius: cssVarRadius,
|
|
180
|
+
backgroundColor: cssVarBg,
|
|
181
|
+
color: cssVarColor,
|
|
182
|
+
cursor: currentPage === 1 ? 'not-allowed' : 'pointer',
|
|
183
|
+
opacity: currentPage === 1 ? 0.5 : 1,
|
|
184
|
+
fontSize: theme.typography.fontSize[sizeTokens.fontSizeKey],
|
|
185
|
+
minWidth: sizeTokens.minWidth,
|
|
186
|
+
transition: theme.transitions?.fast ?? '150ms ease-in-out',
|
|
187
|
+
}, "aria-label": "Previous page" }, previousLabel),
|
|
188
|
+
React.createElement("span", { className: clsx(paginationTheme.simpleText, paginationTheme.pageInfo), style: {
|
|
189
|
+
fontSize: theme.typography.fontSize[sizeTokens.fontSizeKey],
|
|
190
|
+
color: cssVarColor,
|
|
191
|
+
whiteSpace: 'nowrap',
|
|
192
|
+
}, "aria-live": "polite" },
|
|
193
|
+
"Page ",
|
|
194
|
+
currentPage,
|
|
195
|
+
" of ",
|
|
196
|
+
totalPages),
|
|
197
|
+
React.createElement("button", { type: "button", disabled: currentPage === totalPages, onClick: () => handlePageChange(currentPage + 1), className: clsx(paginationTheme.simpleButton, currentPage === totalPages && paginationTheme.itemDisabled), style: {
|
|
198
|
+
padding: `${theme.spacing[sizeTokens.paddingKey]} ${theme.spacing.medium}`,
|
|
199
|
+
border: `1px solid ${cssVarBorder}`,
|
|
200
|
+
borderRadius: cssVarRadius,
|
|
201
|
+
backgroundColor: cssVarBg,
|
|
202
|
+
color: cssVarColor,
|
|
203
|
+
cursor: currentPage === totalPages ? 'not-allowed' : 'pointer',
|
|
204
|
+
opacity: currentPage === totalPages ? 0.5 : 1,
|
|
205
|
+
fontSize: theme.typography.fontSize[sizeTokens.fontSizeKey],
|
|
206
|
+
minWidth: sizeTokens.minWidth,
|
|
207
|
+
transition: theme.transitions?.fast ?? '150ms ease-in-out',
|
|
208
|
+
}, "aria-label": "Next page" }, nextLabel)));
|
|
209
|
+
}
|
|
210
|
+
// ── Numbered variant (default — original behavior) ─────────────────
|
|
91
211
|
const pageNumbers = getPageNumbers();
|
|
92
212
|
return (React.createElement("nav", { className: clsx(paginationTheme.container, className), style: style, "aria-label": "Pagination" },
|
|
93
213
|
React.createElement("ul", { className: paginationTheme.list, style: {
|
|
@@ -98,27 +218,44 @@ export const Pagination = ({ results: resultsProp, currentPage: currentPageProp,
|
|
|
98
218
|
padding: 0,
|
|
99
219
|
margin: 0,
|
|
100
220
|
flexWrap: 'wrap',
|
|
221
|
+
}, tabIndex: 0, onKeyDown: (e) => {
|
|
222
|
+
if (e.key === 'ArrowLeft') {
|
|
223
|
+
e.preventDefault();
|
|
224
|
+
handlePageChange(currentPage - 1);
|
|
225
|
+
}
|
|
226
|
+
else if (e.key === 'ArrowRight') {
|
|
227
|
+
e.preventDefault();
|
|
228
|
+
handlePageChange(currentPage + 1);
|
|
229
|
+
}
|
|
230
|
+
else if (e.key === 'Home') {
|
|
231
|
+
e.preventDefault();
|
|
232
|
+
handlePageChange(1);
|
|
233
|
+
}
|
|
234
|
+
else if (e.key === 'End') {
|
|
235
|
+
e.preventDefault();
|
|
236
|
+
handlePageChange(totalPages);
|
|
237
|
+
}
|
|
101
238
|
} },
|
|
102
239
|
showPrevNext && (React.createElement("li", null,
|
|
103
240
|
React.createElement("button", { type: "button", disabled: currentPage === 1, onClick: () => handlePageChange(currentPage - 1), className: clsx(paginationTheme.item, currentPage === 1 && paginationTheme.itemDisabled), style: {
|
|
104
|
-
padding: theme.spacing.
|
|
241
|
+
padding: theme.spacing[sizeTokens.paddingKey],
|
|
105
242
|
margin: `0 ${theme.spacing.small}`,
|
|
106
|
-
border: `1px solid ${
|
|
107
|
-
borderRadius:
|
|
108
|
-
backgroundColor:
|
|
109
|
-
color:
|
|
243
|
+
border: `1px solid ${cssVarBorder}`,
|
|
244
|
+
borderRadius: cssVarRadius,
|
|
245
|
+
backgroundColor: cssVarBg,
|
|
246
|
+
color: cssVarColor,
|
|
110
247
|
cursor: currentPage === 1 ? 'not-allowed' : 'pointer',
|
|
111
248
|
opacity: currentPage === 1 ? 0.5 : 1,
|
|
112
|
-
fontSize: theme.typography.fontSize.
|
|
113
|
-
}, "aria-label": "Previous page" },
|
|
249
|
+
fontSize: theme.typography.fontSize[sizeTokens.fontSizeKey],
|
|
250
|
+
}, "aria-label": "Previous page" }, previousLabel))),
|
|
114
251
|
pageNumbers.map((page, index) => {
|
|
115
252
|
if (page === 'ellipsis') {
|
|
116
253
|
return (React.createElement("li", { key: `ellipsis-${index}` },
|
|
117
254
|
React.createElement("span", { className: paginationTheme.ellipsis, style: {
|
|
118
|
-
padding: theme.spacing.
|
|
255
|
+
padding: theme.spacing[sizeTokens.paddingKey],
|
|
119
256
|
margin: `0 ${theme.spacing.small}`,
|
|
120
|
-
color:
|
|
121
|
-
fontSize: theme.typography.fontSize.
|
|
257
|
+
color: cssVarColor,
|
|
258
|
+
fontSize: theme.typography.fontSize[sizeTokens.fontSizeKey],
|
|
122
259
|
} }, "...")));
|
|
123
260
|
}
|
|
124
261
|
const isActive = page === currentPage;
|
|
@@ -129,14 +266,15 @@ export const Pagination = ({ results: resultsProp, currentPage: currentPageProp,
|
|
|
129
266
|
}),
|
|
130
267
|
showPrevNext && (React.createElement("li", null,
|
|
131
268
|
React.createElement("button", { type: "button", disabled: currentPage === totalPages, onClick: () => handlePageChange(currentPage + 1), className: clsx(paginationTheme.item, currentPage === totalPages && paginationTheme.itemDisabled), style: {
|
|
132
|
-
padding: theme.spacing.
|
|
269
|
+
padding: theme.spacing[sizeTokens.paddingKey],
|
|
133
270
|
margin: `0 ${theme.spacing.small}`,
|
|
134
|
-
border: `1px solid ${
|
|
135
|
-
borderRadius:
|
|
136
|
-
backgroundColor:
|
|
137
|
-
color:
|
|
271
|
+
border: `1px solid ${cssVarBorder}`,
|
|
272
|
+
borderRadius: cssVarRadius,
|
|
273
|
+
backgroundColor: cssVarBg,
|
|
274
|
+
color: cssVarColor,
|
|
138
275
|
cursor: currentPage === totalPages ? 'not-allowed' : 'pointer',
|
|
139
276
|
opacity: currentPage === totalPages ? 0.5 : 1,
|
|
140
|
-
fontSize: theme.typography.fontSize.
|
|
141
|
-
}, "aria-label": "Next page" },
|
|
277
|
+
fontSize: theme.typography.fontSize[sizeTokens.fontSizeKey],
|
|
278
|
+
}, "aria-label": "Next page" }, nextLabel))),
|
|
279
|
+
resolvedShowPageInfo && (React.createElement("li", { style: { marginLeft: theme.spacing.small } }, pageInfoElement)))));
|
|
142
280
|
};
|
|
@@ -26,6 +26,8 @@ export interface QuerySuggestionsProps {
|
|
|
26
26
|
minQueryLength?: number;
|
|
27
27
|
onSuggestionClick?: (suggestion: string) => void;
|
|
28
28
|
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;
|
|
29
31
|
renderLoading?: () => React.ReactNode;
|
|
30
32
|
renderEmpty?: () => React.ReactNode;
|
|
31
33
|
showTitle?: boolean;
|
|
@@ -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,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,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,aAAa,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;IACtC,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,
|
|
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,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,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,2HAA2H;IAC3H,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;IACtC,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,CA0K5D,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, renderLoading, renderEmpty, showTitle = false, title = 'Suggestions', className, style, theme: customTheme, }) => {
|
|
10
|
+
export const QuerySuggestions = ({ query = '', maxSuggestions = 10, debounceMs = 300, minQueryLength = 2, onSuggestionClick, renderSuggestion, showLoadingState = false, renderLoading, 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({
|
|
@@ -44,7 +44,8 @@ export const QuerySuggestions = ({ query = '', maxSuggestions = 10, debounceMs =
|
|
|
44
44
|
if (query.length < minQueryLength) {
|
|
45
45
|
return null;
|
|
46
46
|
}
|
|
47
|
-
if (loading)
|
|
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) {
|
|
48
49
|
return (React.createElement("div", { className: clsx(suggestionsTheme.container, className), style: style },
|
|
49
50
|
showTitle && (React.createElement("div", { className: suggestionsTheme.title, style: {
|
|
50
51
|
fontSize: theme.typography.fontSize.large,
|
|
@@ -54,7 +55,7 @@ export const QuerySuggestions = ({ query = '', maxSuggestions = 10, debounceMs =
|
|
|
54
55
|
} }, title)),
|
|
55
56
|
renderLoading ? renderLoading() : defaultRenderLoading()));
|
|
56
57
|
}
|
|
57
|
-
if (error || displayedSuggestions.length === 0) {
|
|
58
|
+
if (error || (!loading && displayedSuggestions.length === 0)) {
|
|
58
59
|
return (React.createElement("div", { className: clsx(suggestionsTheme.container, className), style: style },
|
|
59
60
|
showTitle && (React.createElement("div", { className: suggestionsTheme.title, style: {
|
|
60
61
|
fontSize: theme.typography.fontSize.large,
|
|
@@ -28,7 +28,7 @@ export interface QuerySuggestionsDropdownProps extends QuerySuggestionsEventHand
|
|
|
28
28
|
maxRecentSearches?: number;
|
|
29
29
|
/** Show suggestion counts */
|
|
30
30
|
showCounts?: boolean;
|
|
31
|
-
/** Show loading state */
|
|
31
|
+
/** Show loading state (default false: show previous results until new results render) */
|
|
32
32
|
showLoading?: boolean;
|
|
33
33
|
/** Show empty state when no results */
|
|
34
34
|
showEmptyState?: boolean;
|
|
@@ -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;AAMlC,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,
|
|
1
|
+
{"version":3,"file":"QuerySuggestionsDropdown.d.ts","sourceRoot":"","sources":["../../src/components/QuerySuggestionsDropdown.tsx"],"names":[],"mappings":"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"}
|
|
@@ -158,7 +158,7 @@ const LoadingSpinner = ({ style }) => (React.createElement("svg", { style: { ani
|
|
|
158
158
|
// Component
|
|
159
159
|
// ============================================================================
|
|
160
160
|
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, showLoading =
|
|
161
|
+
const { query, isOpen = true, maxSuggestions = 8, minQueryLength = 1, debounceMs = 200, showRecentSearches = true, maxRecentSearches = 5, showCounts = true, showLoading = false, showEmptyState = true, highlight = { enabled: true, preTag: '<mark>', postTag: '</mark>' }, keyboardNav = { enabled: true }, animation = { enabled: true, duration: 150, entrance: 'fade' }, classNames = {}, style, renderSuggestion, renderRecentSearch, renderLoading, renderEmpty, footer, position = 'absolute', width = '100%', zIndex = 1000, closeOnClickOutside = true, closeOnEscape = true, ariaLabel = 'Search suggestions', onSuggestionSelect, onRecentSearchClick, onRecentSearchRemove, onOpen, onClose, onNavigate, } = props;
|
|
162
162
|
const { client, theme } = useSearchContext();
|
|
163
163
|
const containerRef = useRef(null);
|
|
164
164
|
const [activeIndex, setActiveIndex] = useState(-1);
|
|
@@ -343,7 +343,7 @@ export const QuerySuggestionsDropdown = forwardRef(function QuerySuggestionsDrop
|
|
|
343
343
|
loading && showLoading && (React.createElement("div", { className: classNames.loadingState, style: defaultStyles.loadingState }, renderLoading ? renderLoading() : (React.createElement(React.Fragment, null,
|
|
344
344
|
React.createElement(LoadingSpinner, null),
|
|
345
345
|
React.createElement("span", null, "Searching..."))))),
|
|
346
|
-
|
|
346
|
+
showRecent && (React.createElement("div", { className: clsx('seekora-suggestions-section', classNames.section, classNames.recentSearches) },
|
|
347
347
|
React.createElement("div", { className: classNames.sectionTitle, style: defaultStyles.sectionTitle }, "Recent Searches"),
|
|
348
348
|
recentSearches.slice(0, maxRecentSearches).map((search, index) => {
|
|
349
349
|
const isActive = activeIndex === index;
|
|
@@ -351,8 +351,8 @@ export const QuerySuggestionsDropdown = forwardRef(function QuerySuggestionsDrop
|
|
|
351
351
|
onRecentSearchClick?.(search);
|
|
352
352
|
}, onMouseEnter: () => setActiveIndex(index) }, renderRecentSearchItem(search, index, isActive)));
|
|
353
353
|
}))),
|
|
354
|
-
|
|
355
|
-
|
|
354
|
+
showRecent && showSuggestions && (React.createElement("div", { style: defaultStyles.divider })),
|
|
355
|
+
showSuggestions && (React.createElement("div", { className: clsx('seekora-suggestions-section', classNames.section, classNames.suggestionsList) },
|
|
356
356
|
query.length > 0 && (React.createElement("div", { className: classNames.sectionTitle, style: defaultStyles.sectionTitle }, "Suggestions")),
|
|
357
357
|
suggestions.map((suggestion, index) => {
|
|
358
358
|
const itemIndex = showRecent ? recentSearches.length + index : index;
|