1
- import type { SystemStyleObject } from '@invoke-ai/ui-library' ;
1
+ import type { InputProps , SystemStyleObject } from '@invoke-ai/ui-library' ;
2
2
import { Box , Divider , Flex , Input , Text } from '@invoke-ai/ui-library' ;
3
3
import { IAINoContentFallback } from 'common/components/IAIImageFallback' ;
4
4
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent' ;
5
5
import { useStateImperative } from 'common/hooks/useStateImperative' ;
6
+ import { fixedForwardRef } from 'common/util/fixedForwardRef' ;
6
7
import { typedMemo } from 'common/util/typedMemo' ;
7
- import { NavigateToModelManagerButton } from 'features/parameters/components/MainModel/NavigateToModelManagerButton' ;
8
8
import type { ChangeEvent } from 'react' ;
9
9
import {
10
10
createContext ,
@@ -37,35 +37,36 @@ export type ImperativeModelPickerHandle = {
37
37
setSearchTerm : ( searchTerm : string ) => void ;
38
38
} ;
39
39
40
- const DefaultOptionComponent = typedMemo ( ( { id } : { id : string } ) => {
41
- return < Text fontWeight = "bold" > { id } </ Text > ;
40
+ const DefaultOptionComponent = typedMemo ( < T extends object > ( { option } : { option : T } ) => {
41
+ const { getOptionId } = usePickerContext ( ) ;
42
+ return < Text fontWeight = "bold" > { getOptionId ( option ) } </ Text > ;
42
43
} ) ;
43
44
DefaultOptionComponent . displayName = 'DefaultOptionComponent' ;
44
45
45
- const DefaultGroupHeaderComponent = typedMemo ( ( { id } : { id : string } ) => {
46
- return < Text fontWeight = "bold" > { id } </ Text > ;
46
+ const DefaultGroupHeaderComponent = typedMemo ( < T extends object > ( { group } : { group : Group < T > } ) => {
47
+ return < Text fontWeight = "bold" > { group . id } </ Text > ;
47
48
} ) ;
48
49
DefaultGroupHeaderComponent . displayName = 'DefaultGroupHeaderComponent' ;
49
50
50
- const DefaultNoOptionsFallback = typedMemo ( ( ) => {
51
+ const DefaultNoOptionsFallbackComponent = typedMemo ( ( ) => {
51
52
const { t } = useTranslation ( ) ;
52
53
return (
53
54
< Flex w = "full" h = "full" alignItems = "center" justifyContent = "center" >
54
55
< Text variant = "subtext" > { t ( 'common.noOptions' ) } </ Text >
55
56
</ Flex >
56
57
) ;
57
58
} ) ;
58
- DefaultNoOptionsFallback . displayName = 'DefaultNoOptionsFallback ' ;
59
+ DefaultNoOptionsFallbackComponent . displayName = 'DefaultNoOptionsFallbackComponent ' ;
59
60
60
- const DefaultNoMatchesFallback = typedMemo ( ( ) => {
61
+ const DefaultNoMatchesFallbackComponent = typedMemo ( ( ) => {
61
62
const { t } = useTranslation ( ) ;
62
63
return (
63
64
< Flex w = "full" h = "full" alignItems = "center" justifyContent = "center" >
64
65
< Text variant = "subtext" > { t ( 'common.noMatches' ) } </ Text >
65
66
</ Flex >
66
67
) ;
67
68
} ) ;
68
- DefaultNoMatchesFallback . displayName = 'DefaultNoMatchesFallback ' ;
69
+ DefaultNoMatchesFallbackComponent . displayName = 'DefaultNoMatchesFallbackComponent ' ;
69
70
70
71
export type PickerProps < T extends object > = {
71
72
options : ( T | Group < T > ) [ ] ;
@@ -75,9 +76,10 @@ export type PickerProps<T extends object> = {
75
76
selectedItem ?: T ;
76
77
onSelect ?: ( option : T ) => void ;
77
78
onClose ?: ( ) => void ;
78
- noOptionsFallback ?: React . ReactNode ;
79
- noMatchesFallback ?: React . ReactNode ;
80
79
handleRef ?: React . Ref < ImperativeModelPickerHandle > ;
80
+ SearchBarComponent ?: ReturnType < typeof fixedForwardRef < HTMLInputElement , InputProps > > ;
81
+ NoOptionsFallbackComponent ?: React . ComponentType ;
82
+ NoMatchesFallbackComponent ?: React . ComponentType ;
81
83
OptionComponent ?: React . ComponentType < { option : T } > ;
82
84
GroupHeaderComponent ?: React . ComponentType < { group : Group < T > } > ;
83
85
} ;
@@ -88,10 +90,11 @@ type PickerContextState<T extends object> = {
88
90
getIsDisabled ?: ( option : T ) => boolean ;
89
91
setActiveOptionId : ( id : string ) => void ;
90
92
onSelectById : ( id : string ) => void ;
91
- noOptionsFallback ?: React . ReactNode ;
92
- noMatchesFallback ?: React . ReactNode ;
93
- OptionComponent ?: React . ComponentType < { option : T } > ;
94
- GroupHeaderComponent ?: React . ComponentType < { group : Group < T > } > ;
93
+ SearchBarComponent : ReturnType < typeof fixedForwardRef < HTMLInputElement , InputProps > > ;
94
+ NoOptionsFallbackComponent : React . ComponentType ;
95
+ NoMatchesFallbackComponent : React . ComponentType ;
96
+ OptionComponent : React . ComponentType < { option : T } > ;
97
+ GroupHeaderComponent : React . ComponentType < { group : Group < T > } > ;
95
98
} ;
96
99
97
100
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
@@ -180,13 +183,14 @@ export const Picker = typedMemo(<T extends object>(props: PickerProps<T>) => {
180
183
handleRef,
181
184
isMatch,
182
185
getIsDisabled,
183
- noMatchesFallback = < DefaultNoMatchesFallback /> ,
184
- noOptionsFallback = < DefaultNoOptionsFallback /> ,
185
186
onClose,
186
187
onSelect,
187
188
selectedItem,
188
- OptionComponent,
189
- GroupHeaderComponent,
189
+ SearchBarComponent = DefaultPickerSearchBarComponent ,
190
+ NoMatchesFallbackComponent = DefaultNoMatchesFallbackComponent ,
191
+ NoOptionsFallbackComponent = DefaultNoOptionsFallbackComponent ,
192
+ OptionComponent = DefaultOptionComponent ,
193
+ GroupHeaderComponent = DefaultGroupHeaderComponent ,
190
194
} = props ;
191
195
const [ activeOptionId , setActiveOptionId , getActiveOptionId ] = useStateImperative ( ( ) =>
192
196
getFirstOptionId ( options , getOptionId )
@@ -346,22 +350,24 @@ export const Picker = typedMemo(<T extends object>(props: PickerProps<T>) => {
346
350
isMatch,
347
351
getIsDisabled,
348
352
onSelectById,
349
- noOptionsFallback,
350
- noMatchesFallback,
353
+ setActiveOptionId,
354
+ SearchBarComponent,
355
+ NoOptionsFallbackComponent,
356
+ NoMatchesFallbackComponent,
351
357
OptionComponent,
352
358
GroupHeaderComponent,
353
- setActiveOptionId,
354
359
} ) satisfies PickerContextState < T > ,
355
360
[
356
- GroupHeaderComponent ,
357
- OptionComponent ,
358
- getIsDisabled ,
359
361
getOptionId ,
360
362
isMatch ,
361
- noMatchesFallback ,
362
- noOptionsFallback ,
363
+ getIsDisabled ,
363
364
onSelectById ,
364
365
setActiveOptionId ,
366
+ SearchBarComponent ,
367
+ NoOptionsFallbackComponent ,
368
+ NoMatchesFallbackComponent ,
369
+ OptionComponent ,
370
+ GroupHeaderComponent ,
365
371
]
366
372
) ;
367
373
@@ -378,14 +384,11 @@ export const Picker = typedMemo(<T extends object>(props: PickerProps<T>) => {
378
384
gap = { 2 }
379
385
onKeyDown = { onKeyDown }
380
386
>
381
- < Flex gap = { 2 } alignItems = "center" >
382
- < Input ref = { inputRef } value = { searchTerm } onChange = { onChangeSearchTerm } placeholder = "Filter" />
383
- < NavigateToModelManagerButton />
384
- </ Flex >
387
+ < SearchBarComponent ref = { inputRef } value = { searchTerm } onChange = { onChangeSearchTerm } />
385
388
< Divider />
386
389
< Flex tabIndex = { - 1 } w = "full" flexGrow = { 1 } >
387
- { flattenedOptions . length === 0 && noOptionsFallback }
388
- { flattenedOptions . length > 0 && flattenedFilteredOptions . length === 0 && noMatchesFallback }
390
+ { flattenedOptions . length === 0 && < NoOptionsFallbackComponent /> }
391
+ { flattenedOptions . length > 0 && flattenedFilteredOptions . length === 0 && < NoMatchesFallbackComponent /> }
389
392
{ flattenedOptions . length > 0 && flattenedFilteredOptions . length > 0 && (
390
393
< ScrollableContent >
391
394
< PickerList
@@ -402,6 +405,13 @@ export const Picker = typedMemo(<T extends object>(props: PickerProps<T>) => {
402
405
} ) ;
403
406
Picker . displayName = 'Picker' ;
404
407
408
+ const DefaultPickerSearchBarComponent = typedMemo (
409
+ fixedForwardRef < HTMLInputElement , InputProps > ( ( props , ref ) => {
410
+ return < Input placeholder = "Search" ref = { ref } { ...props } /> ;
411
+ } )
412
+ ) ;
413
+ DefaultPickerSearchBarComponent . displayName = 'DefaultPickerSearchBarComponent' ;
414
+
405
415
const PickerList = typedMemo (
406
416
< T extends object > ( {
407
417
items,
@@ -473,7 +483,7 @@ const PickerOptionGroup = typedMemo(
473
483
474
484
return (
475
485
< Flex flexDir = "column" gap = { 2 } w = "full" >
476
- { GroupHeaderComponent ? < GroupHeaderComponent group = { group } /> : < DefaultGroupHeaderComponent id = { group . id } /> }
486
+ < GroupHeaderComponent group = { group } />
477
487
< Flex flexDir = "column" gap = { 1 } w = "full" >
478
488
{ group . options . map ( ( item ) => {
479
489
const id = getOptionId ( item ) ;
@@ -535,7 +545,7 @@ const PickerOption = typedMemo(
535
545
onPointerMove = { isDisabled ? undefined : onPointerMove }
536
546
onClick = { isDisabled ? undefined : onClick }
537
547
>
538
- { OptionComponent ? < OptionComponent option = { option } /> : < DefaultOptionComponent id = { id } /> }
548
+ < OptionComponent option = { option } />
539
549
</ Box >
540
550
) ;
541
551
}
0 commit comments