@seekora-ai/ui-sdk-react 0.2.13 → 0.2.15
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 +259 -47
- package/dist/components/FacetDropdown.d.ts +92 -0
- package/dist/components/FacetDropdown.d.ts.map +1 -0
- package/dist/components/FacetDropdown.js +374 -0
- package/dist/components/Facets.d.ts +56 -1
- package/dist/components/Facets.d.ts.map +1 -1
- package/dist/components/Facets.js +602 -41
- package/dist/components/FederatedDropdown.d.ts.map +1 -1
- package/dist/components/FederatedDropdown.js +45 -31
- package/dist/components/HierarchicalMenu.d.ts.map +1 -1
- package/dist/components/HierarchicalMenu.js +112 -4
- 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/QuerySuggestionsDropdown.d.ts.map +1 -1
- package/dist/components/QuerySuggestionsDropdown.js +32 -18
- package/dist/components/RangeInput.d.ts.map +1 -1
- package/dist/components/RangeInput.js +6 -6
- package/dist/components/RangeSlider.d.ts.map +1 -1
- package/dist/components/RangeSlider.js +101 -32
- package/dist/components/RichQuerySuggestions.d.ts +7 -0
- package/dist/components/RichQuerySuggestions.d.ts.map +1 -1
- package/dist/components/RichQuerySuggestions.js +40 -26
- package/dist/components/SearchBar.d.ts +16 -0
- package/dist/components/SearchBar.d.ts.map +1 -1
- package/dist/components/SearchBar.js +139 -17
- package/dist/components/SearchBarWithSuggestions.js +3 -3
- package/dist/components/SearchLayout.d.ts.map +1 -1
- package/dist/components/SearchLayout.js +10 -1
- 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 +10 -0
- package/dist/components/SearchResults.d.ts.map +1 -1
- package/dist/components/SearchResults.js +46 -30
- 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/primitives/ActionButtons.d.ts.map +1 -1
- package/dist/components/primitives/ActionButtons.js +34 -10
- package/dist/components/primitives/BadgeList.d.ts.map +1 -1
- package/dist/components/primitives/BadgeList.js +33 -13
- package/dist/components/primitives/ImageDisplay.d.ts.map +1 -1
- package/dist/components/primitives/ImageDisplay.js +11 -8
- package/dist/components/primitives/ImageZoom.js +26 -26
- package/dist/components/primitives/VariantSelector.js +10 -10
- package/dist/components/primitives/VariantSwatches.js +3 -3
- package/dist/components/product-page/ProductGallery.d.ts +8 -1
- package/dist/components/product-page/ProductGallery.d.ts.map +1 -1
- package/dist/components/product-page/ProductGallery.js +2 -2
- package/dist/components/section-primitives/SectionSearchProvider.d.ts +3 -1
- package/dist/components/section-primitives/SectionSearchProvider.d.ts.map +1 -1
- package/dist/components/section-primitives/SectionSearchProvider.js +3 -2
- package/dist/components/suggestions/AmazonDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/AmazonDropdown.js +2 -4
- package/dist/components/suggestions/GoogleDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/GoogleDropdown.js +2 -6
- package/dist/components/suggestions/MinimalDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/MinimalDropdown.js +2 -4
- package/dist/components/suggestions/MobileSheetDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/MobileSheetDropdown.js +20 -22
- package/dist/components/suggestions/PinterestDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/PinterestDropdown.js +2 -6
- package/dist/components/suggestions/ShopifyDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/ShopifyDropdown.js +39 -41
- package/dist/components/suggestions/SpotlightDropdown.d.ts.map +1 -1
- package/dist/components/suggestions/SpotlightDropdown.js +2 -4
- 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/DropdownPanel.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/DropdownPanel.js +15 -2
- package/dist/components/suggestions-primitives/ItemCard.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/ItemCard.js +21 -8
- package/dist/components/suggestions-primitives/ItemGrid.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/ItemGrid.js +9 -3
- package/dist/components/suggestions-primitives/ProductCard.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/ProductCard.js +25 -10
- package/dist/components/suggestions-primitives/ProductCardLayouts.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/ProductCardLayouts.js +24 -12
- package/dist/components/suggestions-primitives/SearchInput.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/SearchInput.js +28 -9
- package/dist/components/suggestions-primitives/SuggestionItem.d.ts.map +1 -1
- package/dist/components/suggestions-primitives/SuggestionItem.js +3 -0
- 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/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 +23 -6
- package/dist/hooks/useSuggestionsAnalytics.d.ts.map +1 -1
- package/dist/hooks/useSuggestionsAnalytics.js +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.umd.js +1 -1
- package/dist/src/index.d.ts +345 -19
- package/dist/src/index.esm.js +2869 -787
- package/dist/src/index.esm.js.map +1 -1
- package/dist/src/index.js +2868 -785
- package/dist/src/index.js.map +1 -1
- package/package.json +6 -6
|
@@ -4,18 +4,29 @@
|
|
|
4
4
|
* Visual slider for numeric range filtering
|
|
5
5
|
* Alternative to RangeInput for a more interactive UX
|
|
6
6
|
*/
|
|
7
|
-
import React, { useState, useCallback, useEffect,
|
|
7
|
+
import React, { useState, useCallback, useEffect, useRef } from 'react';
|
|
8
8
|
import { useSearchContext } from './SearchProvider';
|
|
9
9
|
import { useSearchState } from '../hooks/useSearchState';
|
|
10
10
|
import { clsx } from 'clsx';
|
|
11
|
+
const SHADOWS = {
|
|
12
|
+
sm: '0 1px 2px rgba(0,0,0,0.05)',
|
|
13
|
+
md: '0 2px 4px rgba(0,0,0,0.1)',
|
|
14
|
+
lg: '0 4px 6px rgba(0,0,0,0.1)',
|
|
15
|
+
xl: '0 10px 15px rgba(0,0,0,0.1)',
|
|
16
|
+
};
|
|
11
17
|
export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: currentMinProp, currentMax: currentMaxProp, onRangeChange, formatValue = (v) => v.toString(), className, style, theme: customTheme, showValues = true, syncWithState = true, debounceMs = 300, }) => {
|
|
12
18
|
const { theme } = useSearchContext();
|
|
13
19
|
const { refinements, addRefinement, removeRefinement } = useSearchState();
|
|
14
20
|
const rangeSliderTheme = customTheme || {};
|
|
21
|
+
const thumbClass = rangeSliderTheme.thumb || 'seekora-range-slider__thumb';
|
|
15
22
|
// Parse current range from StateManager
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
// NOTE: computed every render (no useMemo) because the state manager mutates
|
|
24
|
+
// the refinements array in place — the reference never changes.
|
|
25
|
+
let stateRange;
|
|
26
|
+
if (!syncWithState) {
|
|
27
|
+
stateRange = { min: undefined, max: undefined };
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
19
30
|
let minVal;
|
|
20
31
|
let maxVal;
|
|
21
32
|
refinements.forEach(r => {
|
|
@@ -28,15 +39,17 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
28
39
|
maxVal = parseFloat(maxMatch[1]);
|
|
29
40
|
}
|
|
30
41
|
});
|
|
31
|
-
|
|
32
|
-
}
|
|
42
|
+
stateRange = { min: minVal, max: maxVal };
|
|
43
|
+
}
|
|
33
44
|
const [internalMin, setInternalMin] = useState(currentMinProp ?? stateRange.min ?? min);
|
|
34
45
|
const [internalMax, setInternalMax] = useState(currentMaxProp ?? stateRange.max ?? max);
|
|
35
|
-
const
|
|
46
|
+
const isDraggingRef = useRef(false);
|
|
36
47
|
const debounceRef = useRef(null);
|
|
37
|
-
|
|
48
|
+
const pendingMinRef = useRef(internalMin);
|
|
49
|
+
const pendingMaxRef = useRef(internalMax);
|
|
50
|
+
// Sync with StateManager changes (only when stateRange actually changes, not on drag)
|
|
38
51
|
useEffect(() => {
|
|
39
|
-
if (syncWithState && !
|
|
52
|
+
if (syncWithState && !isDraggingRef.current) {
|
|
40
53
|
if (stateRange.min !== undefined)
|
|
41
54
|
setInternalMin(stateRange.min);
|
|
42
55
|
else
|
|
@@ -46,7 +59,7 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
46
59
|
else
|
|
47
60
|
setInternalMax(max);
|
|
48
61
|
}
|
|
49
|
-
}, [syncWithState, stateRange.min, stateRange.max,
|
|
62
|
+
}, [syncWithState, stateRange.min, stateRange.max, min, max]);
|
|
50
63
|
// Update StateManager with range refinements
|
|
51
64
|
const updateStateManager = useCallback((minVal, maxVal) => {
|
|
52
65
|
if (!syncWithState)
|
|
@@ -57,24 +70,24 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
57
70
|
removeRefinement(field, r.value, false);
|
|
58
71
|
}
|
|
59
72
|
});
|
|
60
|
-
// Add new range refinements
|
|
61
|
-
|
|
73
|
+
// Add new range refinements — trigger search on the last one added
|
|
74
|
+
const setMin = minVal > min;
|
|
75
|
+
const setMax = maxVal < max;
|
|
76
|
+
if (setMin && setMax) {
|
|
62
77
|
addRefinement(field, `>=${minVal}`, false);
|
|
78
|
+
addRefinement(field, `<=${maxVal}`, true);
|
|
63
79
|
}
|
|
64
|
-
if (
|
|
65
|
-
addRefinement(field, `<=${maxVal}`, minVal <= min); // Trigger search if only max is set
|
|
66
|
-
}
|
|
67
|
-
if (minVal > min && maxVal >= max) {
|
|
68
|
-
// Trigger search after setting min
|
|
80
|
+
else if (setMin) {
|
|
69
81
|
addRefinement(field, `>=${minVal}`, true);
|
|
70
82
|
}
|
|
71
|
-
else if (
|
|
72
|
-
|
|
73
|
-
// already triggered above
|
|
83
|
+
else if (setMax) {
|
|
84
|
+
addRefinement(field, `<=${maxVal}`, true);
|
|
74
85
|
}
|
|
75
86
|
}, [syncWithState, field, refinements, addRefinement, removeRefinement, min, max]);
|
|
76
|
-
// Debounced update
|
|
87
|
+
// Debounced update (during drag only)
|
|
77
88
|
const debouncedUpdate = useCallback((minVal, maxVal) => {
|
|
89
|
+
pendingMinRef.current = minVal;
|
|
90
|
+
pendingMaxRef.current = maxVal;
|
|
78
91
|
if (debounceRef.current) {
|
|
79
92
|
clearTimeout(debounceRef.current);
|
|
80
93
|
}
|
|
@@ -89,19 +102,75 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
89
102
|
const handleMinChange = (e) => {
|
|
90
103
|
const value = Math.min(Number(e.target.value), internalMax - step);
|
|
91
104
|
setInternalMin(value);
|
|
92
|
-
|
|
105
|
+
isDraggingRef.current = true;
|
|
93
106
|
debouncedUpdate(value, internalMax);
|
|
94
107
|
};
|
|
95
108
|
// Handle max slider change
|
|
96
109
|
const handleMaxChange = (e) => {
|
|
97
110
|
const value = Math.max(Number(e.target.value), internalMin + step);
|
|
98
111
|
setInternalMax(value);
|
|
99
|
-
|
|
112
|
+
isDraggingRef.current = true;
|
|
100
113
|
debouncedUpdate(internalMin, value);
|
|
101
114
|
};
|
|
102
|
-
// Handle drag end
|
|
115
|
+
// Handle drag end — flush pending update immediately
|
|
103
116
|
const handleDragEnd = () => {
|
|
104
|
-
|
|
117
|
+
isDraggingRef.current = false;
|
|
118
|
+
// Cancel the debounce and commit immediately
|
|
119
|
+
if (debounceRef.current) {
|
|
120
|
+
clearTimeout(debounceRef.current);
|
|
121
|
+
debounceRef.current = null;
|
|
122
|
+
}
|
|
123
|
+
updateStateManager(pendingMinRef.current, pendingMaxRef.current);
|
|
124
|
+
if (onRangeChange) {
|
|
125
|
+
onRangeChange(pendingMinRef.current, pendingMaxRef.current);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
// Handle keyboard navigation for enhanced control (Shift+Arrow for 10x step, Home/End)
|
|
129
|
+
const handleMinKeyDown = (e) => {
|
|
130
|
+
let newValue = null;
|
|
131
|
+
if (e.key === 'Home') {
|
|
132
|
+
e.preventDefault();
|
|
133
|
+
newValue = min;
|
|
134
|
+
}
|
|
135
|
+
else if (e.key === 'End') {
|
|
136
|
+
e.preventDefault();
|
|
137
|
+
newValue = internalMax - step;
|
|
138
|
+
}
|
|
139
|
+
else if (e.shiftKey && (e.key === 'ArrowLeft' || e.key === 'ArrowDown')) {
|
|
140
|
+
e.preventDefault();
|
|
141
|
+
newValue = Math.max(min, internalMin - step * 10);
|
|
142
|
+
}
|
|
143
|
+
else if (e.shiftKey && (e.key === 'ArrowRight' || e.key === 'ArrowUp')) {
|
|
144
|
+
e.preventDefault();
|
|
145
|
+
newValue = Math.min(internalMax - step, internalMin + step * 10);
|
|
146
|
+
}
|
|
147
|
+
if (newValue !== null) {
|
|
148
|
+
setInternalMin(newValue);
|
|
149
|
+
debouncedUpdate(newValue, internalMax);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
const handleMaxKeyDown = (e) => {
|
|
153
|
+
let newValue = null;
|
|
154
|
+
if (e.key === 'Home') {
|
|
155
|
+
e.preventDefault();
|
|
156
|
+
newValue = internalMin + step;
|
|
157
|
+
}
|
|
158
|
+
else if (e.key === 'End') {
|
|
159
|
+
e.preventDefault();
|
|
160
|
+
newValue = max;
|
|
161
|
+
}
|
|
162
|
+
else if (e.shiftKey && (e.key === 'ArrowLeft' || e.key === 'ArrowDown')) {
|
|
163
|
+
e.preventDefault();
|
|
164
|
+
newValue = Math.max(internalMin + step, internalMax - step * 10);
|
|
165
|
+
}
|
|
166
|
+
else if (e.shiftKey && (e.key === 'ArrowRight' || e.key === 'ArrowUp')) {
|
|
167
|
+
e.preventDefault();
|
|
168
|
+
newValue = Math.min(max, internalMax + step * 10);
|
|
169
|
+
}
|
|
170
|
+
if (newValue !== null) {
|
|
171
|
+
setInternalMax(newValue);
|
|
172
|
+
debouncedUpdate(internalMin, newValue);
|
|
173
|
+
}
|
|
105
174
|
};
|
|
106
175
|
// Calculate filled track position
|
|
107
176
|
const minPercent = ((internalMin - min) / (max - min)) * 100;
|
|
@@ -119,7 +188,7 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
119
188
|
} }, label)),
|
|
120
189
|
React.createElement("div", { className: rangeSliderTheme.slider, style: {
|
|
121
190
|
position: 'relative',
|
|
122
|
-
|
|
191
|
+
minHeight: '40px',
|
|
123
192
|
display: 'flex',
|
|
124
193
|
alignItems: 'center',
|
|
125
194
|
} },
|
|
@@ -138,7 +207,7 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
138
207
|
backgroundColor: theme.colors.primary,
|
|
139
208
|
borderRadius: '2px',
|
|
140
209
|
} }),
|
|
141
|
-
React.createElement("input", { type: "range", min: min, max: max, step: step, value: internalMin, onChange: handleMinChange, onMouseUp: handleDragEnd, onTouchEnd: handleDragEnd, className:
|
|
210
|
+
React.createElement("input", { type: "range", min: min, max: max, step: step, value: internalMin, onChange: handleMinChange, onMouseUp: handleDragEnd, onTouchEnd: handleDragEnd, onKeyDown: handleMinKeyDown, tabIndex: 0, "aria-valuenow": internalMin, "aria-valuemin": min, "aria-valuemax": max, className: thumbClass, style: {
|
|
142
211
|
position: 'absolute',
|
|
143
212
|
width: '100%',
|
|
144
213
|
height: '4px',
|
|
@@ -148,7 +217,7 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
148
217
|
cursor: 'pointer',
|
|
149
218
|
pointerEvents: 'none',
|
|
150
219
|
}, "aria-label": `Minimum ${label || field}` }),
|
|
151
|
-
React.createElement("input", { type: "range", min: min, max: max, step: step, value: internalMax, onChange: handleMaxChange, onMouseUp: handleDragEnd, onTouchEnd: handleDragEnd, className:
|
|
220
|
+
React.createElement("input", { type: "range", min: min, max: max, step: step, value: internalMax, onChange: handleMaxChange, onMouseUp: handleDragEnd, onTouchEnd: handleDragEnd, onKeyDown: handleMaxKeyDown, tabIndex: 0, "aria-valuenow": internalMax, "aria-valuemin": min, "aria-valuemax": max, className: thumbClass, style: {
|
|
152
221
|
position: 'absolute',
|
|
153
222
|
width: '100%',
|
|
154
223
|
height: '4px',
|
|
@@ -168,7 +237,7 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
168
237
|
React.createElement("span", { className: rangeSliderTheme.value }, formatValue(internalMin)),
|
|
169
238
|
React.createElement("span", { className: rangeSliderTheme.value }, formatValue(internalMax)))),
|
|
170
239
|
React.createElement("style", null, `
|
|
171
|
-
.${
|
|
240
|
+
.${thumbClass}::-webkit-slider-thumb {
|
|
172
241
|
-webkit-appearance: none;
|
|
173
242
|
appearance: none;
|
|
174
243
|
width: 20px;
|
|
@@ -177,9 +246,9 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
177
246
|
border-radius: 50%;
|
|
178
247
|
cursor: pointer;
|
|
179
248
|
pointer-events: all;
|
|
180
|
-
box-shadow:
|
|
249
|
+
box-shadow: ${SHADOWS.md};
|
|
181
250
|
}
|
|
182
|
-
.${
|
|
251
|
+
.${thumbClass}::-moz-range-thumb {
|
|
183
252
|
width: 20px;
|
|
184
253
|
height: 20px;
|
|
185
254
|
background: ${theme.colors.primary};
|
|
@@ -187,7 +256,7 @@ export const RangeSlider = ({ field, label, min, max, step = 1, currentMin: curr
|
|
|
187
256
|
cursor: pointer;
|
|
188
257
|
pointer-events: all;
|
|
189
258
|
border: none;
|
|
190
|
-
box-shadow:
|
|
259
|
+
box-shadow: ${SHADOWS.md};
|
|
191
260
|
}
|
|
192
261
|
`)));
|
|
193
262
|
};
|
|
@@ -71,6 +71,13 @@ export interface RichQuerySuggestionsProps extends QuerySuggestionsEventHandlers
|
|
|
71
71
|
ariaLabel?: string;
|
|
72
72
|
/** Analytics tags */
|
|
73
73
|
analyticsTags?: string[];
|
|
74
|
+
/** Highlight options for query suggestions (passed to highlight rendering) */
|
|
75
|
+
highlightOptions?: {
|
|
76
|
+
highlightColor?: string;
|
|
77
|
+
highlightTextColor?: string;
|
|
78
|
+
highlightFontWeight?: string | number;
|
|
79
|
+
highlightStyle?: 'background' | 'underline' | 'bold' | 'color-only';
|
|
80
|
+
};
|
|
74
81
|
}
|
|
75
82
|
export interface RichQuerySuggestionsRef {
|
|
76
83
|
getActiveIndex: () => number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RichQuerySuggestions.d.ts","sourceRoot":"","sources":["../../src/components/RichQuerySuggestions.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAQN,MAAM,OAAO,CAAC;AAIf,OAAO,KAAK,EACV,cAAc,EACd,YAAY,EACZ,cAAc,EAGd,kBAAkB,EAClB,0BAA0B,EAC1B,6BAA6B,EAE7B,qBAAqB,EACtB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"RichQuerySuggestions.d.ts","sourceRoot":"","sources":["../../src/components/RichQuerySuggestions.tsx"],"names":[],"mappings":"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"}
|
|
@@ -14,6 +14,20 @@ import React, { useState, useRef, useCallback, useMemo, forwardRef, useImperativ
|
|
|
14
14
|
import { useSearchContext } from './SearchProvider';
|
|
15
15
|
import { useQuerySuggestionsEnhanced } from '../hooks/useQuerySuggestionsEnhanced';
|
|
16
16
|
import { clsx } from 'clsx';
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Constants
|
|
19
|
+
// ============================================================================
|
|
20
|
+
const TRANSITIONS = {
|
|
21
|
+
fast: '150ms ease-in-out',
|
|
22
|
+
normal: '200ms ease-in-out',
|
|
23
|
+
slow: '300ms ease-in-out',
|
|
24
|
+
};
|
|
25
|
+
const BORDER_RADIUS = {
|
|
26
|
+
sm: 4,
|
|
27
|
+
md: 6,
|
|
28
|
+
lg: 8,
|
|
29
|
+
full: 9999,
|
|
30
|
+
};
|
|
17
31
|
// Default section order
|
|
18
32
|
const DEFAULT_SECTIONS = [
|
|
19
33
|
{ id: 'recent', title: 'Recent Searches', maxItems: 5, enabled: true, order: 1 },
|
|
@@ -26,16 +40,16 @@ const DEFAULT_SECTIONS = [
|
|
|
26
40
|
// ============================================================================
|
|
27
41
|
const styles = {
|
|
28
42
|
container: {
|
|
29
|
-
backgroundColor: 'var(--seekora-bg-surface,
|
|
30
|
-
border: '1px solid var(--seekora-border-color,
|
|
31
|
-
borderRadius:
|
|
43
|
+
backgroundColor: 'var(--seekora-bg-surface, transparent)',
|
|
44
|
+
border: '1px solid var(--seekora-border-color, rgba(128,128,128,0.2))',
|
|
45
|
+
borderRadius: `var(--seekora-border-radius-lg, ${BORDER_RADIUS.lg * 1.5}px)`,
|
|
32
46
|
boxShadow: 'var(--seekora-shadow-xl, 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04))',
|
|
33
47
|
overflow: 'hidden',
|
|
34
48
|
},
|
|
35
49
|
header: {
|
|
36
50
|
padding: '12px 16px',
|
|
37
|
-
borderBottom: '1px solid var(--seekora-border-color,
|
|
38
|
-
backgroundColor: 'var(--seekora-bg-secondary,
|
|
51
|
+
borderBottom: '1px solid var(--seekora-border-color, rgba(128,128,128,0.2))',
|
|
52
|
+
backgroundColor: 'var(--seekora-bg-secondary, rgba(255, 255, 255, 0.1))',
|
|
39
53
|
},
|
|
40
54
|
content: {
|
|
41
55
|
overflowY: 'auto',
|
|
@@ -70,7 +84,7 @@ const styles = {
|
|
|
70
84
|
alignItems: 'flex-start',
|
|
71
85
|
padding: '10px 16px',
|
|
72
86
|
cursor: 'pointer',
|
|
73
|
-
transition:
|
|
87
|
+
transition: `background-color ${TRANSITIONS.fast}`,
|
|
74
88
|
gap: '12px',
|
|
75
89
|
},
|
|
76
90
|
itemActive: {
|
|
@@ -89,7 +103,7 @@ const styles = {
|
|
|
89
103
|
},
|
|
90
104
|
itemQuery: {
|
|
91
105
|
fontSize: '14px',
|
|
92
|
-
color: 'var(--seekora-text-primary,
|
|
106
|
+
color: 'var(--seekora-text-primary, inherit)',
|
|
93
107
|
fontWeight: 500,
|
|
94
108
|
margin: 0,
|
|
95
109
|
overflow: 'hidden',
|
|
@@ -98,7 +112,7 @@ const styles = {
|
|
|
98
112
|
},
|
|
99
113
|
itemMeta: {
|
|
100
114
|
fontSize: '12px',
|
|
101
|
-
color: 'var(--seekora-text-secondary,
|
|
115
|
+
color: 'var(--seekora-text-secondary, inherit)',
|
|
102
116
|
marginTop: '2px',
|
|
103
117
|
},
|
|
104
118
|
itemCount: {
|
|
@@ -119,11 +133,11 @@ const styles = {
|
|
|
119
133
|
padding: '3px 8px',
|
|
120
134
|
fontSize: '11px',
|
|
121
135
|
fontWeight: 500,
|
|
122
|
-
color: 'var(--seekora-text-secondary,
|
|
123
|
-
backgroundColor: 'var(--seekora-bg-tertiary,
|
|
124
|
-
borderRadius:
|
|
136
|
+
color: 'var(--seekora-text-secondary, inherit)',
|
|
137
|
+
backgroundColor: 'var(--seekora-bg-tertiary, rgba(255, 255, 255, 0.1))',
|
|
138
|
+
borderRadius: `${BORDER_RADIUS.lg * 1.5}px`,
|
|
125
139
|
cursor: 'pointer',
|
|
126
|
-
transition:
|
|
140
|
+
transition: `all ${TRANSITIONS.fast}`,
|
|
127
141
|
},
|
|
128
142
|
categoryPillHover: {
|
|
129
143
|
backgroundColor: 'var(--seekora-primary-light, #dbeafe)',
|
|
@@ -145,12 +159,12 @@ const styles = {
|
|
|
145
159
|
padding: '6px 12px',
|
|
146
160
|
fontSize: '13px',
|
|
147
161
|
fontWeight: 500,
|
|
148
|
-
color: 'var(--seekora-text-primary,
|
|
149
|
-
backgroundColor: 'var(--seekora-bg-secondary,
|
|
150
|
-
border: '1px solid var(--seekora-border-color,
|
|
151
|
-
borderRadius:
|
|
162
|
+
color: 'var(--seekora-text-primary, inherit)',
|
|
163
|
+
backgroundColor: 'var(--seekora-bg-secondary, rgba(255, 255, 255, 0.1))',
|
|
164
|
+
border: '1px solid var(--seekora-border-color, rgba(128,128,128,0.2))',
|
|
165
|
+
borderRadius: `${BORDER_RADIUS.full}px`,
|
|
152
166
|
cursor: 'pointer',
|
|
153
|
-
transition:
|
|
167
|
+
transition: `all ${TRANSITIONS.fast}`,
|
|
154
168
|
},
|
|
155
169
|
trendingChipHover: {
|
|
156
170
|
borderColor: 'var(--seekora-primary, #3b82f6)',
|
|
@@ -164,7 +178,7 @@ const styles = {
|
|
|
164
178
|
trendingRank: {
|
|
165
179
|
width: '18px',
|
|
166
180
|
height: '18px',
|
|
167
|
-
borderRadius:
|
|
181
|
+
borderRadius: `${BORDER_RADIUS.full}px`,
|
|
168
182
|
backgroundColor: 'var(--seekora-primary, #3b82f6)',
|
|
169
183
|
color: 'white',
|
|
170
184
|
fontSize: '10px',
|
|
@@ -176,25 +190,25 @@ const styles = {
|
|
|
176
190
|
removeButton: {
|
|
177
191
|
padding: '4px',
|
|
178
192
|
marginLeft: 'auto',
|
|
179
|
-
borderRadius:
|
|
193
|
+
borderRadius: `${BORDER_RADIUS.sm}px`,
|
|
180
194
|
border: 'none',
|
|
181
195
|
background: 'transparent',
|
|
182
196
|
cursor: 'pointer',
|
|
183
197
|
color: 'var(--seekora-text-tertiary, #9ca3af)',
|
|
184
198
|
opacity: 0,
|
|
185
|
-
transition:
|
|
199
|
+
transition: `opacity ${TRANSITIONS.fast}`,
|
|
186
200
|
},
|
|
187
201
|
divider: {
|
|
188
202
|
height: '1px',
|
|
189
|
-
backgroundColor: 'var(--seekora-border-color,
|
|
203
|
+
backgroundColor: 'var(--seekora-border-color, rgba(128,128,128,0.2))',
|
|
190
204
|
margin: '4px 16px',
|
|
191
205
|
},
|
|
192
206
|
footer: {
|
|
193
207
|
padding: '12px 16px',
|
|
194
|
-
borderTop: '1px solid var(--seekora-border-color,
|
|
195
|
-
backgroundColor: 'var(--seekora-bg-secondary,
|
|
208
|
+
borderTop: '1px solid var(--seekora-border-color, rgba(128,128,128,0.2))',
|
|
209
|
+
backgroundColor: 'var(--seekora-bg-secondary, rgba(255, 255, 255, 0.1))',
|
|
196
210
|
fontSize: '12px',
|
|
197
|
-
color: 'var(--seekora-text-secondary,
|
|
211
|
+
color: 'var(--seekora-text-secondary, inherit)',
|
|
198
212
|
},
|
|
199
213
|
loadingOverlay: {
|
|
200
214
|
position: 'absolute',
|
|
@@ -202,12 +216,12 @@ const styles = {
|
|
|
202
216
|
display: 'flex',
|
|
203
217
|
alignItems: 'center',
|
|
204
218
|
justifyContent: 'center',
|
|
205
|
-
backgroundColor: 'rgba(255, 255, 255, 0.
|
|
219
|
+
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
|
206
220
|
},
|
|
207
221
|
emptyState: {
|
|
208
222
|
padding: '32px 16px',
|
|
209
223
|
textAlign: 'center',
|
|
210
|
-
color: 'var(--seekora-text-secondary,
|
|
224
|
+
color: 'var(--seekora-text-secondary, inherit)',
|
|
211
225
|
},
|
|
212
226
|
highlight: {
|
|
213
227
|
backgroundColor: 'var(--seekora-highlight-bg, #fef9c3)',
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import React from 'react';
|
|
7
7
|
import type { SearchOptions } from '@seekora-ai/search-sdk';
|
|
8
|
+
export type SearchBarSize = 'small' | 'medium' | 'large';
|
|
8
9
|
export interface SearchBarTheme {
|
|
9
10
|
container?: string;
|
|
10
11
|
input?: string;
|
|
@@ -14,6 +15,9 @@ export interface SearchBarTheme {
|
|
|
14
15
|
suggestionItemHover?: string;
|
|
15
16
|
suggestionItemActive?: string;
|
|
16
17
|
loadingIndicator?: string;
|
|
18
|
+
searchIcon?: string;
|
|
19
|
+
clearButton?: string;
|
|
20
|
+
submitButton?: string;
|
|
17
21
|
}
|
|
18
22
|
export interface SearchBarProps {
|
|
19
23
|
placeholder?: string;
|
|
@@ -37,6 +41,18 @@ export interface SearchBarProps {
|
|
|
37
41
|
showLoadingState?: boolean;
|
|
38
42
|
renderSuggestion?: (suggestion: string, index: number) => React.ReactNode;
|
|
39
43
|
renderLoading?: () => React.ReactNode;
|
|
44
|
+
/** Custom render for the search icon. If not provided, a default magnifying glass SVG is rendered. */
|
|
45
|
+
renderSearchIcon?: () => React.ReactNode;
|
|
46
|
+
/** Whether to show a clear button when the query is non-empty (default: true) */
|
|
47
|
+
showClearButton?: boolean;
|
|
48
|
+
/** Custom render for the clear icon */
|
|
49
|
+
renderClearIcon?: () => React.ReactNode;
|
|
50
|
+
/** Whether to show a submit button to the right of the input (default: false) */
|
|
51
|
+
showSubmitButton?: boolean;
|
|
52
|
+
/** Custom render for the submit button */
|
|
53
|
+
renderSubmitButton?: () => React.ReactNode;
|
|
54
|
+
/** Size variant controlling padding and font size (default: 'medium') */
|
|
55
|
+
size?: SearchBarSize;
|
|
40
56
|
}
|
|
41
57
|
export declare const SearchBar: React.FC<SearchBarProps>;
|
|
42
58
|
//# sourceMappingURL=SearchBar.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchBar.d.ts","sourceRoot":"","sources":["../../src/components/SearchBar.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAMxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"SearchBar.d.ts","sourceRoot":"","sources":["../../src/components/SearchBar.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAMxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAU5D,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEzD,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,IAAI,CAAC;IACjD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,GAAG,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;KAAE,KAAK,IAAI,CAAC;IAC/F,aAAa,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,2HAA2H;IAC3H,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAC;IAC1E,aAAa,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;IACtC,sGAAsG;IACtG,gBAAgB,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;IACzC,iFAAiF;IACjF,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uCAAuC;IACvC,eAAe,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;IACxC,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,0CAA0C;IAC1C,kBAAkB,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;IAC3C,yEAAyE;IACzE,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB;AAgED,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CA2b9C,CAAC"}
|
|
@@ -9,7 +9,49 @@ import { useSearchState } from '../hooks/useSearchState';
|
|
|
9
9
|
import { useQuerySuggestions } from '../hooks/useQuerySuggestions';
|
|
10
10
|
import { log } from '@seekora-ai/ui-sdk-core';
|
|
11
11
|
import { clsx } from 'clsx';
|
|
12
|
-
|
|
12
|
+
// Z-index scale for consistent layering
|
|
13
|
+
const Z_INDEX = {
|
|
14
|
+
dropdown: 100,
|
|
15
|
+
modal: 200,
|
|
16
|
+
overlay: 300,
|
|
17
|
+
tooltip: 50,
|
|
18
|
+
};
|
|
19
|
+
const SIZE_CONFIG = {
|
|
20
|
+
small: {
|
|
21
|
+
padding: '0.375rem 0.5rem',
|
|
22
|
+
fontSize: '0.875rem',
|
|
23
|
+
iconSize: 14,
|
|
24
|
+
iconPaddingLeft: '1.75rem',
|
|
25
|
+
iconPaddingRight: '1.75rem',
|
|
26
|
+
iconLeft: '0.5rem',
|
|
27
|
+
iconRight: '0.5rem',
|
|
28
|
+
},
|
|
29
|
+
medium: {
|
|
30
|
+
padding: '0.625rem 1rem',
|
|
31
|
+
fontSize: '1rem',
|
|
32
|
+
iconSize: 18,
|
|
33
|
+
iconPaddingLeft: '2.25rem',
|
|
34
|
+
iconPaddingRight: '2.25rem',
|
|
35
|
+
iconLeft: '0.625rem',
|
|
36
|
+
iconRight: '0.625rem',
|
|
37
|
+
},
|
|
38
|
+
large: {
|
|
39
|
+
padding: '0.875rem 1.25rem',
|
|
40
|
+
fontSize: '1.25rem',
|
|
41
|
+
iconSize: 22,
|
|
42
|
+
iconPaddingLeft: '2.75rem',
|
|
43
|
+
iconPaddingRight: '2.75rem',
|
|
44
|
+
iconLeft: '0.75rem',
|
|
45
|
+
iconRight: '0.75rem',
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
const DefaultSearchIcon = ({ size = 18 }) => (React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
|
|
49
|
+
React.createElement("circle", { cx: "11", cy: "11", r: "8" }),
|
|
50
|
+
React.createElement("line", { x1: "21", y1: "21", x2: "16.65", y2: "16.65" })));
|
|
51
|
+
const DefaultClearIcon = ({ size = 14 }) => (React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
|
|
52
|
+
React.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
53
|
+
React.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })));
|
|
54
|
+
export const SearchBar = ({ placeholder = 'Search...', showSuggestions = true, debounceMs = 300, minQueryLength = 2, maxSuggestions = 10, onSearch, onQueryChange, onSuggestionSelect, onSearchStateChange, searchOptions, className, style, theme: customTheme, showLoadingState = false, renderSuggestion, renderLoading, renderSearchIcon, showClearButton = true, renderClearIcon, showSubmitButton = false, renderSubmitButton, size = 'medium', }) => {
|
|
13
55
|
const { client, theme, enableAnalytics, autoTrackSearch } = useSearchContext();
|
|
14
56
|
const { query, setQuery, search: triggerSearch, results, loading: searchLoading, error: searchError } = useSearchState();
|
|
15
57
|
const [isFocused, setIsFocused] = useState(false);
|
|
@@ -150,28 +192,107 @@ export const SearchBar = ({ placeholder = 'Search...', showSuggestions = true, d
|
|
|
150
192
|
const processingTime = res?.processingTimeMS
|
|
151
193
|
|| res?.data?.processingTimeMS
|
|
152
194
|
|| res?.data?.data?.processingTimeMS;
|
|
195
|
+
// Size-based configuration
|
|
196
|
+
const sizeConfig = SIZE_CONFIG[size];
|
|
197
|
+
// Determine whether the search icon is shown (always unless renderSearchIcon returns null explicitly — but
|
|
198
|
+
// we always show it; there is no prop to hide the search icon, only to customise it)
|
|
199
|
+
const hasSearchIcon = true;
|
|
200
|
+
const hasClearBtn = showClearButton && query.length > 0;
|
|
201
|
+
// Compute input padding accounting for icons
|
|
202
|
+
const inputPaddingLeft = hasSearchIcon ? sizeConfig.iconPaddingLeft : sizeConfig.padding.split(' ')[1] || sizeConfig.padding;
|
|
203
|
+
const inputPaddingRight = hasClearBtn ? sizeConfig.iconPaddingRight : sizeConfig.padding.split(' ')[1] || sizeConfig.padding;
|
|
204
|
+
const borderRadius = typeof theme.borderRadius === 'string' ? theme.borderRadius : theme.borderRadius.medium;
|
|
205
|
+
const focusBorderColor = isFocused ? `var(--seekora-border-focus, ${theme.colors.focus})` : `var(--seekora-border, ${theme.colors.border})`;
|
|
206
|
+
const focusRingShadow = isFocused
|
|
207
|
+
? `0 0 0 3px var(--seekora-border-focus-alpha, ${theme.colors.focus}33)`
|
|
208
|
+
: undefined;
|
|
209
|
+
const handleClear = useCallback(() => {
|
|
210
|
+
setQuery('', false);
|
|
211
|
+
setSelectedIndex(-1);
|
|
212
|
+
inputRef.current?.focus();
|
|
213
|
+
}, [setQuery]);
|
|
214
|
+
const handleSubmit = useCallback(() => {
|
|
215
|
+
const currentValue = inputRef.current?.value || query;
|
|
216
|
+
handleSearch(currentValue);
|
|
217
|
+
}, [query, handleSearch]);
|
|
153
218
|
return (React.createElement("div", { ref: containerRef, className: clsx(searchBarTheme.container, className), style: {
|
|
154
219
|
position: 'relative',
|
|
155
220
|
display: 'flex',
|
|
156
221
|
alignItems: 'center',
|
|
222
|
+
// CSS custom properties for external styling
|
|
223
|
+
'--seekora-searchbar-bg': theme.colors.background,
|
|
224
|
+
'--seekora-searchbar-border': theme.colors.border,
|
|
225
|
+
'--seekora-searchbar-focus-border': theme.colors.focus,
|
|
226
|
+
'--seekora-searchbar-radius': borderRadius,
|
|
227
|
+
'--seekora-searchbar-icon-color': theme.colors.textSecondary,
|
|
157
228
|
...style,
|
|
158
229
|
} },
|
|
159
|
-
React.createElement("
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
230
|
+
React.createElement("div", { style: { position: 'relative', flex: 1, display: 'flex', alignItems: 'center' } },
|
|
231
|
+
hasSearchIcon && (React.createElement("span", { className: searchBarTheme.searchIcon, "aria-hidden": "true", style: {
|
|
232
|
+
position: 'absolute',
|
|
233
|
+
left: sizeConfig.iconLeft,
|
|
234
|
+
top: '50%',
|
|
235
|
+
transform: 'translateY(-50%)',
|
|
236
|
+
display: 'flex',
|
|
237
|
+
alignItems: 'center',
|
|
238
|
+
justifyContent: 'center',
|
|
239
|
+
pointerEvents: 'none',
|
|
240
|
+
color: 'var(--seekora-searchbar-icon-color)',
|
|
241
|
+
zIndex: 2,
|
|
242
|
+
} }, renderSearchIcon ? renderSearchIcon() : React.createElement(DefaultSearchIcon, { size: sizeConfig.iconSize }))),
|
|
243
|
+
React.createElement("input", { ref: inputRef, type: "text", value: query, onChange: handleInputChange, onKeyDown: handleKeyDown, onFocus: handleFocus, onBlur: handleBlur, placeholder: placeholder, className: clsx(searchBarTheme.input, isFocused && searchBarTheme.inputFocused), style: {
|
|
244
|
+
width: '100%',
|
|
245
|
+
paddingTop: sizeConfig.padding.split(' ')[0],
|
|
246
|
+
paddingBottom: sizeConfig.padding.split(' ')[0],
|
|
247
|
+
paddingLeft: inputPaddingLeft,
|
|
248
|
+
paddingRight: inputPaddingRight,
|
|
249
|
+
fontSize: sizeConfig.fontSize,
|
|
250
|
+
fontFamily: theme.typography.fontFamily,
|
|
251
|
+
backgroundColor: 'var(--seekora-searchbar-bg)',
|
|
252
|
+
color: theme.colors.text,
|
|
253
|
+
borderWidth: '1px',
|
|
254
|
+
borderStyle: 'solid',
|
|
255
|
+
borderColor: focusBorderColor,
|
|
256
|
+
borderRadius: 'var(--seekora-searchbar-radius)',
|
|
257
|
+
outline: 'none',
|
|
258
|
+
boxShadow: focusRingShadow,
|
|
259
|
+
transition: theme.transitions?.fast || '150ms ease-in-out',
|
|
260
|
+
boxSizing: 'border-box',
|
|
261
|
+
} }),
|
|
262
|
+
hasClearBtn && (React.createElement("button", { type: "button", onClick: handleClear, className: searchBarTheme.clearButton, "aria-label": "Clear search", style: {
|
|
263
|
+
position: 'absolute',
|
|
264
|
+
right: sizeConfig.iconRight,
|
|
265
|
+
top: '50%',
|
|
266
|
+
transform: 'translateY(-50%)',
|
|
267
|
+
display: 'flex',
|
|
268
|
+
alignItems: 'center',
|
|
269
|
+
justifyContent: 'center',
|
|
270
|
+
background: 'none',
|
|
271
|
+
border: 'none',
|
|
272
|
+
cursor: 'pointer',
|
|
273
|
+
padding: '2px',
|
|
274
|
+
borderRadius: '50%',
|
|
275
|
+
color: 'var(--seekora-searchbar-icon-color)',
|
|
276
|
+
transition: theme.transitions?.fast || '150ms ease-in-out',
|
|
277
|
+
zIndex: 2,
|
|
278
|
+
}, onMouseDown: (e) => {
|
|
279
|
+
// Prevent input blur so the clear action doesn't race with blur handler
|
|
280
|
+
e.preventDefault();
|
|
281
|
+
} }, renderClearIcon ? renderClearIcon() : React.createElement(DefaultClearIcon, { size: sizeConfig.iconSize - 4 })))),
|
|
282
|
+
showSubmitButton && (renderSubmitButton ? (React.createElement("div", { onClick: handleSubmit, style: { marginLeft: theme.spacing.small, cursor: 'pointer' } }, renderSubmitButton())) : (React.createElement("button", { type: "button", onClick: handleSubmit, className: searchBarTheme.submitButton, style: {
|
|
283
|
+
marginLeft: theme.spacing.small,
|
|
284
|
+
padding: sizeConfig.padding,
|
|
285
|
+
fontSize: sizeConfig.fontSize,
|
|
163
286
|
fontFamily: theme.typography.fontFamily,
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}),
|
|
174
|
-
} }),
|
|
287
|
+
fontWeight: theme.typography.fontWeight?.medium ?? 500,
|
|
288
|
+
backgroundColor: `var(--seekora-primary, ${theme.colors.primary})`,
|
|
289
|
+
color: 'var(--seekora-primary-text, #ffffff)',
|
|
290
|
+
border: 'none',
|
|
291
|
+
borderRadius: 'var(--seekora-searchbar-radius)',
|
|
292
|
+
cursor: 'pointer',
|
|
293
|
+
whiteSpace: 'nowrap',
|
|
294
|
+
transition: theme.transitions?.fast || '150ms ease-in-out',
|
|
295
|
+
} }, "Search"))),
|
|
175
296
|
processingTime !== undefined && (React.createElement("span", { style: {
|
|
176
297
|
marginLeft: theme.spacing.small,
|
|
177
298
|
fontSize: theme.typography.fontSize.small,
|
|
@@ -193,7 +314,8 @@ export const SearchBar = ({ placeholder = 'Search...', showSuggestions = true, d
|
|
|
193
314
|
boxShadow: theme.shadows.medium,
|
|
194
315
|
maxHeight: '400px',
|
|
195
316
|
overflowY: 'auto',
|
|
196
|
-
zIndex:
|
|
317
|
+
zIndex: Z_INDEX.dropdown,
|
|
318
|
+
boxSizing: 'border-box',
|
|
197
319
|
} },
|
|
198
320
|
isLoading && displayedSuggestions.length === 0 && showLoadingState && (renderLoading ? renderLoading() : defaultRenderLoading()),
|
|
199
321
|
displayedSuggestions.length > 0 && (React.createElement(React.Fragment, null, displayedSuggestions.map((suggestion, index) => {
|