@@ -17,6 +17,7 @@ interface SuggestionControlProps {
17
17
fetchSuggestions : SuggestionsFnType ;
18
18
className ?: string ;
19
19
placeholderText ?: string ;
20
+ children ?: React . ReactNode ;
20
21
}
21
22
22
23
type BlockHeaderSuggestionControlProps = Omit < SuggestionControlProps , "anchorRef" | "isOpen" > & {
@@ -31,10 +32,11 @@ const SuggestionControl: React.FC<SuggestionControlProps> = ({
31
32
onSelect,
32
33
fetchSuggestions,
33
34
className,
35
+ children,
34
36
} ) => {
35
37
if ( ! isOpen || ! anchorRef . current || ! fetchSuggestions ) return null ;
36
38
37
- return < SuggestionControlInner { ...{ anchorRef, onClose, onSelect, fetchSuggestions, className } } /> ;
39
+ return < SuggestionControlInner { ...{ anchorRef, onClose, onSelect, fetchSuggestions, className, children } } /> ;
38
40
} ;
39
41
40
42
function highlightPositions ( target : string , positions : number [ ] ) : ReactNode [ ] {
@@ -50,7 +52,11 @@ function highlightPositions(target: string, positions: number[]): ReactNode[] {
50
52
51
53
while ( targetIndex < target . length ) {
52
54
if ( posIndex < positions . length && targetIndex === positions [ posIndex ] ) {
53
- result . push ( < span className = "text-blue-500 font-bold" > { target [ targetIndex ] } </ span > ) ;
55
+ result . push (
56
+ < span key = { `h-${ targetIndex } ` } className = "text-blue-500 font-bold" >
57
+ { target [ targetIndex ] }
58
+ </ span >
59
+ ) ;
54
60
posIndex ++ ;
55
61
} else {
56
62
result . push ( target [ targetIndex ] ) ;
@@ -138,19 +144,42 @@ const BlockHeaderSuggestionControl: React.FC<BlockHeaderSuggestionControlProps>
138
144
return < SuggestionControl { ...props } anchorRef = { { current : headerElem } } isOpen = { isOpen } className = { newClass } /> ;
139
145
} ;
140
146
141
- const SuggestionControlInner : React . FC < Omit < SuggestionControlProps , "isOpen" > > = ( {
147
+ /**
148
+ * The empty state component that can be used as a child of SuggestionControl.
149
+ * If no children are provided to SuggestionControl, this default empty state will be used.
150
+ */
151
+ const SuggestionControlNoResults : React . FC < { children ?: React . ReactNode } > = ( { children } ) => {
152
+ return (
153
+ < div className = "flex items-center justify-center min-h-[120px] p-4" >
154
+ { children ?? < span className = "text-gray-500" > No Suggestions</ span > }
155
+ </ div >
156
+ ) ;
157
+ } ;
158
+
159
+ const SuggestionControlNoData : React . FC < { children ?: React . ReactNode } > = ( { children } ) => {
160
+ return (
161
+ < div className = "flex items-center justify-center min-h-[120px] p-4" >
162
+ { children ?? < span className = "text-gray-500" > No Suggestions</ span > }
163
+ </ div >
164
+ ) ;
165
+ } ;
166
+
167
+ interface SuggestionControlInnerProps extends Omit < SuggestionControlProps , "isOpen" > { }
168
+
169
+ const SuggestionControlInner : React . FC < SuggestionControlInnerProps > = ( {
142
170
anchorRef,
143
171
onClose,
144
172
onSelect,
145
173
onTab,
146
174
fetchSuggestions,
147
175
className,
148
176
placeholderText,
177
+ children,
149
178
} ) => {
150
179
const widgetId = useId ( ) ;
151
180
const [ query , setQuery ] = useState ( "" ) ;
152
181
const reqNumRef = useRef ( 0 ) ;
153
- const [ suggestions , setSuggestions ] = useState < SuggestionType [ ] > ( [ ] ) ;
182
+ let [ suggestions , setSuggestions ] = useState < SuggestionType [ ] > ( [ ] ) ;
154
183
const [ selectedIndex , setSelectedIndex ] = useState ( 0 ) ;
155
184
const [ fetched , setFetched ] = useState ( false ) ;
156
185
const inputRef = useRef < HTMLInputElement > ( null ) ;
@@ -160,6 +189,12 @@ const SuggestionControlInner: React.FC<Omit<SuggestionControlProps, "isOpen">> =
160
189
strategy : "absolute" ,
161
190
middleware : [ offset ( - 1 ) ] ,
162
191
} ) ;
192
+ const emptyStateChild = React . Children . toArray ( children ) . find (
193
+ ( child ) => React . isValidElement ( child ) && child . type === SuggestionControlNoResults
194
+ ) ;
195
+ const noDataChild = React . Children . toArray ( children ) . find (
196
+ ( child ) => React . isValidElement ( child ) && child . type === SuggestionControlNoData
197
+ ) ;
163
198
164
199
useEffect ( ( ) => {
165
200
refs . setReference ( anchorRef . current ) ;
@@ -168,7 +203,7 @@ const SuggestionControlInner: React.FC<Omit<SuggestionControlProps, "isOpen">> =
168
203
useEffect ( ( ) => {
169
204
reqNumRef . current ++ ;
170
205
fetchSuggestions ( query , { widgetid : widgetId , reqnum : reqNumRef . current } ) . then ( ( results ) => {
171
- if ( results . reqnum != reqNumRef . current ) {
206
+ if ( results . reqnum !== reqNumRef . current ) {
172
207
return ;
173
208
}
174
209
setSuggestions ( results . suggestions ?? [ ] ) ;
@@ -222,7 +257,6 @@ const SuggestionControlInner: React.FC<Omit<SuggestionControlProps, "isOpen">> =
222
257
}
223
258
}
224
259
} ;
225
-
226
260
return (
227
261
< div
228
262
className = { clsx (
@@ -247,29 +281,37 @@ const SuggestionControlInner: React.FC<Omit<SuggestionControlProps, "isOpen">> =
247
281
placeholder = { placeholderText }
248
282
/>
249
283
</ div >
250
- { fetched && suggestions . length > 0 && (
251
- < div ref = { dropdownRef } className = "max-h-96 overflow-y-auto divide-y divide-gray-700" >
252
- { suggestions . map ( ( suggestion , index ) => (
253
- < div
254
- key = { suggestion . suggestionid }
255
- className = { clsx (
256
- "flex items-center gap-3 px-4 py-2 cursor-pointer" ,
257
- index === selectedIndex ? "bg-accentbg" : "hover:bg-hoverbg" ,
258
- "text-gray-100"
259
- ) }
260
- onClick = { ( ) => {
261
- onSelect ( suggestion , query ) ;
262
- onClose ( ) ;
263
- } }
264
- >
265
- < SuggestionIcon suggestion = { suggestion } />
266
- < SuggestionContent suggestion = { suggestion } />
267
- </ div >
268
- ) ) }
269
- </ div >
270
- ) }
284
+ { fetched &&
285
+ ( suggestions . length > 0 ? (
286
+ < div ref = { dropdownRef } className = "max-h-96 overflow-y-auto divide-y divide-gray-700" >
287
+ { suggestions . map ( ( suggestion , index ) => (
288
+ < div
289
+ key = { suggestion . suggestionid }
290
+ className = { clsx (
291
+ "flex items-center gap-3 px-4 py-2 cursor-pointer" ,
292
+ index === selectedIndex ? "bg-accentbg" : "hover:bg-hoverbg" ,
293
+ "text-gray-100"
294
+ ) }
295
+ onClick = { ( ) => {
296
+ onSelect ( suggestion , query ) ;
297
+ onClose ( ) ;
298
+ } }
299
+ >
300
+ < SuggestionIcon suggestion = { suggestion } />
301
+ < SuggestionContent suggestion = { suggestion } />
302
+ </ div >
303
+ ) ) }
304
+ </ div >
305
+ ) : (
306
+ // Render the empty state (either a provided child or the default)
307
+ < div key = "empty" className = "flex items-center justify-center min-h-[120px] p-4" >
308
+ { query === ""
309
+ ? ( noDataChild ?? < SuggestionControlNoData /> )
310
+ : ( emptyStateChild ?? < SuggestionControlNoResults /> ) }
311
+ </ div >
312
+ ) ) }
271
313
</ div >
272
314
) ;
273
315
} ;
274
316
275
- export { BlockHeaderSuggestionControl , SuggestionControl } ;
317
+ export { BlockHeaderSuggestionControl , SuggestionControl , SuggestionControlNoData , SuggestionControlNoResults } ;
0 commit comments